Files
CHOMPStation2/code/modules/food/kitchen/cooking_machines/_appliance.dm
CHOMPStation2StaffMirrorBot 1750269f11 [MIRROR] Gripper Qol (#11976)
Co-authored-by: Will <7099514+Willburd@users.noreply.github.com>
2025-11-13 01:30:24 +01:00

897 lines
29 KiB
Plaintext

// This folder contains code that was originally ported from Apollo Station and then refactored/optimized/changed.
// Tracks precooked food to stop deep fried baked grilled grilled grilled diona nymph cereal.
/obj/item/reagent_containers/food/snacks
var/tmp/list/cooked = list()
// Root type for cooking machines. See following files for specific implementations.
/obj/machinery/appliance
name = "cooker"
desc = "You shouldn't be seeing this!"
icon = 'icons/obj/cooking_machines.dmi'
var/appliancetype = 0
density = TRUE
anchored = TRUE
use_power = USE_POWER_IDLE
idle_power_usage = 5 // Power used when turned on, but not processing anything
active_power_usage = 1000 // Power used when turned on and actively cooking something
var/cooking_power = 0 // Effectiveness/speed at cooking
var/cooking_coeff = 0 // Optimal power * proximity to optimal temp; used to calc. cooking power.
var/heating_power = 1000 // Effectiveness at heating up; not used for mixers, should be equal to active_power_usage
var/max_contents = 1 // Maximum number of things this appliance can simultaneously cook
var/on_icon // Icon state used when cooking.
var/off_icon // Icon state used when not cooking.
var/cooking = FALSE // Whether or not the machine is currently operating.
var/cook_type // A string value used to track what kind of food this machine makes.
var/can_cook_mobs // Whether or not this machine accepts grabbed mobs.
var/mobdamagetype = BRUTE // Burn damage for cooking appliances, brute for cereal/candy
var/food_color // Colour of resulting food item.
var/cooked_sound = 'sound/machines/ding.ogg' // Sound played when cooking completes.
var/can_burn_food = FALSE // Can the object burn food that is left inside?
var/burn_chance = 10 // How likely is the food to burn?
var/list/cooking_objs = list() // List of things being cooked
// If the machine has multiple output modes, define them here.
var/selected_option
var/list/output_options = list()
var/list/datum/recipe/appliance_available_recipes = list()
var/container_type = null
var/combine_first = FALSE // If TRUE, this appliance will do combination cooking before checking recipes
var/food_safety = FALSE // If true, the appliance automatically ejects food instead of burning it
var/static/radial_eject = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_eject")
var/static/radial_power = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_power")
var/static/radial_safety = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_safety")
var/static/radial_output = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_change_output")
/obj/machinery/appliance/Initialize(mapload)
. = ..()
default_apply_parts()
if(output_options.len)
verbs += /obj/machinery/appliance/proc/choose_output
if(!LAZYLEN(appliance_available_recipes))
for(var/datum/recipe/test as anything in subtypesof(/datum/recipe))
if((appliancetype & initial(test.appliance)))
appliance_available_recipes += new test
/obj/machinery/appliance/Destroy()
for(var/datum/cooking_item/CI as anything in cooking_objs)
qdel(CI.container)//Food is fragile, it probably doesnt survive the destruction of the machine
cooking_objs -= CI
qdel(CI)
return ..()
/obj/machinery/appliance/examine(var/mob/user)
. = ..()
if(Adjacent(user))
. += list_contents(user)
/obj/machinery/appliance/proc/list_contents(var/mob/user)
if (cooking_objs.len)
var/string = "Contains..."
for(var/datum/cooking_item/CI as anything in cooking_objs)
string += "-\a [CI.container.label(null, CI.combine_target)], [report_progress(CI)]</br>"
return string
else
to_chat(user, span_notice("It is empty."))
/obj/machinery/appliance/proc/report_progress_tgui(datum/cooking_item/CI)
if(!CI || !CI.max_cookwork)
return list("average", "Not Cooking.")
if(!CI.cookwork)
return list("blue", "Cold.")
var/progress = CI.cookwork / CI.max_cookwork
if (progress < 0.25)
return list("blue", "It's barely started cooking.")
if (progress < 0.75)
return list("average", "It's cooking away nicely.")
if (progress < 1)
return list("good", "It's almost ready!")
var/half_overcook = (CI.overcook_mult - 1)*0.5
if (progress < 1+half_overcook)
return list("good", "It's done!")
if (progress < CI.overcook_mult)
return list("bad", "It looks overcooked, get it out!")
else
return list("bad", "It is burning!")
/obj/machinery/appliance/proc/report_progress(var/datum/cooking_item/CI)
if (!CI || !CI.max_cookwork)
return null
if (!CI.cookwork)
return "It is cold."
var/progress = CI.cookwork / CI.max_cookwork
if (progress < 0.25)
return "It's barely started cooking."
if (progress < 0.75)
return span_notice("It's cooking away nicely.")
if (progress < 1)
return span_boldnotice("It's almost ready!")
var/half_overcook = (CI.overcook_mult - 1)*0.5
if (progress < 1+half_overcook)
return span_soghun(span_bold("It is done!"))
if (progress < CI.overcook_mult)
return span_warning("It looks overcooked, get it out!")
else
return span_danger("It is burning!")
/obj/machinery/appliance/update_icon()
if (!stat && cooking_objs.len)
icon_state = on_icon
else
icon_state = off_icon
/obj/machinery/appliance/verb/toggle_power()
set name = "Toggle Power"
set category = "Object"
set src in view()
attempt_toggle_power(usr)
/obj/machinery/appliance/proc/attempt_toggle_power(mob/user)
if (!isliving(user))
return
if (!user.IsAdvancedToolUser())
to_chat(user, span_warning("You lack the dexterity to do that!"))
return
if (user.stat || user.restrained() || user.incapacitated())
return
if (!Adjacent(user) && !issilicon(user))
to_chat(user, span_warning("You can't reach [src] from here!"))
return
if (stat & POWEROFF)//Its turned off
stat &= ~POWEROFF
use_power = 1
user.visible_message(span_filter_notice("[user] turns [src] on."), span_filter_notice("You turn on [src]."))
else //Its on, turn it off
stat |= POWEROFF
use_power = 0
user.visible_message(span_filter_notice("[user] turns [src] off."), span_filter_notice("You turn off [src]."))
cooking = FALSE // Stop cooking here, too, just in case.
playsound(src, 'sound/machines/click.ogg', 40, 1)
update_icon()
/obj/machinery/appliance/AICtrlClick(mob/user)
attempt_toggle_power(user)
/obj/machinery/appliance/proc/choose_output()
set src in view()
set name = "Choose output"
set category = "Object"
if (!isliving(usr))
return
if (!usr.IsAdvancedToolUser())
to_chat(usr, span_filter_notice("You lack the dexterity to do that!"))
return
if (usr.stat || usr.restrained() || usr.incapacitated())
return
if (!Adjacent(usr) && !issilicon(usr))
to_chat(usr, span_filter_notice("You can't adjust the [src] from this distance, get closer!"))
return
if(output_options.len)
var/choice = tgui_input_list(usr, "What specific food do you wish to make with \the [src]?", "Food Output Choice", output_options+"Default")
if(!choice)
return
if(choice == "Default")
selected_option = null
to_chat(usr, span_notice("You decide not to make anything specific with \the [src]."))
else
selected_option = choice
to_chat(usr, span_notice("You prepare \the [src] to make \a [selected_option] with the next thing you put in. Try putting several ingredients in a container!"))
//Handles all validity checking and error messages for inserting things
/obj/machinery/appliance/proc/can_insert(var/obj/item/I, var/mob/user)
if(istype(I.loc, /mob/living/silicon))
return 0
else if (istype(I.loc, /obj/item/rig_module))
return 0
// We are trying to cook a grabbed mob.
var/obj/item/grab/G = I
if(istype(G))
if(!can_cook_mobs)
to_chat(user, span_warning("That's not going to fit."))
return 0
if(!isliving(G.affecting))
to_chat(user, span_warning("You can't cook that."))
return 0
return 2
if (!has_space(I))
to_chat(user, span_warning("There's no room in [src] for that!"))
return 0
if (container_type && istype(I, container_type))
return 1
// We're trying to cook something else. Check if it's valid.
var/obj/item/reagent_containers/food/snacks/check = I
if(istype(check) && islist(check.cooked) && (cook_type in check.cooked))
to_chat(user, span_warning("\The [check] has already been [cook_type]."))
return 0
else if(istype(check, /obj/item/reagent_containers/glass))
to_chat(user, span_warning("That would probably break [src]."))
return 0
else if(istype(check, /obj/item/disk/nuclear))
to_chat(user, span_warning("You can't cook that."))
return 0
else if(I.has_tool_quality(TOOL_CROWBAR) || I.has_tool_quality(TOOL_SCREWDRIVER) || istype(I, /obj/item/storage/part_replacer)) // You can't cook tools, dummy.
return 0
else if(!istype(check) && !istype(check, /obj/item/holder))
to_chat(user, span_warning("That's not edible."))
return 0
return 1
//This function is overridden by cookers that do stuff with containers
/obj/machinery/appliance/proc/has_space(var/obj/item/I)
if(cooking_objs.len >= max_contents)
return FALSE
return TRUE
/obj/machinery/appliance/attackby(var/obj/item/I, var/mob/user)
if(!cook_type || (stat & (BROKEN)))
to_chat(user, span_warning("\The [src] is not working."))
return FALSE
var/obj/item/ToCook = I
if(istype(I, /obj/item/gripper))
var/obj/item/gripper/GR = I
var/obj/item/wrap = GR.get_current_pocket()
if(wrap)
wrap.loc = get_turf(src)
var/result = can_insert(wrap, user)
if(!result)
wrap.forceMove(GR)
if(!(default_deconstruction_screwdriver(user, I)))
default_part_replacement(user, I)
return
if(QDELETED(wrap))
GR.update_ref(null)
if(wrap.loc != src)
GR.drop_item_nm()
ToCook = wrap
else
attack_hand(user)
return
else
var/result = can_insert(I, user)
if(!result)
if(!(default_deconstruction_screwdriver(user, I)))
default_part_replacement(user, I)
return
if(result == 2)
var/obj/item/grab/G = I
if (G && istype(G) && G.affecting)
cook_mob(G.affecting, user)
return
//From here we can start cooking food
add_content(ToCook, user)
update_icon()
//Override for container mechanics
/obj/machinery/appliance/proc/add_content(var/obj/item/I, var/mob/user)
if(!user.unEquip(I) && !isturf(I.loc))
return
var/datum/cooking_item/CI = has_space(I)
if (istype(I, /obj/item/reagent_containers/cooking_container) && CI == 1)
var/obj/item/reagent_containers/cooking_container/CC = I
CI = new /datum/cooking_item/(CC)
I.forceMove(src)
cooking_objs.Add(CI)
user.visible_message(span_infoplain(span_bold("\The [user]") + " puts \the [I] into \the [src]."))
if (CC.check_contents() == 0)//If we're just putting an empty container in, then dont start any processing.
return TRUE
else
if (CI && istype(CI))
I.forceMove(CI.container)
else //Something went wrong
return FALSE
if (selected_option)
CI.combine_target = selected_option
// We can actually start cooking now.
user.visible_message(span_infoplain(span_bold("\The [user]") + " puts \the [I] into \the [src]."))
get_cooking_work(CI)
cooking = TRUE
return CI
/obj/machinery/appliance/proc/get_cooking_work(var/datum/cooking_item/CI)
for (var/obj/item/J in CI.container)
cookwork_by_item(J, CI)
for(var/datum/reagent/R as anything in CI.container.reagents.reagent_list)
if (istype(R, /datum/reagent/nutriment))
CI.max_cookwork += R.volume *2//Added reagents contribute less than those in food items due to granular form
//Nonfat reagents will soak oil
if (!istype(R, /datum/reagent/nutriment/triglyceride))
CI.max_oil += R.volume * 0.25
else
CI.max_cookwork += R.volume
CI.max_oil += R.volume * 0.10
//Rescaling cooking work to avoid insanely long times for large things
var/buffer = CI.max_cookwork
CI.max_cookwork = 0
var/multiplier = 0.35
var/step = 4
while (buffer > step)
buffer -= step
CI.max_cookwork += step*multiplier
multiplier *= 0.95
CI.max_cookwork += buffer*multiplier
//Just a helper to save code duplication in the above
/obj/machinery/appliance/proc/cookwork_by_item(var/obj/item/I, var/datum/cooking_item/CI)
var/obj/item/reagent_containers/food/snacks/S = I
var/work = 0
if (istype(S))
if (S.reagents)
for(var/datum/reagent/R as anything in S.reagents.reagent_list)
if (istype(R, /datum/reagent/nutriment))
work += R.volume *3//Core nutrients contribute much more than peripheral chemicals
//Nonfat reagents will soak oil
if (!istype(R, /datum/reagent/nutriment/triglyceride))
CI.max_oil += R.volume * 0.35
else
work += R.volume
CI.max_oil += R.volume * 0.15
else if(istype(I, /obj/item/holder))
var/obj/item/holder/H = I
if (H.held_mob)
work += ((H.held_mob.mob_size * H.held_mob.size_multiplier) * (H.held_mob.mob_size * H.held_mob.size_multiplier) * 2)+2
CI.max_cookwork += work
//Called every tick while we're cooking something
/obj/machinery/appliance/proc/do_cooking_tick(var/datum/cooking_item/CI)
if (!istype(CI) || !CI.max_cookwork)
return FALSE
var/was_done = FALSE
if (CI.cookwork >= CI.max_cookwork)
was_done = TRUE
CI.cookwork += cooking_power
if (!was_done && CI.cookwork >= CI.max_cookwork)
//If cookwork has gone from above to below 0, then this item finished cooking
finish_cooking(CI)
else if (!CI.burned && CI.cookwork > min(CI.max_cookwork * CI.overcook_mult, CI.max_cookwork + 30))
if(!food_safety)
burn_food(CI)
else
eject(CI, null)
// Gotta hurt.
for(var/obj/item/holder/H in CI.container.contents)
var/mob/living/M = H.held_mob
if(M)
M.apply_damage(rand(1,3) * (1/M.size_multiplier), mobdamagetype, pick(BP_ALL))
return TRUE
/obj/machinery/appliance/process()
if(cooking_power > 0 && cooking)
var/all_done_cooking = TRUE
for(var/datum/cooking_item/CI in cooking_objs)
do_cooking_tick(CI)
if(CI.max_cookwork > 0)
all_done_cooking = FALSE
if(all_done_cooking)
cooking = FALSE
update_icon()
/obj/machinery/appliance/proc/predict_cooking(datum/cooking_item/CI)
var/datum/recipe/recipe = null
var/atom/C = null
if(CI.container)
C = CI.container
else
C = src
recipe = select_recipe(appliance_available_recipes, C)
var/list/results = list()
if(recipe)
var/obj/O = recipe.result
results += initial(O.name)
else if(CI.combine_target)
results += predict_combination(CI)
else
for(var/obj/item/I in CI.container)
results += predict_modification(I, CI)
return jointext(results, ", ")
/obj/machinery/appliance/proc/predict_combination(datum/cooking_item/CI)
var/obj/cook_path = output_options[CI.combine_target]
var/list/words = list()
var/list/cooktypes = list()
for(var/obj/item/reagent_containers/food/snacks/S in CI.container)
words |= splittext(S.name, " ")
cooktypes |= S.cooked
//Set the name.
words -= list("and", "the", "in", "is", "bar", "raw", "sticks", "boiled", "fried", "deep", "-o-", "warm", "two", "flavored")
//Remove common connecting words and unsuitable ones from the list. Unsuitable words include those describing
//the shape, cooked-ness/temperature or other state of an ingredient which doesn't apply to the finished product
var/name = initial(cook_path.name)
words.Remove(name)
shuffle(words)
var/num = 6 //Maximum number of words
while (num > 0)
num--
if (!words.len)
break
//Add prefixes from the ingredients in a random order until we run out or hit limit
name = "[pop(words)] [name]"
return name
/obj/machinery/appliance/proc/predict_modification(obj/item/input, datum/cooking_item/CI)
return "[cook_type] [input.name]"
/obj/machinery/appliance/proc/finish_cooking(var/datum/cooking_item/CI)
src.visible_message(span_infoplain(span_bold("\The [src]") + " pings!"))
if(cooked_sound)
playsound(get_turf(src), cooked_sound, 50, 1)
//Check recipes first, a valid recipe overrides other options
var/datum/recipe/recipe = null
var/atom/C = null
if (CI.container)
C = CI.container
else
C = src
recipe = select_recipe(appliance_available_recipes,C)
if (recipe)
CI.result_type = 4//Recipe type, a specific recipe will transform the ingredients into a new food
var/list/results = recipe.make_food(C)
var/obj/temp = new /obj(src) //To prevent infinite loops, all results will be moved into a temporary location so they're not considered as inputs for other recipes
for (var/atom/movable/AM in results)
AM.forceMove(temp)
//making multiple copies of a recipe from one container. For example, tons of fries
while (select_recipe(appliance_available_recipes,C) == recipe)
var/list/TR = list()
TR += recipe.make_food(C)
for (var/atom/movable/AM in TR) //Move results to buffer
AM.forceMove(temp)
results += TR
for(var/obj/item/reagent_containers/food/snacks/R as anything in results)
R.forceMove(C) //Move everything from the buffer back to the container
R.cooked |= cook_type
QDEL_NULL(temp) //delete buffer object
. = 1 //None of the rest of this function is relevant for recipe cooking
else if(CI.combine_target)
CI.result_type = 3//Combination type. We're making something out of our ingredients
. = combination_cook(CI)
else
//Otherwise, we're just doing standard modification cooking. change a color + name
for (var/obj/item/i in CI.container)
modify_cook(i, CI)
//Final step. Cook function just cooks batter for now.
for (var/obj/item/reagent_containers/food/snacks/S in CI.container)
S.cook()
//Combination cooking involves combining the names and reagents of ingredients into a predefined output object
//The ingredients represent flavours or fillings. EG: donut pizza, cheese bread
/obj/machinery/appliance/proc/combination_cook(var/datum/cooking_item/CI)
var/cook_path = output_options[CI.combine_target]
var/list/words = list()
var/list/cooktypes = list()
var/datum/reagents/buffer = new /datum/reagents(1000)
var/totalcolour
var/reagents_determine_color
if(!LAZYLEN(CI.container.contents)) // It's possible to make something, such as a cake in the oven, with only reagents. This stops them from being grey and sad.
reagents_determine_color = TRUE
for (var/obj/item/I in CI.container)
var/obj/item/reagent_containers/food/snacks/S
if (istype(I, /obj/item/holder))
S = create_mob_food(I, CI)
else if (istype(I, /obj/item/reagent_containers/food/snacks))
S = I
if (!S)
continue
words |= splittext(S.name," ")
cooktypes |= S.cooked
if (S.reagents && S.reagents.total_volume > 0)
if (S.filling_color)
if (!totalcolour || !buffer.total_volume)
totalcolour = S.filling_color
else
var/t = buffer.total_volume + S.reagents.total_volume
t = buffer.total_volume / y
totalcolour = BlendRGB(totalcolour, S.filling_color, t)
//Blend colours in order to find a good filling color
S.reagents.trans_to_holder(buffer, S.reagents.total_volume)
//Cleanup these empty husk ingredients now
if (I)
qdel(I)
if (S)
qdel(S)
CI.container.reagents.trans_to_holder(buffer, CI.container.reagents.total_volume)
var/obj/item/reagent_containers/food/snacks/result = new cook_path(CI.container)
buffer.trans_to_holder(result.reagents, buffer.total_volume) //trans_to doesn't handle food items well, so
//just call trans_to_holder instead
// Reagent-only foods.
if(reagents_determine_color)
totalcolour = result.reagents.get_color()
for(var/datum/reagent/reag in result.reagents.reagent_list)
words |= text2list(reag.name, " ")
//Filling overlay
var/image/I = image(result.icon, "[result.icon_state]_filling")
I.color = totalcolour
result.add_overlay(I)
result.filling_color = totalcolour
//Set the name.
words -= list("and", "the", "in", "is", "bar", "raw", "sticks", "boiled", "fried", "deep", "-o-", "warm", "two", "flavored")
//Remove common connecting words and unsuitable ones from the list. Unsuitable words include those describing
//the shape, cooked-ness/temperature or other state of an ingredient which doesn't apply to the finished product
words.Remove(result.name)
shuffle(words)
var/num = 6 //Maximum number of words
while (num > 0)
num--
if (!words.len)
break
//Add prefixes from the ingredients in a random order until we run out or hit limit
result.name = "[pop(words)] [result.name]"
//This proc sets the size of the output result
result.update_icon()
return result
//Helper proc for standard modification cooking
/obj/machinery/appliance/proc/modify_cook(var/obj/item/input, var/datum/cooking_item/CI)
var/obj/item/reagent_containers/food/snacks/result
if (istype(input, /obj/item/holder))
result = create_mob_food(input, CI)
else if (istype(input, /obj/item/reagent_containers/food/snacks))
result = input
else
//Nonviable item
return
if (!result)
return
result.cooked |= cook_type
// Set icon and appearance.
change_product_appearance(result, CI)
// Update strings.
change_product_strings(result, CI)
/obj/machinery/appliance/proc/burn_food(var/datum/cooking_item/CI)
// You dun goofed.
CI.burned = 1
CI.container.clear()
new /obj/item/reagent_containers/food/snacks/badrecipe(CI.container)
// Produce nasty smoke.
visible_message(span_danger("\The [src] vomits a gout of rancid smoke!"))
var/datum/effect/effect/system/smoke_spread/bad/burntfood/smoke = new /datum/effect/effect/system/smoke_spread/bad/burntfood
playsound(src, 'sound/effects/smoke.ogg', 20, 1)
smoke.attach(src)
smoke.set_up(10, 0, get_turf(src), 300)
smoke.start()
// CHOMPAdd - Chance to make a terrible fire
if(prob(70))
var/turf/T = get_turf(src)
T.lingering_fire(0.45)
// CHOMPAdd End
// Set off fire alarms!
var/obj/machinery/firealarm/FA = locate() in get_area(src)
if(FA)
FA.alarm()
/obj/machinery/appliance/attack_hand(var/mob/user)
if(..())
return
interact(user)
/obj/machinery/appliance/interact(mob/user)
var/list/options = list(
"power" = radial_power,
"safety" = radial_safety,
)
if(LAZYLEN(cooking_objs))
options["remove"] = radial_eject
if(LAZYLEN(output_options))
options["select_output"] = radial_output
var/choice = show_radial_menu(user, src, options, require_near = !issilicon(user))
switch(choice)
if("power")
toggle_power()
if("safety")
toggle_safety()
if("remove")
removal_menu(user)
if("select_output")
choose_output()
/obj/machinery/appliance/proc/removal_menu(var/mob/user)
if (can_remove_items(user))
var/list/menuoptions = list()
for(var/datum/cooking_item/CI as anything in cooking_objs)
if (CI.container)
menuoptions[CI.container.label(menuoptions.len)] = CI
var/selection = tgui_input_list(user, "Which item would you like to remove?", "Remove ingredients", menuoptions)
if (selection)
var/datum/cooking_item/CI = menuoptions[selection]
eject(CI, user)
update_icon()
return TRUE
return FALSE
/obj/machinery/appliance/proc/can_remove_items(var/mob/user, show_warning = TRUE)
if (!Adjacent(user))
return FALSE
if (isanimal(user))
return FALSE
return TRUE
/obj/machinery/appliance/proc/eject(var/datum/cooking_item/CI, var/mob/user = null)
var/obj/item/thing
var/delete = 1
var/status = CI.container.check_contents()
if (status == 1)//If theres only one object in a container then we extract that
thing = locate(/obj/item) in CI.container
delete = 0
else//If the container is empty OR contains more than one thing, then we must extract the container
thing = CI.container
if (user)
if(!user.put_in_hands(thing))
thing.forceMove(get_turf(src))
else if(istype(thing, /obj/item/reagent_containers/cooking_container))
var/obj/item/reagent_containers/cooking_container/cc = thing
cc.do_empty()
delete = 0
else
thing.forceMove(get_turf(src))
if (delete)
cooking_objs -= CI
qdel(CI)
else
CI.reset()//reset instead of deleting if the container is left inside
if(user)
user.visible_message(span_notice("\The [user] remove \the [thing] from \the [src]."))
else
src.visible_message(span_infoplain(span_bold("\The [src]") + " pings as it automatically ejects its contents!"))
if(cooked_sound)
playsound(get_turf(src), cooked_sound, 50, 1)
/obj/machinery/appliance/proc/cook_mob(var/mob/living/victim, var/mob/user)
return
/obj/machinery/appliance/proc/change_product_strings(var/obj/item/reagent_containers/food/snacks/product, var/datum/cooking_item/CI)
product.name = "[cook_type] [product.name]"
product.desc = "[product.desc]\nIt has been [cook_type]."
/obj/machinery/appliance/proc/change_product_appearance(var/obj/item/reagent_containers/food/snacks/product, var/datum/cooking_item/CI)
if (!product.coating) //Coatings change colour through a new sprite
product.color = food_color
product.filling_color = food_color
/mob/living/proc/calculate_composition() // moved from devour.dm on aurora's side
if (!composition_reagent)//if no reagent has been set, then we'll set one
if (isSynthetic())
src.composition_reagent = REAGENT_ID_IRON
else
if(istype(src, /mob/living/carbon/human/diona) || istype(src, /mob/living/carbon/alien/diona))
src.composition_reagent = REAGENT_ID_NUTRIMENT // diona are plants, not meat
else
src.composition_reagent = REAGENT_ID_PROTEIN
if(ishuman(src))
var/mob/living/carbon/human/H = src
if(istype(H.species, /datum/species/diona))
src.composition_reagent = REAGENT_ID_NUTRIMENT
//if the mob is a simple animal - MOB NOT ANIMAL - with a defined meat quantity
if (isanimal(src))
var/mob/living/simple_mob/SA = src
if(SA.meat_amount)
src.composition_reagent_quantity = SA.meat_amount*2*9
//The quantity of protein is based on the meat_amount, but multiplied by 2
var/size_reagent = (src.mob_size * src.mob_size) * 3//The quantity of protein is set to 3x mob size squared
if (size_reagent > src.composition_reagent_quantity)//We take the larger of the two
src.composition_reagent_quantity = size_reagent
//This function creates a food item which represents a dead mob
/obj/machinery/appliance/proc/create_mob_food(var/obj/item/holder/H, var/datum/cooking_item/CI)
if (!istype(H) || !H.held_mob)
qdel(H)
return null
var/mob/living/victim = H.held_mob
if (victim.stat != DEAD)
return null //Victim somehow survived the cooking, they do not become food
victim.calculate_composition()
var/obj/item/reagent_containers/food/snacks/variable/mob/result = new /obj/item/reagent_containers/food/snacks/variable/mob(CI.container)
result.w_class = victim.mob_size
result.reagents.add_reagent(victim.composition_reagent, victim.composition_reagent_quantity)
if (victim.reagents)
victim.reagents.trans_to_holder(result.reagents, victim.reagents.total_volume)
if (isanimal(victim))
var/mob/living/simple_mob/SA = victim
result.kitchen_tag = SA.kitchen_tag
result.appearance = victim
var/matrix/M = matrix()
M.Turn(45)
M.Translate(1,-2)
result.transform = M
// all done, now delete the old objects
H.held_mob = null
qdel(victim)
victim = null
qdel(H)
H = null
return result
/datum/cooking_item
var/max_cookwork
var/cookwork
var/overcook_mult = 6 // How long it takes to overcook. This is max_cookwork x overcook mult. If you're changing this, mind that at 3x, a max_cookwork of 30 becomes 90 ticks for the purpose of burning, and a max_cookwork of 4 only has 12 before burning! // CHOMPedit: doubled to 6
var/result_type = 0
var/obj/item/reagent_containers/cooking_container/container = null
var/combine_target = null
//Result type is one of the following:
//0 unfinished, no result yet
//1 Standard modification cooking. eg Fried Donk Pocket, Baked wheat, etc
//2 Modification but with a new object that's an inert copy of the old. Generally used for deepfried mice
//3 Combination cooking, EG Donut Bread, Donk pocket pizza, etc
//4:Specific recipe cooking. EG: Turning raw potato sticks into fries
var/burned = 0
var/oil = 0
var/max_oil = 0//Used for fryers.
/datum/cooking_item/New(var/obj/item/I)
container = I
//This is called for containers whose contents are ejected without removing the container
/datum/cooking_item/proc/reset()
max_cookwork = 0
cookwork = 0
result_type = 0
burned = 0
max_oil = 0
oil = 0
combine_target = null
//Container is not reset
/obj/machinery/appliance/RefreshParts()
..()
var/scan_rating = 0
var/cap_rating = 0
for(var/obj/item/stock_parts/P in src.component_parts)
if(istype(P, /obj/item/stock_parts/scanning_module))
scan_rating += P.rating - 1 // Default parts shouldn't mess with stats
// to_world("RefreshParts returned scan rating of [scan_rating] during this step.") // Debug lines, uncomment if you need to test.
else if(istype(P, /obj/item/stock_parts/capacitor))
cap_rating += P.rating - 1 // Default parts shouldn't mess with stats
// to_world("RefreshParts returned cap rating of [cap_rating] during this step.") // Debug lines, uncomment if you need to test.
active_power_usage = initial(active_power_usage) - scan_rating * 25
heating_power = initial(heating_power) + cap_rating * 25
cooking_power = cooking_coeff * (1 + (scan_rating + cap_rating) / 20) // 100% eff. becomes 120%, 140%, 160% w/ better parts, thus rewarding upgrading the appliances during your shift.
// to_world("RefreshParts returned cooking power of [cooking_power] during this step.") // Debug lines, uncomment if you need to test.
/obj/machinery/appliance/verb/toggle_safety()
set name = "Toggle Safety"
set desc = "Toggles whether the appliance automatically ejects food when it starts to burn."
set category = "Object"
set src in view(1)
if(!isliving(usr))
return
food_safety = !food_safety
to_chat(usr, span_notice("You flip \the [src]'s safe mode switch. Safe mode is now [food_safety ? "on" : "off"]."))