Files
Bubberstation/code/game/objects/items/food/egg.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

350 lines
12 KiB
Plaintext

////////////////////////////////////////////EGGS////////////////////////////////////////////
/obj/item/food/chocolateegg
name = "chocolate egg"
desc = "Such, sweet, fattening food."
icon = 'icons/obj/food/egg.dmi'
icon_state = "chocolateegg"
food_reagents = list(/datum/reagent/consumable/nutriment = 5, /datum/reagent/consumable/sugar = 2, /datum/reagent/consumable/coco = 2, /datum/reagent/consumable/nutriment/vitamin = 1)
tastes = list("chocolate" = 4, "sweetness" = 1)
foodtypes = JUNKFOOD | SUGAR
food_flags = FOOD_FINGER_FOOD
w_class = WEIGHT_CLASS_TINY
crafting_complexity = FOOD_COMPLEXITY_2
/// Counter for number of chicks hatched by throwing eggs, minecraft style. Chicks will not emerge from thrown eggs if this value exceeds the MAX_CHICKENS define.
GLOBAL_VAR_INIT(chicks_from_eggs, 0)
/obj/item/food/egg
name = "egg"
desc = "An egg!"
icon = 'icons/obj/food/egg.dmi'
icon_state = "egg"
inhand_icon_state = "egg"
food_reagents = list(/datum/reagent/consumable/eggyolk = 2, /datum/reagent/consumable/eggwhite = 4)
foodtypes = MEAT | RAW
w_class = WEIGHT_CLASS_TINY
ant_attracting = FALSE
decomp_type = /obj/item/food/egg/rotten
decomp_req_handle = TRUE //so laid eggs can actually become chickens
/// How likely is it that a chicken will come out of here if we throw it?
var/chick_throw_prob = 13
/obj/item/food/egg/make_bakeable()
AddComponent(/datum/component/bakeable, /obj/item/food/boiledegg, rand(15 SECONDS, 20 SECONDS), TRUE, TRUE)
/obj/item/food/egg/make_microwaveable()
AddElement(/datum/element/microwavable, /obj/item/food/boiledegg)
/obj/item/food/egg/organic
name = "organic egg"
desc = "A 100% natural egg from the best hens."
starting_reagent_purity = 1
/obj/item/food/egg/rotten
food_reagents = list(/datum/reagent/consumable/eggrot = 10, /datum/reagent/consumable/mold = 10)
foodtypes = GROSS
preserved_food = TRUE
/obj/item/food/egg/rotten/make_bakeable()
AddComponent(/datum/component/bakeable, /obj/item/food/boiledegg/rotten, rand(15 SECONDS, 20 SECONDS), TRUE, TRUE)
/obj/item/food/egg/rotten/make_microwaveable()
AddElement(/datum/element/microwavable, /obj/item/food/boiledegg/rotten)
/obj/item/food/egg/gland
desc = "An egg! It looks weird..."
/obj/item/food/egg/gland/Initialize(mapload)
. = ..()
reagents.add_reagent(get_random_reagent_id(), 15)
var/color = mix_color_from_reagents(reagents.reagent_list)
add_atom_colour(color, FIXED_COLOUR_PRIORITY)
/obj/item/food/egg/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if (..()) // was it caught by a mob?
return
var/turf/hit_turf = get_turf(hit_atom)
new /obj/effect/decal/cleanable/food/egg_smudge(hit_turf)
if (prob(chick_throw_prob))
spawn_impact_chick(hit_turf)
reagents.expose(hit_atom, TOUCH)
qdel(src)
/// Spawn a baby chicken from throwing an egg
/obj/item/food/egg/proc/spawn_impact_chick(turf/spawn_turf)
var/chickens_remaining = MAX_CHICKENS - GLOB.chicks_from_eggs
if (chickens_remaining < 1)
return
var/spawned_chickens = prob(97) ? 1 : min(4, chickens_remaining) // We don't want to go over the limit
if (spawned_chickens > 1) // Chicken jackpot!
visible_message(span_notice("[spawned_chickens] chicks come out of the egg! Jackpot!"))
else
visible_message(span_notice("A chick comes out of the cracked egg!"))
for(var/i in 1 to spawned_chickens)
new /mob/living/basic/chick(spawn_turf)
GLOB.chicks_from_eggs++
/obj/item/food/egg/attackby(obj/item/item, mob/user, list/modifiers, list/attack_modifiers)
if(istype(item, /obj/item/toy/crayon))
var/obj/item/toy/crayon/crayon = item
var/clr = crayon.crayon_color
if(!(clr in list("blue", "green", "mime", "orange", "purple", "rainbow", "red", "yellow")))
to_chat(usr, span_notice("[src] refuses to take on this colour!"))
return
to_chat(usr, span_notice("You colour [src] with [item]."))
icon_state = "egg-[clr]"
else if(istype(item, /obj/item/stamp/clown))
var/clowntype = pick("grock", "grimaldi", "rainbow", "chaos", "joker", "sexy", "standard", "bobble",
"krusty", "bozo", "pennywise", "ronald", "jacobs", "kelly", "popov", "cluwne")
icon_state = "egg-clown-[clowntype]"
desc = "An egg that has been decorated with the grotesque, robustable likeness of a clown's face. "
to_chat(usr, span_notice("You stamp [src] with [item], creating an artistic and not remotely horrifying likeness of clown makeup."))
else if(is_reagent_container(item))
var/obj/item/reagent_containers/dunk_test_container = item
if (!dunk_test_container.is_drainable() || !dunk_test_container.reagents.has_reagent(/datum/reagent/water))
return
to_chat(user, span_notice("You check if [src] is rotten."))
if(istype(src, /obj/item/food/egg/rotten))
to_chat(user, span_warning("[src] floats in the [dunk_test_container]!"))
else
to_chat(user, span_notice("[src] sinks into the [dunk_test_container]!"))
else
..()
/obj/item/food/egg/interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers)
if(!istype(interacting_with, /obj/machinery/griddle))
return NONE
var/obj/machinery/griddle/hit_griddle = interacting_with
if(length(hit_griddle.griddled_objects) >= hit_griddle.max_items)
interacting_with.balloon_alert(user, "no room!")
return ITEM_INTERACT_BLOCKING
var/atom/broken_egg = new /obj/item/food/rawegg(interacting_with.loc)
if(LAZYACCESS(modifiers, ICON_X))
broken_egg.pixel_x = clamp(text2num(LAZYACCESS(modifiers, ICON_X)) - 16, -(ICON_SIZE_X/2), ICON_SIZE_X/2)
if(LAZYACCESS(modifiers, ICON_Y))
broken_egg.pixel_y = clamp(text2num(LAZYACCESS(modifiers, ICON_Y)) - 16, -(ICON_SIZE_Y/2), ICON_SIZE_Y/2)
playsound(user, 'sound/items/sheath.ogg', 40, TRUE)
reagents.copy_to(broken_egg, reagents.total_volume)
hit_griddle.AddToGrill(broken_egg, user)
interacting_with.balloon_alert(user, "cracks [src] open")
qdel(src)
return ITEM_INTERACT_BLOCKING
/obj/item/food/egg/blue
icon_state = "egg-blue"
inhand_icon_state = "egg-blue"
/obj/item/food/egg/green
icon_state = "egg-green"
inhand_icon_state = "egg-green"
/obj/item/food/egg/mime
icon_state = "egg-mime"
inhand_icon_state = "egg-mime"
/obj/item/food/egg/orange
icon_state = "egg-orange"
inhand_icon_state = "egg-orange"
/obj/item/food/egg/purple
icon_state = "egg-purple"
inhand_icon_state = "egg-purple"
/obj/item/food/egg/rainbow
icon_state = "egg-rainbow"
inhand_icon_state = "egg-rainbow"
/obj/item/food/egg/red
icon_state = "egg-red"
inhand_icon_state = "egg-red"
/obj/item/food/egg/yellow
icon_state = "egg-yellow"
inhand_icon_state = "egg-yellow"
/obj/item/food/egg/penguin_egg
icon = 'icons/mob/simple/penguins.dmi'
icon_state = "penguin_egg"
/obj/item/food/egg/fertile
name = "fertile-looking egg"
desc = "An egg! It looks fertilized.\nQuite how you can tell this just by looking at it is a mystery."
chick_throw_prob = 100
/obj/item/food/egg/fertile/Initialize(mapload, loc)
. = ..()
AddComponent(/datum/component/fertile_egg,\
embryo_type = /mob/living/basic/chick,\
minimum_growth_rate = 1,\
maximum_growth_rate = 2,\
total_growth_required = 200,\
current_growth = 0,\
location_allowlist = typecacheof(list(/turf)),\
spoilable = FALSE,\
)
/obj/item/food/friedegg
name = "fried egg"
desc = "A fried egg. Would go well with a touch of salt and pepper."
icon = 'icons/obj/food/egg.dmi'
icon_state = "friedegg"
food_reagents = list(
/datum/reagent/consumable/nutriment/protein = 3,
/datum/reagent/consumable/eggyolk = 1,
/datum/reagent/consumable/nutriment/vitamin = 1,
)
bite_consumption = 1
tastes = list("egg" = 4)
foodtypes = MEAT | FRIED | BREAKFAST
w_class = WEIGHT_CLASS_SMALL
crafting_complexity = FOOD_COMPLEXITY_1
/obj/item/food/rawegg
name = "raw egg"
desc = "Supposedly good for you, if you can stomach it. Better fried."
icon = 'icons/obj/food/egg.dmi'
icon_state = "rawegg"
food_reagents = list() // Receives all reagents from its whole egg counterpart
bite_consumption = 1
tastes = list("raw egg" = 6, "sliminess" = 1)
eatverbs = list("gulp down")
foodtypes = MEAT | RAW
w_class = WEIGHT_CLASS_SMALL
/obj/item/food/rawegg/make_grillable()
AddComponent(/datum/component/grillable, /obj/item/food/friedegg, rand(20 SECONDS, 35 SECONDS), TRUE, FALSE)
/obj/item/food/boiledegg
name = "boiled egg"
desc = "A hard boiled egg."
icon = 'icons/obj/food/egg.dmi'
icon_state = "egg"
inhand_icon_state = "egg"
food_reagents = list(
/datum/reagent/consumable/nutriment/protein = 3,
/datum/reagent/consumable/nutriment/vitamin = 1,
)
tastes = list("egg" = 1)
foodtypes = MEAT | BREAKFAST
food_flags = FOOD_FINGER_FOOD
w_class = WEIGHT_CLASS_SMALL
ant_attracting = FALSE
decomp_type = /obj/item/food/boiledegg/rotten
crafting_complexity = FOOD_COMPLEXITY_1
/obj/item/food/eggsausage
name = "egg with sausage"
desc = "A good egg with a side of sausages."
icon = 'icons/obj/food/egg.dmi'
icon_state = "eggsausage"
food_reagents = list(/datum/reagent/consumable/nutriment/protein = 8, /datum/reagent/consumable/nutriment/vitamin = 2, /datum/reagent/consumable/nutriment = 4)
foodtypes = MEAT | FRIED | BREAKFAST
tastes = list("egg" = 4, "meat" = 4)
venue_value = FOOD_PRICE_NORMAL
crafting_complexity = FOOD_COMPLEXITY_3
custom_materials = list(/datum/material/meat = MEATSLAB_MATERIAL_AMOUNT)
/obj/item/food/boiledegg/rotten
food_reagents = list(/datum/reagent/consumable/eggrot = 10)
tastes = list("rotten egg" = 1)
foodtypes = GROSS
preserved_food = TRUE
/obj/item/food/omelette //FUCK THIS
name = "omelette du fromage"
desc = "That's all you can say!"
icon = 'icons/obj/food/egg.dmi'
icon_state = "omelette"
food_reagents = list(
/datum/reagent/consumable/nutriment/protein = 10,
/datum/reagent/consumable/nutriment/vitamin = 3,
)
bite_consumption = 1
w_class = WEIGHT_CLASS_SMALL
tastes = list("egg" = 1, "cheese" = 1)
foodtypes = MEAT | BREAKFAST | DAIRY
venue_value = FOOD_PRICE_CHEAP
crafting_complexity = FOOD_COMPLEXITY_2
/obj/item/food/omelette/Initialize(mapload)
. = ..()
AddElement(/datum/element/love_food_buff, /datum/status_effect/food/speech/french)
/obj/item/food/omelette/attackby(obj/item/item, mob/user, list/modifiers, list/attack_modifiers)
if(istype(item, /obj/item/kitchen/fork))
var/obj/item/kitchen/fork/fork = item
if(fork.forkload)
to_chat(user, span_warning("You already have omelette on your fork!"))
else
fork.icon_state = "forkloaded"
user.visible_message(span_notice("[user] takes a piece of omelette with [user.p_their()] fork!"), \
span_notice("You take a piece of omelette with your fork."))
var/datum/reagent/reagent = pick(reagents.reagent_list)
reagents.remove_reagent(reagent.type, 1)
fork.forkload = reagent
if(reagents.total_volume <= 0)
qdel(src)
return
..()
/obj/item/food/benedict
name = "eggs benedict"
desc = "There is only one egg on this, how rude."
icon = 'icons/obj/food/egg.dmi'
icon_state = "benedict"
food_reagents = list(
/datum/reagent/consumable/nutriment/vitamin = 6,
/datum/reagent/consumable/nutriment/protein = 6,
/datum/reagent/consumable/nutriment = 3,
)
w_class = WEIGHT_CLASS_SMALL
tastes = list("egg" = 1, "bacon" = 1, "bun" = 1)
foodtypes = MEAT|BREAKFAST|GRAIN|FRIED
venue_value = FOOD_PRICE_NORMAL
crafting_complexity = FOOD_COMPLEXITY_3
custom_materials = list(/datum/material/meat = MEATSLAB_MATERIAL_AMOUNT)
/obj/item/food/eggwrap
name = "egg wrap"
desc = "The precursor to Pigs in a Blanket."
icon = 'icons/obj/food/egg.dmi'
icon_state = "eggwrap"
food_reagents = list(
/datum/reagent/consumable/nutriment = 6,
/datum/reagent/consumable/nutriment/protein = 2,
/datum/reagent/consumable/nutriment/vitamin = 3,
)
tastes = list("egg" = 1)
foodtypes = MEAT|VEGETABLES|FRIED
w_class = WEIGHT_CLASS_TINY
crafting_complexity = FOOD_COMPLEXITY_3
/obj/item/food/chawanmushi
name = "chawanmushi"
desc = "A legendary egg custard that makes friends out of enemies. Probably too hot for a cat to eat."
icon = 'icons/obj/food/egg.dmi'
icon_state = "chawanmushi"
food_reagents = list(
/datum/reagent/consumable/nutriment = 4,
/datum/reagent/consumable/nutriment/protein = 3,
/datum/reagent/consumable/nutriment/vitamin = 1,
)
tastes = list("custard" = 1)
foodtypes = MEAT | VEGETABLES
venue_value = FOOD_PRICE_NORMAL
crafting_complexity = FOOD_COMPLEXITY_3