mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-09 16:12:17 +00:00
Commit One
This commit is contained in:
728
code/modules/food/kitchen/cooking_machines/_appliance.dm
Normal file
728
code/modules/food/kitchen/cooking_machines/_appliance.dm
Normal file
@@ -0,0 +1,728 @@
|
||||
// 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/weapon/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 = 1
|
||||
anchored = 1
|
||||
|
||||
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/initial_active_power_usage = 1000
|
||||
|
||||
var/cooking_power = 1
|
||||
var/initial_cooking_power = 1
|
||||
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 // 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 // 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/available_recipes
|
||||
|
||||
var/container_type = null
|
||||
|
||||
var/combine_first = 0//If 1, this appliance will do combinaiton cooking before checking recipes
|
||||
|
||||
/obj/machinery/appliance/Initialize()
|
||||
. = ..()
|
||||
component_parts = list()
|
||||
component_parts += /obj/item/weapon/circuitboard/cooking
|
||||
component_parts += /obj/item/weapon/stock_parts/capacitor
|
||||
component_parts += /obj/item/weapon/stock_parts/capacitor
|
||||
component_parts += /obj/item/weapon/stock_parts/capacitor
|
||||
component_parts += /obj/item/weapon/stock_parts/scanning_module
|
||||
component_parts += /obj/item/weapon/stock_parts/matter_bin
|
||||
component_parts += /obj/item/weapon/stock_parts/matter_bin
|
||||
if(output_options.len)
|
||||
verbs += /obj/machinery/appliance/proc/choose_output
|
||||
|
||||
if (!available_recipes)
|
||||
available_recipes = new
|
||||
|
||||
for (var/type in subtypesof(/datum/recipe))
|
||||
var/datum/recipe/test = new type
|
||||
if ((appliancetype & test.appliance))
|
||||
available_recipes += test
|
||||
else
|
||||
qdel(test)
|
||||
|
||||
/obj/machinery/appliance/Destroy()
|
||||
for (var/a in cooking_objs)
|
||||
var/datum/cooking_item/CI = a
|
||||
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(usr))
|
||||
list_contents(user)
|
||||
return 1
|
||||
|
||||
/obj/machinery/appliance/proc/list_contents(var/mob/user)
|
||||
if (cooking_objs.len)
|
||||
var/string = "Contains..."
|
||||
for (var/a in cooking_objs)
|
||||
var/datum/cooking_item/CI = a
|
||||
string += "-\a [CI.container.label(null, CI.combine_target)], [report_progress(CI)]</br>"
|
||||
usr << string
|
||||
else
|
||||
usr << span("notice","It is empty.")
|
||||
|
||||
/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("notice", "<b>It's almost ready!</b>")
|
||||
|
||||
var/half_overcook = (CI.overcook_mult - 1)*0.5
|
||||
if (progress < 1+half_overcook)
|
||||
return span("soghun","<b>It is done !</b>")
|
||||
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())
|
||||
user << "You lack the dexterity to do that!"
|
||||
return
|
||||
|
||||
if (user.stat || user.restrained() || user.incapacitated())
|
||||
return
|
||||
|
||||
if (!Adjacent(user) && !issilicon(user))
|
||||
user << "You can't reach [src] from here."
|
||||
return
|
||||
|
||||
if (stat & POWEROFF)//Its turned off
|
||||
stat &= ~POWEROFF
|
||||
use_power = 1
|
||||
user.visible_message("[user] turns [src] on.", "You turn on [src].")
|
||||
|
||||
else //Its on, turn it off
|
||||
stat |= POWEROFF
|
||||
use_power = 0
|
||||
user.visible_message("[user] turns [src] off.", "You turn off [src].")
|
||||
|
||||
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())
|
||||
usr << "You lack the dexterity to do that!"
|
||||
return
|
||||
|
||||
if (usr.stat || usr.restrained() || usr.incapacitated())
|
||||
return
|
||||
|
||||
if (!Adjacent(usr) && !issilicon(usr))
|
||||
usr << "You can't adjust the [src] from this distance, get closer!"
|
||||
return
|
||||
|
||||
if(output_options.len)
|
||||
var/choice = input("What specific food do you wish to make with \the [src]?") as null|anything in output_options+"Default"
|
||||
if(!choice)
|
||||
return
|
||||
if(choice == "Default")
|
||||
selected_option = null
|
||||
usr << "<span class='notice'>You decide not to make anything specific with \the [src].</span>"
|
||||
else
|
||||
selected_option = choice
|
||||
usr << "<span class='notice'>You prepare \the [src] to make \a [selected_option] with the next thing you put in. Try putting several ingredients in a container!</span>"
|
||||
|
||||
//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/weapon/grab/G = I
|
||||
if(istype(G))
|
||||
|
||||
if(!can_cook_mobs)
|
||||
user << "<span class='warning'>That's not going to fit.</span>"
|
||||
return 0
|
||||
|
||||
if(!isliving(G.affecting))
|
||||
user << "<span class='warning'>You can't cook that.</span>"
|
||||
return 0
|
||||
|
||||
return 2
|
||||
|
||||
|
||||
if (!has_space(I))
|
||||
user << "<span class='warning'>There's no room in [src] for that!</span>"
|
||||
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/weapon/reagent_containers/food/snacks/check = I
|
||||
if(istype(check) && islist(check.cooked) && (cook_type in check.cooked))
|
||||
user << "<span class='warning'>\The [check] has already been [cook_type].</span>"
|
||||
return 0
|
||||
else if(istype(check, /obj/item/weapon/reagent_containers/glass))
|
||||
user << "<span class='warning'>That would probably break [src].</span>"
|
||||
return 0
|
||||
else if(istype(check, /obj/item/weapon/disk/nuclear))
|
||||
user << "<span class='warning'>You can't cook that.</span>"
|
||||
return 0
|
||||
else if(!istype(check) && !istype(check, /obj/item/weapon/holder))
|
||||
user << "<span class='warning'>That's not edible.</span>"
|
||||
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 0
|
||||
|
||||
else return 1
|
||||
|
||||
/obj/machinery/appliance/attackby(var/obj/item/I, var/mob/user)
|
||||
if(!cook_type || (stat & (BROKEN)))
|
||||
user << "<span class='warning'>\The [src] is not working.</span>"
|
||||
return
|
||||
|
||||
var/result = can_insert(I, user)
|
||||
if(!result)
|
||||
if(default_deconstruction_screwdriver(user, I))
|
||||
return
|
||||
else if(default_part_replacement(user, I))
|
||||
return
|
||||
else
|
||||
return
|
||||
|
||||
if(result == 2)
|
||||
var/obj/item/weapon/grab/G = I
|
||||
if (G && istype(G) && G.affecting)
|
||||
cook_mob(G.affecting, user)
|
||||
return
|
||||
|
||||
//From here we can start cooking food
|
||||
add_content(I, user)
|
||||
update_icon()
|
||||
|
||||
//Override for container mechanics
|
||||
/obj/machinery/appliance/proc/add_content(var/obj/item/I, var/mob/user)
|
||||
if(!user.unEquip(I))
|
||||
return
|
||||
|
||||
var/datum/cooking_item/CI = has_space(I)
|
||||
if (istype(I, /obj/item/weapon/reagent_containers/cooking_container) && CI == 1)
|
||||
var/obj/item/weapon/reagent_containers/cooking_container/CC = I
|
||||
CI = new /datum/cooking_item/(CC)
|
||||
I.forceMove(src)
|
||||
cooking_objs.Add(CI)
|
||||
user.visible_message("<span class='notice'>\The [user] puts \the [I] into \the [src].</span>")
|
||||
if (CC.check_contents() == 0)//If we're just putting an empty container in, then dont start any processing.
|
||||
return
|
||||
else
|
||||
if (CI && istype(CI))
|
||||
I.forceMove(CI.container)
|
||||
|
||||
else //Something went wrong
|
||||
return
|
||||
|
||||
if (selected_option)
|
||||
CI.combine_target = selected_option
|
||||
|
||||
// We can actually start cooking now.
|
||||
user.visible_message("<span class='notice'>\The [user] puts \the [I] into \the [src].</span>")
|
||||
|
||||
get_cooking_work(CI)
|
||||
cooking = 1
|
||||
return CI
|
||||
|
||||
/obj/machinery/appliance/proc/get_cooking_work(var/datum/cooking_item/CI)
|
||||
for (var/obj/item/J in CI.container)
|
||||
oilwork(J, CI)
|
||||
|
||||
for (var/r in CI.container.reagents.reagent_list)
|
||||
var/datum/reagent/R = r
|
||||
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 = 1
|
||||
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/oilwork(var/obj/item/I, var/datum/cooking_item/CI)
|
||||
var/obj/item/weapon/reagent_containers/food/snacks/S = I
|
||||
var/work = 0
|
||||
if (istype(S))
|
||||
if (S.reagents)
|
||||
for (var/r in S.reagents.reagent_list)
|
||||
var/datum/reagent/R = r
|
||||
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/weapon/holder))
|
||||
var/obj/item/weapon/holder/H = I
|
||||
if (H.held_mob)
|
||||
work += (H.held_mob.mob_size * H.held_mob.mob_size * 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 0
|
||||
|
||||
var/was_done = 0
|
||||
if (CI.cookwork >= CI.max_cookwork)
|
||||
was_done = 1
|
||||
|
||||
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 > CI.max_cookwork * CI.overcook_mult)
|
||||
burn_food(CI)
|
||||
|
||||
// Gotta hurt.
|
||||
for(var/obj/item/weapon/holder/H in CI.container.contents)
|
||||
var/mob/living/M = H.held_mob
|
||||
if (M)
|
||||
M.apply_damage(rand(1,3), mobdamagetype, "chest")
|
||||
|
||||
return 1
|
||||
|
||||
/obj/machinery/appliance/process()
|
||||
if (cooking_power > 0 && cooking)
|
||||
for (var/i in cooking_objs)
|
||||
do_cooking_tick(i)
|
||||
|
||||
|
||||
/obj/machinery/appliance/proc/finish_cooking(var/datum/cooking_item/CI)
|
||||
|
||||
src.visible_message("<span class='notice'>\The [src] pings!</span>")
|
||||
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(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(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/r in results)
|
||||
var/obj/item/weapon/reagent_containers/food/snacks/R = r
|
||||
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/weapon/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
|
||||
|
||||
for (var/obj/item/I in CI.container)
|
||||
var/obj/item/weapon/reagent_containers/food/snacks/S
|
||||
if (istype(I, /obj/item/weapon/holder))
|
||||
S = create_mob_food(I, CI)
|
||||
else if (istype(I, /obj/item/weapon/reagent_containers/food/snacks))
|
||||
S = I
|
||||
|
||||
if (!S)
|
||||
continue
|
||||
|
||||
words |= text2list(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/weapon/reagent_containers/food/snacks/result = new cook_path(CI.container)
|
||||
buffer.trans_to(result, buffer.total_volume)
|
||||
|
||||
//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/weapon/reagent_containers/food/snacks/result
|
||||
if (istype(input, /obj/item/weapon/holder))
|
||||
result = create_mob_food(input, CI)
|
||||
else if (istype(input, /obj/item/weapon/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/weapon/reagent_containers/food/snacks/badrecipe(CI.container)
|
||||
|
||||
// Produce nasty smoke.
|
||||
visible_message("<span class='danger'>\The [src] vomits a gout of rancid smoke!</span>")
|
||||
var/datum/effect/effect/system/smoke_spread/bad/smoke = new /datum/effect/effect/system/smoke_spread/bad
|
||||
smoke.attach(src)
|
||||
smoke.set_up(10, 0, get_turf(src), 300)
|
||||
smoke.start()
|
||||
|
||||
/obj/machinery/appliance/attack_hand(var/mob/user)
|
||||
if (cooking_objs.len)
|
||||
if (removal_menu(user))
|
||||
return
|
||||
else
|
||||
..()
|
||||
|
||||
/obj/machinery/appliance/proc/removal_menu(var/mob/user)
|
||||
if (can_remove_items(user))
|
||||
var/list/menuoptions = list()
|
||||
for (var/a in cooking_objs)
|
||||
var/datum/cooking_item/CI = a
|
||||
if (CI.container)
|
||||
menuoptions[CI.container.label(menuoptions.len)] = CI
|
||||
|
||||
var/selection = input(user, "Which item would you like to remove?", "Remove ingredients") as null|anything in menuoptions
|
||||
if (selection)
|
||||
var/datum/cooking_item/CI = menuoptions[selection]
|
||||
eject(CI, user)
|
||||
update_icon()
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/obj/machinery/appliance/proc/can_remove_items(var/mob/user)
|
||||
if (!Adjacent(user))
|
||||
return 0
|
||||
|
||||
if (isanimal(user))
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
/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 || !user.put_in_hands(thing))
|
||||
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
|
||||
|
||||
/obj/machinery/appliance/proc/cook_mob(var/mob/living/victim, var/mob/user)
|
||||
return
|
||||
|
||||
/obj/machinery/appliance/proc/change_product_strings(var/obj/item/weapon/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/weapon/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 = "iron"
|
||||
else
|
||||
if(istype(src, /mob/living/carbon/human/diona) || istype(src, /mob/living/carbon/alien/diona))
|
||||
src.composition_reagent = "nutriment" // diona are plants, not meat
|
||||
else
|
||||
src.composition_reagent = "protein"
|
||||
if(istype(src, /mob/living/carbon/human))
|
||||
var/mob/living/carbon/human/H = src
|
||||
if(istype(H.species, /datum/species/diona))
|
||||
src.composition_reagent = "nutriment"
|
||||
|
||||
//if the mob is a simple animal with a defined meat quantity
|
||||
if (istype(src, /mob/living/simple_animal))
|
||||
var/mob/living/simple_animal/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/weapon/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/weapon/reagent_containers/food/snacks/variable/mob/result = new /obj/item/weapon/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_animal/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 = 5
|
||||
var/result_type = 0
|
||||
var/obj/item/weapon/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/weapon/stock_parts/P in src.component_parts)
|
||||
if(istype(P, /obj/item/weapon/stock_parts/scanning_module))
|
||||
scan_rating += P.rating
|
||||
else if(istype(P, /obj/item/weapon/stock_parts/capacitor))
|
||||
cap_rating += P.rating
|
||||
|
||||
active_power_usage = initial(active_power_usage) - scan_rating*10
|
||||
cooking_power = initial(cooking_power) + (scan_rating+cap_rating)/10
|
||||
|
||||
/obj/item/weapon/circuitboard/cooking
|
||||
name = "kitchen appliance circuitry"
|
||||
desc = "The circuitboard for many kitchen appliances. Not of much use."
|
||||
origin_tech = list(TECH_MAGNET = 2, TECH_ENGINEERING = 2)
|
||||
req_components = list(
|
||||
/obj/item/weapon/stock_parts/capacitor = 3,
|
||||
/obj/item/weapon/stock_parts/scanning_module = 1,
|
||||
/obj/item/weapon/stock_parts/matter_bin = 2)
|
||||
@@ -1,261 +1,132 @@
|
||||
// This folder contains code that was originally ported from Apollo Station and then refactored/optimized/changed.
|
||||
/obj/machinery/appliance/cooker
|
||||
var/temperature = T20C
|
||||
var/min_temp = 80 + T0C //Minimum temperature to do any cooking
|
||||
var/optimal_temp = 200 + T0C //Temperature at which we have 100% efficiency. efficiency is lowered on either side of this
|
||||
var/optimal_power = 0.1//cooking power at 100%
|
||||
|
||||
// Tracks precooked food to stop deep fried baked grilled grilled grilled diona nymph cereal.
|
||||
/obj/item/weapon/reagent_containers/food/snacks/var/list/cooked
|
||||
var/loss = 1 //Temp lost per proc when equalising
|
||||
var/resistance = 320000 //Resistance to heating. combines with active power usage to determine how long heating takes
|
||||
|
||||
// Root type for cooking machines. See following files for specific implementations.
|
||||
/obj/machinery/cooker
|
||||
name = "cooker"
|
||||
desc = "You shouldn't be seeing this!"
|
||||
icon = 'icons/obj/cooking_machines.dmi'
|
||||
density = 1
|
||||
anchored = 1
|
||||
use_power = USE_POWER_IDLE
|
||||
idle_power_usage = 5
|
||||
var/light_x = 0
|
||||
var/light_y = 0
|
||||
cooking_power = 0
|
||||
|
||||
var/on_icon // Icon state used when cooking.
|
||||
var/off_icon // Icon state used when not cooking.
|
||||
var/cooking // 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/cook_time = 200 // How many ticks the cooking will take.
|
||||
var/can_cook_mobs // Whether or not this machine accepts grabbed mobs.
|
||||
var/food_color // Colour of resulting food item.
|
||||
var/cooked_sound // Sound played when cooking completes.
|
||||
var/can_burn_food // Can the object burn food that is left inside?
|
||||
var/burn_chance = 10 // How likely is the food to burn?
|
||||
var/obj/item/cooking_obj // Holder for the currently cooking object.
|
||||
|
||||
// If the machine has multiple output modes, define them here.
|
||||
var/selected_option
|
||||
var/list/output_options = list()
|
||||
|
||||
/obj/machinery/cooker/Destroy()
|
||||
if(cooking_obj)
|
||||
qdel(cooking_obj)
|
||||
cooking_obj = null
|
||||
return ..()
|
||||
|
||||
/obj/machinery/cooker/proc/set_cooking(new_setting)
|
||||
cooking = new_setting
|
||||
icon_state = new_setting ? on_icon : off_icon
|
||||
|
||||
/obj/machinery/cooker/examine(mob/user)
|
||||
/obj/machinery/appliance/cooker/examine(var/mob/user)
|
||||
. = ..()
|
||||
if(cooking_obj && Adjacent(user))
|
||||
. += "You can see \a [cooking_obj] inside."
|
||||
|
||||
/obj/machinery/cooker/attackby(var/obj/item/I, var/mob/user)
|
||||
|
||||
if(!cook_type || (stat & (NOPOWER|BROKEN)))
|
||||
to_chat(user, "<span class='warning'>\The [src] is not working.</span>")
|
||||
return
|
||||
|
||||
if(cooking)
|
||||
to_chat(user, "<span class='warning'>\The [src] is running!</span>")
|
||||
return
|
||||
|
||||
if(default_unfasten_wrench(user, I, 20))
|
||||
return
|
||||
|
||||
// We are trying to cook a grabbed mob.
|
||||
var/obj/item/weapon/grab/G = I
|
||||
if(istype(G))
|
||||
|
||||
if(!can_cook_mobs)
|
||||
to_chat(user, "<span class='warning'>That's not going to fit.</span>")
|
||||
return
|
||||
|
||||
if(!isliving(G.affecting))
|
||||
to_chat(user, "<span class='warning'>You can't cook that.</span>")
|
||||
return
|
||||
|
||||
cook_mob(G.affecting, user)
|
||||
return
|
||||
|
||||
// We're trying to cook something else. Check if it's valid.
|
||||
var/obj/item/weapon/reagent_containers/food/snacks/check = I
|
||||
if(istype(check) && islist(check.cooked) && (cook_type in check.cooked))
|
||||
to_chat(user, "<span class='warning'>\The [check] has already been [cook_type].</span>")
|
||||
return 0
|
||||
else if(istype(check, /obj/item/weapon/reagent_containers/glass))
|
||||
to_chat(user, "<span class='warning'>That would probably break [src].</span>")
|
||||
return 0
|
||||
else if(istype(check, /obj/item/weapon/disk/nuclear))
|
||||
to_chat(user, "Central Command would kill you if you [cook_type] that.")
|
||||
return 0
|
||||
else if(!istype(check) && !istype(check, /obj/item/weapon/holder) && !istype(check, /obj/item/organ)) //Gripper check has to go here, else it still just cuts it off. ~Mechoid
|
||||
// Is it a borg using a gripper?
|
||||
if(istype(check, /obj/item/weapon/gripper)) // Grippers. ~Mechoid.
|
||||
var/obj/item/weapon/gripper/B = check //B, for Borg.
|
||||
if(!B.wrapped)
|
||||
to_chat(user, "\The [B] is not holding anything.")
|
||||
return 0
|
||||
if(.) //no need to duplicate adjacency check
|
||||
if(!stat)
|
||||
if (temperature < min_temp)
|
||||
to_chat(user, span("warning", "\The [src] is still heating up and is too cold to cook anything yet."))
|
||||
else
|
||||
var/B_held = B.wrapped
|
||||
to_chat(user, "You use \the [B] to put \the [B_held] into \the [src].")
|
||||
return 0
|
||||
to_chat(user, span("notice", "It is running at [round(get_efficiency(), 0.1)]% efficiency!"))
|
||||
to_chat(user, "Temperature: [round(temperature - T0C, 0.1)]C / [round(optimal_temp - T0C, 0.1)]C")
|
||||
else
|
||||
to_chat(user, "<span class='warning'>That's not edible.</span>")
|
||||
return 0
|
||||
|
||||
if(istype(I, /obj/item/organ))
|
||||
var/obj/item/organ/O = I
|
||||
if(O.robotic)
|
||||
to_chat(user, "<span class='warning'>That would probably break [src].</span>")
|
||||
return
|
||||
|
||||
// Gotta hurt.
|
||||
if(istype(cooking_obj, /obj/item/weapon/holder))
|
||||
for(var/mob/living/M in cooking_obj.contents)
|
||||
M.apply_damage(rand(30,40), BURN, "chest")
|
||||
|
||||
// Not sure why a food item that passed the previous checks would fail to drop, but safety first. (Hint: Borg grippers. That is why. ~Mechoid.)
|
||||
if(!user.unEquip(I) && !istype(user,/mob/living/silicon/robot))
|
||||
return
|
||||
|
||||
// We can actually start cooking now.
|
||||
user.visible_message("<span class='notice'>\The [user] puts \the [I] into \the [src].</span>")
|
||||
cooking_obj = I
|
||||
cooking_obj.forceMove(src)
|
||||
set_cooking(TRUE)
|
||||
icon_state = on_icon
|
||||
|
||||
// Doop de doo. Jeopardy theme goes here.
|
||||
sleep(cook_time)
|
||||
|
||||
// Sanity checks.
|
||||
if(!cooking_obj || cooking_obj.loc != src)
|
||||
cooking_obj = null
|
||||
icon_state = off_icon
|
||||
set_cooking(FALSE)
|
||||
return
|
||||
|
||||
// RIP slow-moving held mobs.
|
||||
if(istype(cooking_obj, /obj/item/weapon/holder))
|
||||
for(var/mob/living/M in cooking_obj.contents)
|
||||
M.death()
|
||||
qdel(M)
|
||||
|
||||
// Cook the food.
|
||||
var/cook_path
|
||||
if(selected_option && output_options.len)
|
||||
cook_path = output_options[selected_option]
|
||||
if(!cook_path)
|
||||
cook_path = /obj/item/weapon/reagent_containers/food/snacks/variable
|
||||
var/obj/item/weapon/reagent_containers/food/snacks/result = new cook_path(src) //Holy typepaths, Batman.
|
||||
|
||||
// Set icon and appearance.
|
||||
change_product_appearance(result)
|
||||
|
||||
// Update strings.
|
||||
change_product_strings(result)
|
||||
|
||||
// Copy reagents over. trans_to_obj must be used, as trans_to fails for snacks due to is_open_container() failing.
|
||||
if(cooking_obj.reagents && cooking_obj.reagents.total_volume)
|
||||
cooking_obj.reagents.trans_to_obj(result, cooking_obj.reagents.total_volume)
|
||||
|
||||
// Set cooked data.
|
||||
var/obj/item/weapon/reagent_containers/food/snacks/food_item = cooking_obj
|
||||
if(istype(food_item) && islist(food_item.cooked))
|
||||
result.cooked = food_item.cooked.Copy()
|
||||
to_chat(user, span("warning", "It is switched off."))
|
||||
|
||||
/obj/machinery/appliance/cooker/list_contents(var/mob/user)
|
||||
if (cooking_objs.len)
|
||||
var/string = "Contains...</br>"
|
||||
var/num = 0
|
||||
for (var/a in cooking_objs)
|
||||
num++
|
||||
var/datum/cooking_item/CI = a
|
||||
if (CI && CI.container)
|
||||
string += "- [CI.container.label(num)], [report_progress(CI)]</br>"
|
||||
to_chat(user, string)
|
||||
else
|
||||
result.cooked = list()
|
||||
result.cooked |= cook_type
|
||||
to_chat(user, span("notice","It is empty."))
|
||||
|
||||
/obj/machinery/appliance/cooker/proc/get_efficiency()
|
||||
//RefreshParts()
|
||||
return (cooking_power / optimal_power) * 100
|
||||
|
||||
// Reset relevant variables.
|
||||
qdel(cooking_obj)
|
||||
src.visible_message("<span class='notice'>\The [src] pings!</span>")
|
||||
if(cooked_sound)
|
||||
playsound(src, cooked_sound, 50, 1)
|
||||
/obj/machinery/appliance/cooker/New()
|
||||
. = ..()
|
||||
loss = (active_power_usage / resistance)*0.5
|
||||
cooking_objs = list()
|
||||
for (var/i = 0, i < max_contents, i++)
|
||||
cooking_objs.Add(new /datum/cooking_item/(new container_type(src)))
|
||||
cooking = 0
|
||||
|
||||
if(!can_burn_food)
|
||||
icon_state = off_icon
|
||||
set_cooking(FALSE)
|
||||
result.forceMove(get_turf(src))
|
||||
cooking_obj = null
|
||||
update_icon() // this probably won't cause issues, but Aurora used SSIcons and queue_icon_update() instead
|
||||
|
||||
/obj/machinery/appliance/cooker/update_icon()
|
||||
cut_overlays()
|
||||
var/image/light
|
||||
if (use_power == 2 && !stat)
|
||||
light = image(icon, "light_on")
|
||||
else
|
||||
var/failed
|
||||
var/overcook_period = max(FLOOR(cook_time/5, 1),1)
|
||||
cooking_obj = result
|
||||
var/count = overcook_period
|
||||
while(1)
|
||||
sleep(overcook_period)
|
||||
count += overcook_period
|
||||
if(!cooking || !result || result.loc != src)
|
||||
failed = 1
|
||||
else if(prob(burn_chance) || count == cook_time) //Fail before it has a chance to cook again.
|
||||
// You dun goofed.
|
||||
qdel(cooking_obj)
|
||||
cooking_obj = new /obj/item/weapon/reagent_containers/food/snacks/badrecipe(src)
|
||||
// Produce nasty smoke.
|
||||
visible_message("<span class='danger'>\The [src] vomits a gout of rancid smoke!</span>")
|
||||
var/datum/effect/effect/system/smoke_spread/bad/smoke = new /datum/effect/effect/system/smoke_spread/bad()
|
||||
smoke.attach(src)
|
||||
smoke.set_up(10, 0, usr.loc)
|
||||
smoke.start()
|
||||
failed = 1
|
||||
|
||||
if(failed)
|
||||
set_cooking(FALSE)
|
||||
icon_state = off_icon
|
||||
break
|
||||
|
||||
/obj/machinery/cooker/attack_hand(var/mob/user)
|
||||
|
||||
if(cooking_obj && user.Adjacent(src)) //Fixes borgs being able to teleport food in these machines to themselves.
|
||||
to_chat(user, "<span class='notice'>You grab \the [cooking_obj] from \the [src].</span>")
|
||||
user.put_in_hands(cooking_obj)
|
||||
set_cooking(FALSE)
|
||||
cooking_obj = null
|
||||
icon_state = off_icon
|
||||
return
|
||||
|
||||
if(output_options.len)
|
||||
|
||||
if(cooking)
|
||||
to_chat(user, "<span class='warning'>\The [src] is in use!</span>")
|
||||
return
|
||||
|
||||
var/choice = input("What specific food do you wish to make with \the [src]?") as null|anything in output_options+"Default"
|
||||
if(!choice)
|
||||
return
|
||||
if(choice == "Default")
|
||||
selected_option = null
|
||||
to_chat(user, "<span class='notice'>You decide not to make anything specific with \the [src].</span>")
|
||||
else
|
||||
selected_option = choice
|
||||
to_chat(user, "<span class='notice'>You prepare \the [src] to make \a [selected_option].</span>")
|
||||
light = image(icon, "light_off")
|
||||
light.pixel_x = light_x
|
||||
light.pixel_y = light_y
|
||||
add_overlay(light)
|
||||
|
||||
/obj/machinery/appliance/cooker/process()
|
||||
if (!stat)
|
||||
heat_up()
|
||||
else
|
||||
var/turf/T = get_turf(src)
|
||||
if (temperature > T.temperature)
|
||||
equalize_temperature()
|
||||
..()
|
||||
|
||||
/obj/machinery/appliance/cooker/power_change()
|
||||
. = ..()
|
||||
update_icon() // this probably won't cause issues, but Aurora used SSIcons and queue_icon_update() instead
|
||||
|
||||
/obj/machinery/appliance/cooker/proc/update_cooking_power()
|
||||
var/temp_scale = 0
|
||||
if(temperature > min_temp)
|
||||
temp_scale = (temperature - min_temp) / (optimal_temp - min_temp) // If we're between min and optimal this will yield a value in the range 0-1
|
||||
|
||||
/obj/machinery/cooker/proc/cook_mob(var/mob/living/victim, var/mob/user)
|
||||
return
|
||||
|
||||
/obj/machinery/cooker/proc/change_product_strings(var/obj/item/weapon/reagent_containers/food/snacks/product)
|
||||
if(product.type == /obj/item/weapon/reagent_containers/food/snacks/variable) // Base type, generic.
|
||||
product.name = "[cook_type] [cooking_obj.name]"
|
||||
product.desc = "[cooking_obj.desc] It has been [cook_type]."
|
||||
if(temp_scale > 1) // We're above optimal, efficiency goes down as we pass too much over it
|
||||
if(temp_scale >= 2)
|
||||
temp_scale = 0
|
||||
else
|
||||
temp_scale = 1 - (temp_scale - 1)
|
||||
|
||||
cooking_power = optimal_power * temp_scale
|
||||
// RefreshParts()
|
||||
|
||||
/obj/machinery/appliance/cooker/proc/heat_up()
|
||||
if(temperature < optimal_temp)
|
||||
if(use_power == 1 && ((optimal_temp - temperature) > 5))
|
||||
playsound(src, 'sound/machines/click.ogg', 20, 1)
|
||||
use_power = 2.//If we're heating we use the active power
|
||||
update_icon()
|
||||
temperature += active_power_usage / resistance
|
||||
update_cooking_power()
|
||||
return 1
|
||||
else
|
||||
product.name = "[cooking_obj.name] [product.name]"
|
||||
if(use_power == 2)
|
||||
use_power = 1
|
||||
playsound(src, 'sound/machines/click.ogg', 20, 1)
|
||||
update_icon()
|
||||
//We're holding steady. temperature falls more slowly
|
||||
if(prob(25))
|
||||
equalize_temperature()
|
||||
return -1
|
||||
|
||||
/obj/machinery/cooker/proc/change_product_appearance(var/obj/item/weapon/reagent_containers/food/snacks/product)
|
||||
if(product.type == /obj/item/weapon/reagent_containers/food/snacks/variable) // Base type, generic.
|
||||
product.appearance = cooking_obj
|
||||
product.color = food_color
|
||||
product.filling_color = food_color
|
||||
/obj/machinery/appliance/cooker/proc/equalize_temperature()
|
||||
temperature -= loss//Temperature will fall somewhat slowly
|
||||
update_cooking_power()
|
||||
|
||||
// Make 'em into a corpse.
|
||||
if(istype(cooking_obj, /obj/item/weapon/holder))
|
||||
var/matrix/M = matrix()
|
||||
M.Turn(90)
|
||||
M.Translate(1,-6)
|
||||
product.transform = M
|
||||
//Cookers do differently, they use containers
|
||||
/obj/machinery/appliance/cooker/has_space(var/obj/item/I)
|
||||
if(istype(I, /obj/item/weapon/reagent_containers/cooking_container))
|
||||
//Containers can go into an empty slot
|
||||
if(cooking_objs.len < max_contents)
|
||||
return 1
|
||||
else
|
||||
var/image/I = image(product.icon, "[product.icon_state]_filling")
|
||||
if(istype(cooking_obj, /obj/item/weapon/reagent_containers/food/snacks))
|
||||
var/obj/item/weapon/reagent_containers/food/snacks/S = cooking_obj
|
||||
I.color = S.filling_color
|
||||
if(!I.color)
|
||||
I.color = food_color
|
||||
product.overlays += I
|
||||
//Any food items directly added need an empty container. A slot without a container cant hold food
|
||||
for (var/datum/cooking_item/CI in cooking_objs)
|
||||
if (CI.container.check_contents() == 0)
|
||||
return CI
|
||||
|
||||
return 0
|
||||
|
||||
/obj/machinery/appliance/cooker/add_content(var/obj/item/I, var/mob/user)
|
||||
var/datum/cooking_item/CI = ..()
|
||||
if (CI && CI.combine_target)
|
||||
to_chat(user, "\The [I] will be used to make a [selected_option]. Output selection is returned to default for future items.")
|
||||
selected_option = null
|
||||
@@ -1,72 +1,160 @@
|
||||
// Wrapper obj for cooked food. Appearance is set in the cooking code, not on spawn.
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable
|
||||
name = "cooked food"
|
||||
icon = 'icons/obj/food_custom.dmi'
|
||||
desc = "If you can see this description then something is wrong. Please report the bug on the tracker."
|
||||
nutriment_amt = 5
|
||||
bitesize = 2
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/pizza
|
||||
name = "personal pizza"
|
||||
desc = "A personalized pan pizza meant for only one person."
|
||||
icon_state = "personal_pizza"
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/bread
|
||||
name = "bread"
|
||||
desc = "Tasty bread."
|
||||
icon_state = "breadcustom"
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/pie
|
||||
name = "pie"
|
||||
desc = "Tasty pie."
|
||||
icon_state = "piecustom"
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/cake
|
||||
name = "cake"
|
||||
desc = "A popular band."
|
||||
icon_state = "cakecustom"
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/pocket
|
||||
name = "hot pocket"
|
||||
desc = "You wanna put a bangin- oh, nevermind."
|
||||
icon_state = "donk"
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/kebab
|
||||
name = "kebab"
|
||||
desc = "Remove this!"
|
||||
icon_state = "kabob"
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/waffles
|
||||
name = "waffles"
|
||||
desc = "Made with love."
|
||||
icon_state = "waffles"
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/cookie
|
||||
name = "cookie"
|
||||
desc = "Sugar snap!"
|
||||
icon_state = "cookie"
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/donut
|
||||
name = "filled donut"
|
||||
desc = "Donut eat this!" // kill me
|
||||
icon_state = "donut"
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/jawbreaker
|
||||
name = "flavored jawbreaker"
|
||||
desc = "It's like cracking a molar on a rainbow."
|
||||
icon_state = "jawbreaker"
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/candybar
|
||||
name = "flavored chocolate bar"
|
||||
desc = "Made in a factory downtown."
|
||||
icon_state = "bar"
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/sucker
|
||||
name = "flavored sucker"
|
||||
desc = "Suck, suck, suck."
|
||||
icon_state = "sucker"
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/jelly
|
||||
name = "jelly"
|
||||
desc = "All your friends will be jelly."
|
||||
icon_state = "jellycustom"
|
||||
// Wrapper obj for cooked food. Appearance is set in the cooking code, not on spawn.
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable
|
||||
name = "cooked food"
|
||||
icon = 'icons/obj/food_custom.dmi'
|
||||
desc = "If you can see this description then something is wrong. Please report the bug on the tracker."
|
||||
bitesize = 2
|
||||
|
||||
var/size = 5 //The quantity of reagents which is considered "normal" for this kind of food
|
||||
//These objects will change size depending on the ratio of reagents to this value
|
||||
var/min_scale = 0.5
|
||||
var/max_scale = 2
|
||||
var/scale = 1
|
||||
|
||||
w_class = 2
|
||||
var/prefix
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/initialize()
|
||||
. = ..()
|
||||
if (reagents)
|
||||
reagents.maximum_volume = size*8 + 10
|
||||
else
|
||||
create_reagents(size*8 + 10)
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/update_icon()
|
||||
if (reagents && reagents.total_volume)
|
||||
var/ratio = reagents.total_volume / size
|
||||
|
||||
scale = ratio**(1/3) //Scaling factor is square root of desired area
|
||||
scale = Clamp(scale, min_scale, max_scale)
|
||||
else
|
||||
scale = min_scale
|
||||
|
||||
var/matrix/M = matrix()
|
||||
M.Scale(scale)
|
||||
src.transform = M
|
||||
|
||||
w_class *= scale
|
||||
if (!prefix)
|
||||
if (scale == min_scale)
|
||||
prefix = "tiny"
|
||||
else if (scale <= 0.8)
|
||||
prefix = "small"
|
||||
|
||||
else
|
||||
if (scale >= 1.2)
|
||||
prefix = "large"
|
||||
if (scale >= 1.4)
|
||||
prefix = "extra large"
|
||||
if (scale >= 1.6)
|
||||
prefix = "huge"
|
||||
if (scale >= max_scale)
|
||||
prefix = "massive"
|
||||
|
||||
name = "[prefix] [name]"
|
||||
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/pizza
|
||||
name = "personal pizza"
|
||||
desc = "A personalized pan pizza meant for only one person."
|
||||
icon_state = "personal_pizza"
|
||||
size = 20
|
||||
w_class = 3
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/bread
|
||||
name = "bread"
|
||||
desc = "Tasty bread."
|
||||
icon_state = "breadcustom"
|
||||
size = 40
|
||||
w_class = 3
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/pie
|
||||
name = "pie"
|
||||
desc = "Tasty pie."
|
||||
icon_state = "piecustom"
|
||||
size = 25
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/cake
|
||||
name = "cake"
|
||||
desc = "A popular band."
|
||||
icon_state = "cakecustom"
|
||||
size = 40
|
||||
w_class = 3
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/pocket
|
||||
name = "hot pocket"
|
||||
desc = "You wanna put a bangin- oh, nevermind."
|
||||
icon_state = "donk"
|
||||
size = 8
|
||||
w_class = 1
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/kebab
|
||||
name = "kebab"
|
||||
desc = "Remove this!"
|
||||
icon_state = "kabob"
|
||||
size = 10
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/waffles
|
||||
name = "waffles"
|
||||
desc = "Made with love."
|
||||
icon_state = "waffles"
|
||||
size = 12
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/cookie
|
||||
name = "cookie"
|
||||
desc = "Sugar snap!"
|
||||
icon_state = "cookie"
|
||||
size = 6
|
||||
w_class = 1
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/donut
|
||||
name = "filled donut"
|
||||
desc = "Donut eat this!" // kill me
|
||||
icon_state = "donut"
|
||||
size = 8
|
||||
w_class = 1
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/jawbreaker
|
||||
name = "flavored jawbreaker"
|
||||
desc = "It's like cracking a molar on a rainbow."
|
||||
icon_state = "jawbreaker"
|
||||
size = 4
|
||||
w_class = 1
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/candybar
|
||||
name = "flavored chocolate bar"
|
||||
desc = "Made in a factory downtown."
|
||||
icon_state = "bar"
|
||||
size = 6
|
||||
w_class = 1
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/sucker
|
||||
name = "flavored sucker"
|
||||
desc = "Suck, suck, suck."
|
||||
icon_state = "sucker"
|
||||
size = 4
|
||||
w_class = 1
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/jelly
|
||||
name = "jelly"
|
||||
desc = "All your friends will be jelly."
|
||||
icon_state = "jellycustom"
|
||||
size = 8
|
||||
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/cereal
|
||||
name = "cereal"
|
||||
desc = "Crispy and flaky"
|
||||
icon_state = "cereal_box"
|
||||
size = 30
|
||||
w_class = 3
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/cereal/initialize()
|
||||
. =..()
|
||||
name = pick(list("flakes", "krispies", "crunch", "pops", "O's", "crisp", "loops", "jacks", "clusters"))
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/variable/mob
|
||||
desc = "Poor little thing."
|
||||
size = 5
|
||||
w_class = 1
|
||||
var/kitchen_tag = "animal"
|
||||
141
code/modules/food/kitchen/cooking_machines/_mixer.dm
Normal file
141
code/modules/food/kitchen/cooking_machines/_mixer.dm
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
The mixer subtype is used for the candymaker and cereal maker. They are similar to cookers but with a few
|
||||
fundamental differences
|
||||
1. They have a single container which cant be removed. it will eject multiple contents
|
||||
2. Items can't be added or removed once the process starts
|
||||
3. Items are all placed in the same container when added directly
|
||||
4. They do combining mode only. And will always combine the entire contents of the container into an output
|
||||
*/
|
||||
|
||||
/obj/machinery/appliance/mixer
|
||||
max_contents = 1
|
||||
stat = POWEROFF
|
||||
cooking_power = 0.4
|
||||
active_power_usage = 3000
|
||||
idle_power_usage = 50
|
||||
|
||||
/obj/machinery/appliance/mixer/examine(var/mob/user)
|
||||
..()
|
||||
to_chat(user, "<span class='notice'>It is currently set to make a [selected_option]</span>")
|
||||
|
||||
/obj/machinery/appliance/mixer/New()
|
||||
. = ..()
|
||||
cooking_objs += new /datum/cooking_item(new /obj/item/weapon/reagent_containers/cooking_container(src))
|
||||
cooking = 0
|
||||
selected_option = pick(output_options)
|
||||
|
||||
//Mixers cannot-not do combining mode. So the default option is removed from this. A combine target must be chosen
|
||||
/obj/machinery/appliance/mixer/choose_output()
|
||||
set src in oview(1)
|
||||
set name = "Choose output"
|
||||
set category = "Object"
|
||||
|
||||
if (!isliving(usr))
|
||||
return
|
||||
|
||||
if (!usr.IsAdvancedToolUser())
|
||||
to_chat(user, "<span class='notice'>You can't operate [src].</span>")
|
||||
return
|
||||
|
||||
if(output_options.len)
|
||||
var/choice = input("What specific food do you wish to make with \the [src]?") as null|anything in output_options
|
||||
if(!choice)
|
||||
return
|
||||
else
|
||||
selected_option = choice
|
||||
to_chat(user, "<span class='notice'>You prepare \the [src] to make \a [selected_option].</span>")
|
||||
var/datum/cooking_item/CI = cooking_objs[1]
|
||||
CI.combine_target = selected_option
|
||||
|
||||
|
||||
/obj/machinery/appliance/mixer/has_space(var/obj/item/I)
|
||||
var/datum/cooking_item/CI = cooking_objs[1]
|
||||
if (!CI || !CI.container)
|
||||
return 0
|
||||
|
||||
if (CI.container.can_fit(I))
|
||||
return CI
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
/obj/machinery/appliance/mixer/can_remove_items(var/mob/user)
|
||||
if (stat)
|
||||
return 1
|
||||
else
|
||||
to_chat(user, "<span class='warning'>You can't remove ingredients while it's turned on! Turn it off first or wait for it to finish.</span>")
|
||||
|
||||
//Container is not removable
|
||||
/obj/machinery/appliance/mixer/removal_menu(var/mob/user)
|
||||
if (can_remove_items(user))
|
||||
var/list/menuoptions = list()
|
||||
for (var/a in cooking_objs)
|
||||
var/datum/cooking_item/CI = a
|
||||
if (CI.container)
|
||||
if (!CI.container.check_contents())
|
||||
to_chat(user, "There's nothing in [src] you can remove!")
|
||||
return
|
||||
|
||||
for (var/obj/item/I in CI.container)
|
||||
menuoptions[I.name] = I
|
||||
|
||||
var/selection = input(user, "Which item would you like to remove? If you want to remove chemicals, use an empty beaker.", "Remove ingredients") as null|anything in menuoptions
|
||||
if (selection)
|
||||
var/obj/item/I = menuoptions[selection]
|
||||
if (!user || !user.put_in_hands(I))
|
||||
I.forceMove(get_turf(src))
|
||||
update_icon()
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
/obj/machinery/appliance/mixer/toggle_power()
|
||||
set src in view()
|
||||
set name = "Toggle Power"
|
||||
set category = "Object"
|
||||
|
||||
var/datum/cooking_item/CI = cooking_objs[1]
|
||||
if(!CI.container.check_contents())
|
||||
to_chat("There's nothing in it! Add ingredients before turning [src] on!")
|
||||
return
|
||||
|
||||
if(stat & POWEROFF)//Its turned off
|
||||
stat &= ~POWEROFF
|
||||
if(usr)
|
||||
usr.visible_message("[usr] turns the [src] on", "You turn on \the [src].")
|
||||
get_cooking_work(CI)
|
||||
use_power = 2
|
||||
else //Its on, turn it off
|
||||
stat |= POWEROFF
|
||||
use_power = 0
|
||||
if(usr)
|
||||
usr.visible_message("[usr] turns the [src] off", "You turn off \the [src].")
|
||||
playsound(src, 'sound/machines/click.ogg', 40, 1)
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/appliance/mixer/can_insert(var/obj/item/I, var/mob/user)
|
||||
if(!stat)
|
||||
to_chat(user, "<span class='warning'>,You can't add items while \the [src] is running. Wait for it to finish or turn the power off to abort.</span>")
|
||||
return 0
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/machinery/appliance/mixer/finish_cooking(var/datum/cooking_item/CI)
|
||||
..()
|
||||
stat |= POWEROFF
|
||||
playsound(src, 'sound/machines/click.ogg', 40, 1)
|
||||
use_power = 0
|
||||
CI.reset()
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/appliance/mixer/update_icon()
|
||||
if (!stat)
|
||||
icon_state = on_icon
|
||||
else
|
||||
icon_state = off_icon
|
||||
|
||||
|
||||
/obj/machinery/appliance/mixer/process()
|
||||
if (!stat)
|
||||
for (var/i in cooking_objs)
|
||||
do_cooking_tick(i)
|
||||
@@ -1,10 +1,12 @@
|
||||
/obj/machinery/cooker/candy
|
||||
/obj/machinery/appliance/mixer/candy
|
||||
name = "candy machine"
|
||||
desc = "Get yer candied cheese wheels here!"
|
||||
icon_state = "mixer_off"
|
||||
off_icon = "mixer_off"
|
||||
on_icon = "mixer_on"
|
||||
cook_type = "candied"
|
||||
appliancetype = CANDYMAKER
|
||||
cooking_power = 0.6
|
||||
cooked_sound = 'sound/machines/ding.ogg'
|
||||
|
||||
output_options = list(
|
||||
@@ -14,6 +16,6 @@
|
||||
"Jelly" = /obj/item/weapon/reagent_containers/food/snacks/variable/jelly
|
||||
)
|
||||
|
||||
/obj/machinery/cooker/candy/change_product_appearance(var/obj/item/weapon/reagent_containers/food/snacks/cooked/product)
|
||||
/obj/machinery/appliance/mixer/candy/change_product_appearance(var/obj/item/weapon/reagent_containers/food/snacks/cooked/product)
|
||||
food_color = get_random_colour(1)
|
||||
. = ..()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/obj/machinery/cooker/cereal
|
||||
/obj/machinery/appliance/mixer/cereal
|
||||
name = "cereal maker"
|
||||
desc = "Now with Dann O's available!"
|
||||
icon = 'icons/obj/cooking_machines.dmi'
|
||||
@@ -7,20 +7,57 @@
|
||||
on_icon = "cereal_on"
|
||||
off_icon = "cereal_off"
|
||||
cooked_sound = 'sound/machines/ding.ogg'
|
||||
appliancetype = CEREALMAKER
|
||||
|
||||
/obj/machinery/cooker/cereal/change_product_strings(var/obj/item/weapon/reagent_containers/food/snacks/product)
|
||||
output_options = list(
|
||||
"Cereal" = /obj/item/weapon/reagent_containers/food/snacks/variable/cereal
|
||||
)
|
||||
|
||||
/*
|
||||
/obj/machinery/appliance/mixer/cereal/change_product_strings(var/obj/item/weapon/reagent_containers/food/snacks/product, var/datum/cooking_item/CI)
|
||||
. = ..()
|
||||
product.name = "box of [cooking_obj.name] cereal"
|
||||
product.name = "box of [CI.object.name] cereal"
|
||||
|
||||
/obj/machinery/cooker/cereal/change_product_appearance(var/obj/item/weapon/reagent_containers/food/snacks/product)
|
||||
/obj/machinery/appliance/mixer/cereal/change_product_appearance(var/obj/item/weapon/reagent_containers/food/snacks/product)
|
||||
product.icon = 'icons/obj/food.dmi'
|
||||
product.icon_state = "cereal_box"
|
||||
product.filling_color = cooking_obj.color
|
||||
product.filling_color = CI.object.color
|
||||
|
||||
var/image/food_image = image(cooking_obj.icon, cooking_obj.icon_state)
|
||||
food_image.color = cooking_obj.color
|
||||
food_image.overlays += cooking_obj.overlays
|
||||
var/image/food_image = image(CI.object.icon, CI.object.icon_state)
|
||||
food_image.color = CI.object.color
|
||||
food_image.overlays += CI.object.overlays
|
||||
food_image.transform *= 0.7
|
||||
|
||||
product.overlays += food_image
|
||||
*/
|
||||
|
||||
/obj/machinery/appliance/mixer/cereal/combination_cook(var/datum/cooking_item/CI)
|
||||
|
||||
var/list/images = list()
|
||||
var/num = 0
|
||||
for (var/obj/item/I in CI.container).
|
||||
if (istype(I, /obj/item/weapon/reagent_containers/food/snacks/variable/cereal))
|
||||
//Images of cereal boxes on cereal boxes is dumb
|
||||
continue
|
||||
|
||||
var/image/food_image = image(I.icon, I.icon_state)
|
||||
food_image.color = I.color
|
||||
food_image.add_overlay(I.overlays)
|
||||
food_image.transform *= 0.7 - (num * 0.05)
|
||||
food_image.pixel_x = rand(-2,2)
|
||||
food_image.pixel_y = rand(-3,5)
|
||||
|
||||
|
||||
if (!images[I.icon_state])
|
||||
images[I.icon_state] = food_image
|
||||
num++
|
||||
|
||||
if (num > 3)
|
||||
continue
|
||||
|
||||
|
||||
var/obj/item/weapon/reagent_containers/food/snacks/result = ..()
|
||||
|
||||
result.color = result.filling_color
|
||||
for (var/i in images)
|
||||
result.overlays += images[i]
|
||||
|
||||
157
code/modules/food/kitchen/cooking_machines/container.dm
Normal file
157
code/modules/food/kitchen/cooking_machines/container.dm
Normal file
@@ -0,0 +1,157 @@
|
||||
//Cooking containers are used in ovens and fryers, to hold multiple ingredients for a recipe.
|
||||
//They work fairly similar to the microwave - acting as a container for objects and reagents,
|
||||
//which can be checked against recipe requirements in order to cook recipes that require several things
|
||||
|
||||
/obj/item/weapon/reagent_containers/cooking_container
|
||||
icon = 'modular_citadel/icons/obj/cooking_machines.dmi'
|
||||
var/shortname
|
||||
var/max_space = 20//Maximum sum of w-classes of foods in this container at once
|
||||
var/max_reagents = 80//Maximum units of reagents
|
||||
flags = OPENCONTAINER | NOREACT
|
||||
var/list/insertable = list(
|
||||
/obj/item/weapon/reagent_containers/food/snacks,
|
||||
/obj/item/weapon/holder,
|
||||
/obj/item/weapon/paper
|
||||
)
|
||||
|
||||
/obj/item/weapon/reagent_containers/cooking_container/initialize()
|
||||
. = ..()
|
||||
create_reagents(max_reagents)
|
||||
flags |= OPENCONTAINER | NOREACT
|
||||
|
||||
|
||||
/obj/item/weapon/reagent_containers/cooking_container/examine(var/mob/user)
|
||||
..()
|
||||
if (contents.len)
|
||||
var/string = "It contains....</br>"
|
||||
for (var/atom/movable/A in contents)
|
||||
string += "[A.name] </br>"
|
||||
user << span("notice", string)
|
||||
if (reagents.total_volume)
|
||||
user << span("notice", "It contains [reagents.total_volume]u of reagents.")
|
||||
|
||||
|
||||
/obj/item/weapon/reagent_containers/cooking_container/attackby(var/obj/item/I as obj, var/mob/user as mob)
|
||||
for (var/possible_type in insertable)
|
||||
if (istype(I, possible_type))
|
||||
if (!can_fit(I))
|
||||
to_chat(user, "<span class='warning'>There's no more space in the [src] for that!</span>")
|
||||
return 0
|
||||
|
||||
if(!user.unEquip(I))
|
||||
return
|
||||
I.forceMove(src)
|
||||
to_chat(user, "<span class='notice'>You put the [I] into the [src]</span>")
|
||||
return
|
||||
|
||||
/obj/item/weapon/reagent_containers/cooking_container/verb/empty()
|
||||
set src in oview(1)
|
||||
set name = "Empty Container"
|
||||
set category = "Object"
|
||||
set desc = "Removes items from the container, excluding reagents."
|
||||
|
||||
do_empty(usr)
|
||||
|
||||
/obj/item/weapon/reagent_containers/cooking_container/proc/do_empty(mob/user)
|
||||
if (!isliving(user))
|
||||
//Here we only check for ghosts. Animals are intentionally allowed to remove things from oven trays so they can eat it
|
||||
return
|
||||
|
||||
if (user.stat || user.restrained())
|
||||
to_chat(user, "<span class='notice'>You are in no fit state to do this.</span>")
|
||||
return
|
||||
|
||||
if (!Adjacent(user))
|
||||
to_chat(user, "You can't reach [src] from here.")
|
||||
return
|
||||
|
||||
if (!contents.len)
|
||||
to_chat(user, "<span class='warning'>There's nothing in the [src] you can remove!</span>")
|
||||
return
|
||||
|
||||
for (var/atom/movable/A in contents)
|
||||
A.forceMove(get_turf(src))
|
||||
|
||||
to_chat(user, "<span class='notice'>You remove all the solid items from the [src].</span>")
|
||||
|
||||
/obj/item/weapon/reagent_containers/cooking_container/proc/check_contents()
|
||||
if (contents.len == 0)
|
||||
if (!reagents || reagents.total_volume == 0)
|
||||
return 0//Completely empty
|
||||
else if (contents.len == 1)
|
||||
if (!reagents || reagents.total_volume == 0)
|
||||
return 1//Contains only a single object which can be extracted alone
|
||||
return 2//Contains multiple objects and/or reagents
|
||||
|
||||
/obj/item/weapon/reagent_containers/cooking_container/AltClick(var/mob/user)
|
||||
do_empty(user)
|
||||
|
||||
//Deletes contents of container.
|
||||
//Used when food is burned, before replacing it with a burned mess
|
||||
/obj/item/weapon/reagent_containers/cooking_container/proc/clear()
|
||||
for (var/atom/a in contents)
|
||||
qdel(a)
|
||||
|
||||
if (reagents)
|
||||
reagents.clear_reagents()
|
||||
|
||||
/obj/item/weapon/reagent_containers/cooking_container/proc/label(var/number, var/CT = null)
|
||||
//This returns something like "Fryer basket 1 - empty"
|
||||
//The latter part is a brief reminder of contents
|
||||
//This is used in the removal menu
|
||||
. = shortname
|
||||
if (!isnull(number))
|
||||
.+= " [number]"
|
||||
.+= " - "
|
||||
if (CT)
|
||||
.+=CT
|
||||
else if (contents.len)
|
||||
for (var/obj/O in contents)
|
||||
.+=O.name//Just append the name of the first object
|
||||
return
|
||||
else if (reagents && reagents.total_volume > 0)
|
||||
var/datum/reagent/R = reagents.get_master_reagent()
|
||||
.+=R.name//Append name of most voluminous reagent
|
||||
return
|
||||
else
|
||||
. += "empty"
|
||||
|
||||
|
||||
/obj/item/weapon/reagent_containers/cooking_container/proc/can_fit(var/obj/item/I)
|
||||
var/total = 0
|
||||
for (var/obj/item/J in contents)
|
||||
total += J.w_class
|
||||
|
||||
if((max_space - total) >= I.w_class)
|
||||
return 1
|
||||
|
||||
|
||||
//Takes a reagent holder as input and distributes its contents among the items in the container
|
||||
//Distribution is weighted based on the volume already present in each item
|
||||
/obj/item/weapon/reagent_containers/cooking_container/proc/soak_reagent(var/datum/reagents/holder)
|
||||
var/total = 0
|
||||
var/list/weights = list()
|
||||
for (var/obj/item/I in contents)
|
||||
if (I.reagents && I.reagents.total_volume)
|
||||
total += I.reagents.total_volume
|
||||
weights[I] = I.reagents.total_volume
|
||||
|
||||
if (total > 0)
|
||||
for (var/obj/item/I in contents)
|
||||
if (weights[I])
|
||||
holder.trans_to(I, weights[I] / total)
|
||||
|
||||
|
||||
/obj/item/weapon/reagent_containers/cooking_container/oven
|
||||
name = "oven dish"
|
||||
shortname = "shelf"
|
||||
desc = "Put ingredients in this; designed for use with an oven. Warranty void if used."
|
||||
icon_state = "ovendish"
|
||||
max_space = 30
|
||||
max_reagents = 120
|
||||
|
||||
/obj/item/weapon/reagent_containers/cooking_container/fryer
|
||||
name = "fryer basket"
|
||||
shortname = "basket"
|
||||
desc = "Put ingredients in this; designed for use with a deep fryer. Warranty void if used."
|
||||
icon_state = "basket"
|
||||
@@ -1,4 +1,4 @@
|
||||
/obj/machinery/cooker/fryer
|
||||
/obj/machinery/appliance/cooker/fryer
|
||||
name = "deep fryer"
|
||||
desc = "Deep fried <i>everything</i>."
|
||||
icon_state = "fryer_off"
|
||||
@@ -9,30 +9,157 @@
|
||||
food_color = "#FFAD33"
|
||||
cooked_sound = 'sound/machines/ding.ogg'
|
||||
var/datum/looping_sound/deep_fryer/fry_loop
|
||||
appliancetype = FRYER
|
||||
active_power_usage = 12 KILOWATTS
|
||||
|
||||
optimal_power = 0.35
|
||||
|
||||
idle_power_usage = 3.6 KILOWATTS
|
||||
// Power used to maintain temperature once it's heated.
|
||||
// Going with 25% of the active power. This is a somewhat arbitrary value.
|
||||
|
||||
/obj/machinery/cooker/fryer/Initialize()
|
||||
resistance = 20000 // Approx. 8-9 minutes to heat up.
|
||||
|
||||
max_contents = 2
|
||||
container_type = /obj/item/weapon/reagent_containers/cooking_container/fryer
|
||||
|
||||
stat = POWEROFF // Starts turned off
|
||||
|
||||
var/datum/reagents/oil
|
||||
var/optimal_oil = 9000 //90 litres of cooking oil
|
||||
|
||||
/obj/machinery/appliance/cooker/fryer/Initialize()
|
||||
. = ..()
|
||||
fry_loop = new(list(src), FALSE)
|
||||
return ..()
|
||||
|
||||
oil = new/datum/reagents(optimal_oil * 1.25, src)
|
||||
var/variance = rand()*0.15
|
||||
// Fryer is always a little below full, but its usually negligible
|
||||
|
||||
/obj/machinery/cooker/fryer/Destroy()
|
||||
if(prob(20))
|
||||
// Sometimes the fryer will start with much less than full oil, significantly impacting efficiency until filled
|
||||
variance = rand()*0.5
|
||||
oil.add_reagent("cornoil", optimal_oil*(1 - variance))
|
||||
|
||||
/obj/machinery/appliance/cooker/fryer/Destroy()
|
||||
QDEL_NULL(fry_loop)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/appliance/cooker/fryer/examine(var/mob/user)
|
||||
if (..())//no need to duplicate adjacency check
|
||||
to_chat(user, "Oil Level: [oil.total_volume]/[optimal_oil]")
|
||||
|
||||
/obj/machinery/appliance/cooker/fryer/heat_up()
|
||||
if (..())
|
||||
//Set temperature of oil reagent
|
||||
var/datum/reagent/nutriment/triglyceride/oil/OL = oil.get_master_reagent()
|
||||
if (OL && istype(OL))
|
||||
OL.data["temperature"] = temperature
|
||||
|
||||
/obj/machinery/cooker/fryer/set_cooking(new_setting)
|
||||
/obj/machinery/appliance/cooker/fryer/equalize_temperature()
|
||||
if (..())
|
||||
//Set temperature of oil reagent
|
||||
var/datum/reagent/nutriment/triglyceride/oil/OL = oil.get_master_reagent()
|
||||
if (OL && istype(OL))
|
||||
OL.data["temperature"] = temperature
|
||||
|
||||
/obj/machinery/appliance/cooker/fryer/set_cooking(new_setting)
|
||||
..()
|
||||
if(new_setting)
|
||||
fry_loop.start()
|
||||
else
|
||||
fry_loop.stop()
|
||||
|
||||
/obj/machinery/appliance/cooker/fryer/update_cooking_power()
|
||||
..()//In addition to parent temperature calculation
|
||||
//Fryer efficiency also drops when oil levels arent optimal
|
||||
var/oil_level = 0
|
||||
var/datum/reagent/nutriment/triglyceride/oil/OL = oil.get_master_reagent()
|
||||
if(OL && istype(OL))
|
||||
oil_level = OL.volume
|
||||
|
||||
/obj/machinery/cooker/fryer/cook_mob(var/mob/living/victim, var/mob/user)
|
||||
var/oil_efficiency = 0
|
||||
if(oil_level)
|
||||
oil_efficiency = oil_level / optimal_oil
|
||||
|
||||
if(oil_efficiency > 1)
|
||||
//We're above optimal, efficiency goes down as we pass too much over it
|
||||
oil_efficiency = 1 - (oil_efficiency - 1)
|
||||
|
||||
|
||||
cooking_power *= oil_efficiency
|
||||
|
||||
/obj/machinery/appliance/cooker/fryer/update_icon()
|
||||
if(cooking)
|
||||
icon_state = on_icon
|
||||
else
|
||||
icon_state = off_icon
|
||||
..()
|
||||
|
||||
//Fryer gradually infuses any cooked food with oil. Moar calories
|
||||
//This causes a slow drop in oil levels, encouraging refill after extended use
|
||||
/obj/machinery/appliance/cooker/fryer/do_cooking_tick(var/datum/cooking_item/CI)
|
||||
if(..() && (CI.oil < CI.max_oil) && prob(20))
|
||||
var/datum/reagents/buffer = new /datum/reagents(2)
|
||||
oil.trans_to_holder(buffer, min(0.5, CI.max_oil - CI.oil))
|
||||
CI.oil += buffer.total_volume
|
||||
CI.container.soak_reagent(buffer)
|
||||
|
||||
|
||||
//To solve any odd logic problems with results having oil as part of their compiletime ingredients.
|
||||
//Upon finishing a recipe the fryer will analyse any oils in the result, and replace them with our oil
|
||||
//As well as capping the total to the max oil
|
||||
/obj/machinery/appliance/cooker/fryer/finish_cooking(var/datum/cooking_item/CI)
|
||||
..()
|
||||
var/total_oil = 0
|
||||
var/total_our_oil = 0
|
||||
var/total_removed = 0
|
||||
var/datum/reagent/our_oil = oil.get_master_reagent()
|
||||
|
||||
for (var/obj/item/I in CI.container)
|
||||
if (I.reagents && I.reagents.total_volume)
|
||||
for (var/datum/reagent/R in I.reagents.reagent_list)
|
||||
if (istype(R, /datum/reagent/nutriment/triglyceride/oil))
|
||||
total_oil += R.volume
|
||||
if (R.id != our_oil.id)
|
||||
total_removed += R.volume
|
||||
I.reagents.remove_reagent(R.id, R.volume)
|
||||
else
|
||||
total_our_oil += R.volume
|
||||
|
||||
|
||||
if (total_removed > 0 || total_oil != CI.max_oil)
|
||||
total_oil = min(total_oil, CI.max_oil)
|
||||
|
||||
if (total_our_oil < total_oil)
|
||||
//If we have less than the combined total, then top up from our reservoir
|
||||
var/datum/reagents/buffer = new /datum/reagents(INFINITY)
|
||||
oil.trans_to_holder(buffer, total_oil - total_our_oil)
|
||||
CI.container.soak_reagent(buffer)
|
||||
else if (total_our_oil > total_oil)
|
||||
|
||||
//If we have more than the maximum allowed then we delete some.
|
||||
//This could only happen if one of the objects spawns with the same type of oil as ours
|
||||
var/portion = 1 - (total_oil / total_our_oil) //find the percentage to remove
|
||||
for (var/obj/item/I in CI.container)
|
||||
if (I.reagents && I.reagents.total_volume)
|
||||
for (var/datum/reagent/R in I.reagents.reagent_list)
|
||||
if (R.id == our_oil.id)
|
||||
I.reagents.remove_reagent(R.id, R.volume*portion)
|
||||
|
||||
/obj/machinery/appliance/cooker/fryer/cook_mob(var/mob/living/victim, var/mob/user)
|
||||
|
||||
if(!istype(victim))
|
||||
return
|
||||
|
||||
user.visible_message("<span class='danger'>\The [user] starts pushing \the [victim] into \the [src]!</span>")
|
||||
icon_state = on_icon
|
||||
cooking = 1
|
||||
// user.visible_message("<span class='danger'>\The [user] starts pushing \the [victim] into \the [src]!</span>")
|
||||
|
||||
//Removed delay on this action in favour of a cooldown after it
|
||||
//If you can lure someone close to the fryer and grab them then you deserve success.
|
||||
//And a delay on this kind of niche action just ensures it never happens
|
||||
//Cooldown ensures it can't be spammed to instakill someone
|
||||
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN*3)
|
||||
|
||||
fry_loop.start()
|
||||
|
||||
if(!do_mob(user, victim, 20))
|
||||
@@ -48,38 +175,71 @@
|
||||
fry_loop.stop()
|
||||
return
|
||||
|
||||
var/damage = rand(7,13) // Though this damage seems reduced, some hot oil is transferred to the victim and will burn them for a while after
|
||||
|
||||
var/datum/reagent/nutriment/triglyceride/oil/OL = oil.get_master_reagent()
|
||||
damage *= OL.heatdamage(victim)
|
||||
|
||||
var/obj/item/organ/external/E
|
||||
var/nopain
|
||||
if(ishuman(victim) && user.zone_sel.selecting != "groin" && user.zone_sel.selecting != "chest")
|
||||
var/mob/living/carbon/human/H = victim
|
||||
if(H.species.flags & NO_PAIN)
|
||||
nopain = 2
|
||||
E = H.get_organ(user.zone_sel.selecting)
|
||||
if(E.robotic >= ORGAN_ROBOT)
|
||||
if(!E || E.species.flags & NO_PAIN)
|
||||
nopain = 2
|
||||
else if(E.robotic >= ORGAN_ROBOT)
|
||||
nopain = 1
|
||||
|
||||
user.visible_message("<span class='danger'>\The [user] shoves \the [victim][E ? "'s [E.name]" : ""] into \the [src]!</span>")
|
||||
if (damage > 0)
|
||||
if(E)
|
||||
if(E.children && E.children.len)
|
||||
for(var/obj/item/organ/external/child in E.children)
|
||||
if(nopain && nopain < 2 && !(child.robotic >= ORGAN_ROBOT))
|
||||
nopain = 0
|
||||
child.take_damage(0, damage)
|
||||
damage -= (damage*0.5)//IF someone's arm is plunged in, the hand should take most of it
|
||||
E.take_damage(0, damage)
|
||||
else
|
||||
victim.apply_damage(damage, BURN, user.zone_sel.selecting)
|
||||
|
||||
if(E)
|
||||
E.take_damage(0, rand(20,30))
|
||||
if(E.children && E.children.len)
|
||||
for(var/obj/item/organ/external/child in E.children)
|
||||
if(nopain && nopain < 2 && !(child.robotic >= ORGAN_ROBOT))
|
||||
nopain = 0
|
||||
child.take_damage(0, rand(20,30))
|
||||
else
|
||||
victim.apply_damage(rand(30,40), BURN, user.zone_sel.selecting)
|
||||
if(!nopain)
|
||||
victim << "<span class='danger'>Agony consumes you as searing hot oil scorches your [E ? E.name : "flesh"] horribly!</span>"
|
||||
victim.emote("scream")
|
||||
else
|
||||
victim << "<span class='danger'>Searing hot oil scorches your [E ? E.name : "flesh"]!</span>"
|
||||
|
||||
if(!nopain)
|
||||
to_chat(victim, "<span class='danger'>Agony consumes you as searing hot oil scorches your [E ? E.name : "flesh"] horribly!</span>")
|
||||
victim.emote("scream")
|
||||
else
|
||||
to_chat(victim, "<span class='danger'>Searing hot oil scorches your [E ? E.name : "flesh"]!</span>")
|
||||
user.attack_log += text("\[[time_stamp()]\] <font color='red'>Has [cook_type] \the [victim] ([victim.ckey]) in \a [src]</font>")
|
||||
victim.attack_log += text("\[[time_stamp()]\] <font color='orange'>Has been [cook_type] in \a [src] by [user.name] ([user.ckey])</font>")
|
||||
msg_admin_attack("[key_name_admin(user)] [cook_type] \the [victim] ([victim.ckey]) in \a [src]. (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[user.x];Y=[user.y];Z=[user.z]'>JMP</a>)",ckey=key_name(user),ckey_target=key_name(victim))
|
||||
|
||||
if(victim.client)
|
||||
add_attack_logs(user,victim,"[cook_type] in [src]")
|
||||
|
||||
icon_state = off_icon
|
||||
cooking = 0
|
||||
//Coat the victim in some oil
|
||||
oil.trans_to(victim, 40)
|
||||
|
||||
fry_loop.stop()
|
||||
return
|
||||
|
||||
/obj/machinery/appliance/cooker/fryer/attackby(var/obj/item/I, var/mob/user)
|
||||
if(istype(I, /obj/item/weapon/reagent_containers/glass) && I.reagents)
|
||||
if (I.reagents.total_volume <= 0 && oil)
|
||||
//Its empty, handle scooping some hot oil out of the fryer
|
||||
oil.trans_to(I, I.reagents.maximum_volume)
|
||||
user.visible_message("[user] scoops some oil out of \the [src].", span("notice","You scoop some oil out of \the [src]."))
|
||||
return 1
|
||||
else
|
||||
//It contains stuff, handle pouring any oil into the fryer
|
||||
//Possibly in future allow pouring non-oil reagents in, in order to sabotage it and poison food.
|
||||
//That would really require coding some sort of filter or better replacement mechanism first
|
||||
//So for now, restrict to oil only
|
||||
var/amount = 0
|
||||
for (var/datum/reagent/R in I.reagents.reagent_list)
|
||||
if (istype(R, /datum/reagent/nutriment/triglyceride/oil))
|
||||
var/delta = oil.get_free_space()
|
||||
delta = min(delta, R.volume)
|
||||
oil.add_reagent(R.id, delta)
|
||||
I.reagents.remove_reagent(R.id, delta)
|
||||
amount += delta
|
||||
if (amount > 0)
|
||||
user.visible_message("[user] pours some oil into \the [src].", span("notice","You pour [amount]u of oil into \the [src]."), "<span class='notice'>You hear something viscous being poured into a metal container.</span>")
|
||||
return 1
|
||||
//If neither of the above returned, then call parent as normal
|
||||
..()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/obj/machinery/cooker/grill
|
||||
/obj/machinery/appliance/cooker/grill
|
||||
name = "grill"
|
||||
desc = "Backyard grilling, IN SPACE."
|
||||
icon_state = "grill_off"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/obj/machinery/cooker/oven
|
||||
/obj/machinery/appliance/cooker/oven
|
||||
name = "oven"
|
||||
desc = "Cookies are ready, dear."
|
||||
icon = 'icons/obj/cooking_machines.dmi'
|
||||
|
||||
Reference in New Issue
Block a user