Files
Bubberstation/code/modules/food_and_drinks/recipes/food_mixtures.dm
Ghom 1f3894e793 Crafting refactor, implementing materials (#89465)
My original plan was to just implement materials into crafting so that
items would inherit the materials of their components, allowing for some
interesting stuff if the material flags of the item allow it. However to
my dismay crafting is a pile of old tech debt, starting from the old
`del_reqs` and `CheckParts` which still contain lines about old janky
bandaids that are no longer in use nor reachable, up to the
`customizable_reagent_holder` component which has some harddel issues
when your custom food is sliced, and items used in food recipes not
being deleted and instead stored inside the result with no purpose as
well as other inconsistencies like stack recipes that transfer materials
having counterparts in the UI that don't do that.

EDIT: Several things have come up while working on this, so I apologise
that it ended up changing over 100+ files. I managed to atomize some of
the changes, but it's a bit tedious.

EDIT: TLDR because I was told this section is too vague and there's too
much going on. This PR:
- Improves the dated crafting code (not the UI).
- replaced `atom/CheckParts` and `crafting_recipe/on_craft_completion`
with `atom/on_craft_completion`.
- Reqs used in food recipes are now deleted by default and not stored
inside the result (they did nothing).
- Renames the customizable_reagent_holder comp and improves it (No
harddels/ref issues).
- Adds a unit test that tries to craft all recipes to see what's wrong
(it skips some of the much more specific reqs for now).
- In the unit test is also the code to make sure materials of the
crafted item and a non-crafted item of the same type are roughly the
same, so far only applied to food.
- Some mild material/food refactoring around the fact that food item
code has been changed to support materials.

Improving the backbone of the crafting system. Also materials and food
code.

🆑
refactor: Refactored crafting backend. Report possible pesky bugs.
balance: the MEAT backpack (from the MEAT cargo pack) may be a smidge
different because of code standardization.
/🆑
2025-06-05 20:05:13 -04:00

317 lines
15 KiB
Plaintext

/datum/crafting_recipe/food
mass_craftable = TRUE
requirements_mats_blacklist = list(
/obj/item/reagent_containers/cup/bowl,
/obj/item/popsicle_stick,
/obj/item/stack/rods,
)
crafting_flags = parent_type::crafting_flags | CRAFT_TRANSFERS_REAGENTS | CRAFT_CLEARS_REAGENTS | CRAFT_ENFORCE_MATERIALS_PARITY
///The food types that are added to the result when the recipe is completed
var/added_foodtypes = NONE
///The food types that are removed to the result when the recipe is completed
var/removed_foodtypes = NONE
/datum/crafting_recipe/food/New()
. = ..()
//rarely, but a few cooking recipes (cake cat & co) don't result food items.
if(!PERFORM_ALL_TESTS(focus_only/check_foodtypes) || non_craftable || !ispath(result, /obj/item/food))
return
// Food made from these recipes should inherit the food types of the food ingredients used in it
// 'added_foodtypes' and 'added_foodtypes' exist to add and remove (un)desiderable types
// If the food types of the result don't match when spawned compared to when crafted (with base ingredients), throw a warning.
var/made_with_food = FALSE
var/actual_foodtypes = added_foodtypes
for(var/req_path in reqs)
if(!ispath(req_path, /obj/item/food))
continue
var/obj/item/food/ingredient = req_path
made_with_food = TRUE
actual_foodtypes |= initial(ingredient.foodtypes)
if(!made_with_food)
return
actual_foodtypes &= ~removed_foodtypes
var/obj/item/food/result_path = result
var/result_foodtypes = initial(result_path.foodtypes)
if(result_foodtypes != actual_foodtypes)
var/text_flags = jointext(bitfield_to_list(result_foodtypes, FOOD_FLAGS),"|")
var/text_craft_flags = jointext(bitfield_to_list(actual_foodtypes, FOOD_FLAGS),"|")
stack_trace("the foodtypes of [result_path] are [text_flags] when spawned but [text_craft_flags] when crafted.")
/datum/crafting_recipe/food/crafting_ui_data()
var/list/data = list()
if(ispath(result, /obj/item/food))
var/obj/item/food/item = result
data["foodtypes"] = bitfield_to_list(initial(item.foodtypes), FOOD_FLAGS)
data["complexity"] = initial(item.crafting_complexity)
return data
//////////////////////////////////////////FOOD MIXTURES////////////////////////////////////
/datum/chemical_reaction/food
optimal_temp = 400
temp_exponent_factor = 1
optimal_ph_min = 2
optimal_ph_max = 10
thermic_constant = 0
H_ion_release = 0
reaction_tags = REACTION_TAG_FOOD | REACTION_TAG_EASY
required_other = TRUE
/// Typepath of food that is created on reaction
var/atom/resulting_food_path
/// Reagent purity of the result, calculated on reaction
var/resulting_reagent_purity
/datum/chemical_reaction/food/pre_reaction_other_checks(datum/reagents/holder)
resulting_reagent_purity = holder.get_average_purity()
return TRUE
/datum/chemical_reaction/food/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume)
if(resulting_food_path)
var/atom/location = holder.my_atom.drop_location()
for(var/i in 1 to created_volume)
var/obj/item/food/result = new resulting_food_path(location)
if(ispath(resulting_food_path, /obj/item/food) && !isnull(resulting_reagent_purity))
result.reagents?.set_all_reagents_purity(resulting_reagent_purity)
/datum/chemical_reaction/food/tofu
required_reagents = list(/datum/reagent/consumable/soymilk = 10)
required_catalysts = list(/datum/reagent/consumable/enzyme = 5)
mob_react = FALSE
reaction_flags = REACTION_INSTANT
resulting_food_path = /obj/item/food/tofu
/datum/chemical_reaction/food/candycorn
required_reagents = list(/datum/reagent/consumable/nutriment/fat/oil = 5)
required_catalysts = list(/datum/reagent/consumable/sugar = 5)
mob_react = FALSE
reaction_flags = REACTION_INSTANT
resulting_food_path = /obj/item/food/candy_corn
/datum/chemical_reaction/food/chocolatepudding
results = list(/datum/reagent/consumable/chocolatepudding = 20)
required_reagents = list(/datum/reagent/consumable/cream = 5, /datum/reagent/consumable/coco = 5, /datum/reagent/consumable/eggyolk = 2)
/datum/chemical_reaction/food/vanillapudding
results = list(/datum/reagent/consumable/vanillapudding = 20)
required_reagents = list(/datum/reagent/consumable/vanilla = 5, /datum/reagent/consumable/cream = 5, /datum/reagent/consumable/eggyolk = 2)
/datum/chemical_reaction/food/chocolate_bar
required_reagents = list(/datum/reagent/consumable/soymilk = 2, /datum/reagent/consumable/coco = 2, /datum/reagent/consumable/sugar = 2)
reaction_flags = REACTION_INSTANT
resulting_food_path = /obj/item/food/chocolatebar
/datum/chemical_reaction/food/chocolate_bar2
required_reagents = list(/datum/reagent/consumable/milk/chocolate_milk = 4, /datum/reagent/consumable/sugar = 2)
mob_react = FALSE
reaction_flags = REACTION_INSTANT
resulting_food_path = /obj/item/food/chocolatebar
/datum/chemical_reaction/food/chocolate_bar3
required_reagents = list(/datum/reagent/consumable/milk = 2, /datum/reagent/consumable/coco = 2, /datum/reagent/consumable/sugar = 2)
reaction_flags = REACTION_INSTANT
resulting_food_path = /obj/item/food/chocolatebar
/datum/chemical_reaction/food/soysauce
results = list(/datum/reagent/consumable/soysauce = 5)
required_reagents = list(/datum/reagent/consumable/soymilk = 4, /datum/reagent/toxin/acid = 1)
/datum/chemical_reaction/food/corn_syrup
results = list(/datum/reagent/consumable/corn_syrup = 5)
required_reagents = list(/datum/reagent/consumable/corn_starch = 1, /datum/reagent/toxin/acid = 1)
required_temp = 374
/datum/chemical_reaction/food/rice_flour
results = list(/datum/reagent/consumable/rice_flour = 10)
required_reagents = list(/datum/reagent/consumable/flour = 5,/datum/reagent/consumable/rice = 5)
/datum/chemical_reaction/food/caramel
results = list(/datum/reagent/consumable/caramel = 1)
required_reagents = list(/datum/reagent/consumable/sugar = 1)
required_temp = 413.15
optimal_temp = 600
mob_react = FALSE
/datum/chemical_reaction/food/caramel_burned
results = list(/datum/reagent/carbon = 1)
required_reagents = list(/datum/reagent/consumable/caramel = 1)
required_temp = 483.15
optimal_temp = 1000
rate_up_lim = 10
mob_react = FALSE
/datum/chemical_reaction/food/cheesewheel
required_reagents = list(/datum/reagent/consumable/milk = 40)
required_catalysts = list(/datum/reagent/consumable/enzyme = 5)
reaction_flags = REACTION_INSTANT
resulting_food_path = /obj/item/food/cheese/wheel
/datum/chemical_reaction/food/synthmeat
required_reagents = list(/datum/reagent/blood = 5, /datum/reagent/medicine/cryoxadone = 1)
mob_react = FALSE
reaction_flags = REACTION_INSTANT
resulting_food_path = /obj/item/food/meat/slab/synthmeat
/datum/chemical_reaction/food/hot_ramen
results = list(/datum/reagent/consumable/hot_ramen = 3)
required_reagents = list(/datum/reagent/water = 1, /datum/reagent/consumable/dry_ramen = 3)
/datum/chemical_reaction/food/hell_ramen
results = list(/datum/reagent/consumable/hell_ramen = 6)
required_reagents = list(/datum/reagent/consumable/capsaicin = 1, /datum/reagent/consumable/hot_ramen = 6)
/datum/chemical_reaction/food/imitationcarpmeat
required_reagents = list(/datum/reagent/toxin/carpotoxin = 5)
required_container = /obj/item/food/tofu
mix_message = "The mixture becomes similar to carp meat."
reaction_flags = REACTION_INSTANT
/datum/chemical_reaction/food/imitationcarpmeat/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume)
var/location = get_turf(holder.my_atom)
new /obj/item/food/fishmeat/carp/imitation(location)
if(holder?.my_atom)
qdel(holder.my_atom)
/datum/chemical_reaction/food/dough
required_reagents = list(/datum/reagent/water = 10, /datum/reagent/consumable/flour = 15)
mix_message = "The ingredients form a dough."
reaction_flags = REACTION_INSTANT
resulting_food_path = /obj/item/food/dough
/datum/chemical_reaction/food/rice_dough
required_reagents = list(/datum/reagent/consumable/rice_flour = 20,/datum/reagent/water = 10)
mix_message = "The ingredients form a rice dough."
reaction_flags = REACTION_INSTANT
resulting_food_path = /obj/item/food/rice_dough
/datum/chemical_reaction/food/cakebatter
required_reagents = list(/datum/reagent/consumable/eggyolk = 6, /datum/reagent/consumable/eggwhite = 12, /datum/reagent/consumable/flour = 15, /datum/reagent/consumable/sugar = 5)
mix_message = "The ingredients form a cake batter."
reaction_flags = REACTION_INSTANT
resulting_food_path = /obj/item/food/cakebatter
/datum/chemical_reaction/food/cakebatter/vegan
required_reagents = list(/datum/reagent/consumable/soymilk = 15, /datum/reagent/consumable/flour = 15, /datum/reagent/consumable/sugar = 5)
/datum/chemical_reaction/food/pancakebatter
results = list(/datum/reagent/consumable/pancakebatter = 15)
required_reagents = list(/datum/reagent/consumable/eggyolk = 6, /datum/reagent/consumable/eggwhite = 12, /datum/reagent/consumable/milk = 10, /datum/reagent/consumable/flour = 5)
/datum/chemical_reaction/food/uncooked_rice
required_reagents = list(/datum/reagent/consumable/rice = 10, /datum/reagent/water = 10)
mix_message = "The rice absorbs the water."
reaction_flags = REACTION_INSTANT
/datum/chemical_reaction/food/uncooked_rice/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume)
var/location = get_turf(holder.my_atom)
for(var/i in 1 to created_volume)
new /obj/item/food/uncooked_rice(location)
/datum/chemical_reaction/food/nutriconversion
results = list(/datum/reagent/consumable/nutriment/peptides = 0.5)
required_reagents = list(/datum/reagent/consumable/nutriment/ = 0.5)
required_catalysts = list(/datum/reagent/medicine/metafactor = 0.5)
/datum/chemical_reaction/food/protein_peptide
results = list(/datum/reagent/consumable/nutriment/peptides = 0.5)
required_reagents = list(/datum/reagent/consumable/nutriment/protein = 0.5)
required_catalysts = list(/datum/reagent/medicine/metafactor = 0.5)
/datum/chemical_reaction/food/failed_nutriconversion
results = list(/datum/reagent/peptides_failed = 0.5)
required_reagents = list(/datum/reagent/consumable/nutriment/ = 0.5)
required_catalysts = list(/datum/reagent/impurity/probital_failed = 0.5)
thermic_constant = 100 // a tell
/datum/chemical_reaction/food/failed_protein_peptide
results = list(/datum/reagent/peptides_failed = 0.5)
required_reagents = list(/datum/reagent/consumable/nutriment/protein = 0.5)
required_catalysts = list(/datum/reagent/impurity/probital_failed = 0.5)
thermic_constant = 100 // a tell
/datum/chemical_reaction/food/bbqsauce
results = list(/datum/reagent/consumable/bbqsauce = 5)
required_reagents = list(/datum/reagent/ash = 1, /datum/reagent/consumable/tomatojuice = 1, /datum/reagent/medicine/salglu_solution = 3, /datum/reagent/consumable/blackpepper = 1)
/datum/chemical_reaction/food/gravy
results = list(/datum/reagent/consumable/gravy = 3)
required_reagents = list(/datum/reagent/consumable/milk = 1, /datum/reagent/consumable/nutriment = 1, /datum/reagent/consumable/flour = 1)
/datum/chemical_reaction/food/mothic_pizza_dough
required_reagents = list(/datum/reagent/consumable/milk = 5, /datum/reagent/consumable/nutriment/fat/oil/olive = 2, /datum/reagent/medicine/salglu_solution = 5, /datum/reagent/consumable/cornmeal = 10, /datum/reagent/consumable/flour = 5)
mix_message = "The ingredients form a pizza dough."
reaction_flags = REACTION_INSTANT
resulting_food_path = /obj/item/food/mothic_pizza_dough
/datum/chemical_reaction/food/curd_cheese
required_reagents = list(/datum/reagent/consumable/milk = 15, /datum/reagent/consumable/vinegar = 5, /datum/reagent/consumable/cream = 5)
mix_message = "The milk curdles into cheese."
required_temp = 353
reaction_flags = REACTION_INSTANT
resulting_food_path = /obj/item/food/cheese/curd_cheese
/datum/chemical_reaction/food/mozzarella
required_reagents = list(/datum/reagent/consumable/milk = 10, /datum/reagent/consumable/cream = 10)
required_catalysts = list(/datum/reagent/consumable/enzyme = 1)
mix_message = "Fine ribbons of curd form in the milk."
required_temp = 353
reaction_flags = REACTION_INSTANT
resulting_food_path = /obj/item/food/cheese/mozzarella
/datum/chemical_reaction/food/cornmeal_batter
results = list(/datum/reagent/consumable/cornmeal_batter = 35)
required_reagents = list(/datum/reagent/consumable/cornmeal = 20, /datum/reagent/consumable/yoghurt = 10, /datum/reagent/consumable/eggyolk = 5)
mix_message = "A silky batter forms."
/datum/chemical_reaction/food/cornbread
required_reagents = list(/datum/reagent/consumable/cornmeal_batter = 25)
mix_message = "The batter bakes into cornbread- somehow!"
required_temp = 473
reaction_flags = REACTION_INSTANT
resulting_food_path = /obj/item/food/bread/corn
/datum/chemical_reaction/food/yoghurt
required_reagents = list(/datum/reagent/consumable/cream = 10, /datum/reagent/consumable/virus_food = 2)
results = list(/datum/reagent/consumable/yoghurt = 10)
mix_message = "The mixture thickens into yoghurt."
reaction_flags = REACTION_INSTANT
/datum/chemical_reaction/food/olive_oil_upconvert
required_catalysts = list(/datum/reagent/consumable/nutriment/fat/oil/olive = 1)
required_reagents = list( /datum/reagent/consumable/nutriment/fat/oil = 2)
results = list(/datum/reagent/consumable/nutriment/fat/oil/olive = 2)
mix_message = "The cooking oil dilutes the quality oil- how delightfully devilish..."
reaction_flags = REACTION_INSTANT
/datum/chemical_reaction/food/olive_oil
results = list(/datum/reagent/consumable/nutriment/fat/oil/olive = 2)
required_reagents = list(/datum/reagent/consumable/olivepaste = 4, /datum/reagent/water = 1)
reaction_flags = REACTION_INSTANT
/datum/chemical_reaction/food/wine_vinegar
results = list(/datum/reagent/consumable/vinegar = 5)
required_reagents = list(/datum/reagent/consumable/ethanol/wine = 1, /datum/reagent/water = 1, /datum/reagent/consumable/sugar = 1)
reaction_flags = REACTION_INSTANT
/datum/chemical_reaction/food/grounding_neutralise
results = list(/datum/reagent/consumable/salt = 2)
required_reagents = list(/datum/reagent/consumable/liquidelectricity/enriched = 2, /datum/reagent/consumable/grounding_solution = 1)
mix_message = "The mixture lets off a sharp snap as the electricity discharges."
mix_sound = 'sound/items/weapons/taser.ogg'
reaction_flags = REACTION_INSTANT
/datum/chemical_reaction/food/martian_batter
results = list(/datum/reagent/consumable/martian_batter = 10)
required_reagents = list(/datum/reagent/consumable/flour = 5, /datum/reagent/consumable/nutriment/soup/dashi = 5)
mix_message = "A smooth batter forms."
reaction_flags = REACTION_INSTANT
/datum/chemical_reaction/food/grape_vinegar
results = list(/datum/reagent/consumable/vinegar = 5)
required_reagents = list(/datum/reagent/consumable/grapejuice = 5)
required_catalysts = list(/datum/reagent/consumable/enzyme = 5)
mix_message = "The smell of the mixture reminds you of how you lost access to the country club..."