diff --git a/code/__DEFINES/tools.dm b/code/__DEFINES/tools.dm index 11ce83ecfb0b..320648170b44 100644 --- a/code/__DEFINES/tools.dm +++ b/code/__DEFINES/tools.dm @@ -18,3 +18,8 @@ // If delay between the start and the end of tool operation is less than MIN_TOOL_SOUND_DELAY, // tool sound is only played when op is started. If not, it's played twice. #define MIN_TOOL_SOUND_DELAY 20 + +/// When a tooltype_act proc is successful +#define TOOL_ACT_TOOLTYPE_SUCCESS (1<<0) +/// When [COMSIG_ATOM_TOOL_ACT] blocks the act +#define TOOL_ACT_SIGNAL_BLOCKING (1<<1) diff --git a/code/game/objects/items/circuitboards/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machine_circuitboards.dm index e15f95a40709..faa366f893b0 100644 --- a/code/game/objects/items/circuitboards/machine_circuitboards.dm +++ b/code/game/objects/items/circuitboards/machine_circuitboards.dm @@ -1495,3 +1495,23 @@ /obj/item/stock_parts/cell = 3, /obj/item/stock_parts/capacitor = 2 ) + +/obj/item/circuitboard/machine/coffeemaker + name = "Modello 3 Coffeemaker" + build_path = /obj/machinery/coffeemaker + req_components = list( + /obj/item/stack/sheet/glass = 1, + /obj/item/reagent_containers/glass/beaker = 2, + /obj/item/stock_parts/capacitor = 1, + /obj/item/stock_parts/micro_laser = 2, + ) + +/obj/item/circuitboard/machine/coffeemaker/impressa + name = "Impressa Coffeemaker" + build_path = /obj/machinery/coffeemaker/impressa + req_components = list( + /obj/item/stack/sheet/glass = 1, + /obj/item/reagent_containers/glass/beaker = 2, + /obj/item/stock_parts/capacitor = 1, + /obj/item/stock_parts/micro_laser = 2, + ) diff --git a/code/game/objects/items/storage/boxes.dm b/code/game/objects/items/storage/boxes.dm index 9e8d4e94745c..7b1710cd8960 100644 --- a/code/game/objects/items/storage/boxes.dm +++ b/code/game/objects/items/storage/boxes.dm @@ -1542,3 +1542,54 @@ /obj/item/stack/wrapping_paper/small=1 ) generate_items_inside(items_inside,src) + +/obj/item/storage/box/coffeepack + icon_state = "arabica_beans" + name = "arabica beans" + desc = "A bag containing fresh, dry coffee arabica beans. Ethically sourced and packaged by Donk! Co." + illustration = null + icon = 'icons/obj/food/containers.dmi' + var/beantype = /obj/item/reagent_containers/food/snacks/grown/coffee + +/obj/item/storage/box/cofeepack/Initialize(mapload) + . = ..() + var/datum/component/storage/STR = GetComponent(/datum/component/storage) + STR.max_items = 5 + STR.set_holdable(list(/obj/item/reagent_containers/food/snacks/grown/coffee)) + +/obj/item/storage/box/coffeepack/PopulateContents() + var/static/items_inside = list( + /obj/item/reagent_containers/food/snacks/grown/coffee = 5, + /obj/item/reagent_containers/food/snacks/grown/coffee/robusta = 5) + generate_items_inside(items_inside,src) + +/obj/item/storage/box/coffeepack/robusta + icon_state = "robusta_beans" + name = "robusta beans" + desc = "A bag containing fresh, dry coffee robusta beans. Ethically sourced and packaged by Donk! Co." + beantype = /obj/item/reagent_containers/food/snacks/grown/coffee/robusta + + +/* + * Coffee condiments display -- someone can make this fancy eventually, i cant fucking figure it out for the life of me -- it exists in TG + */ + +/obj/item/storage/box/coffee_condi_display + name = "coffee condiments display" + desc = "A neat small box, holding all your favorite coffee condiments." + +/obj/item/storage/box/coffee_condi_display/Initialize(mapload) + . = ..() + var/datum/component/storage/STR = GetComponent(/datum/component/storage) + STR.max_items = 14 + STR.set_holdable(list( + /obj/item/reagent_containers/food/condiment/pack/sugar, + /obj/item/reagent_containers/food/condiment/pack/creamer, + /obj/item/reagent_containers/food/condiment/pack/astrotame, + )) + +/obj/item/storage/box/coffee_condi_display/PopulateContents() + for(var/i in 1 to 4) + new /obj/item/reagent_containers/food/condiment/pack/sugar(src) + new /obj/item/reagent_containers/food/condiment/pack/creamer(src) + new /obj/item/reagent_containers/food/condiment/pack/astrotame(src) diff --git a/code/modules/cargo/packs/food.dm b/code/modules/cargo/packs/food.dm index 86e6f293908d..398233a64488 100644 --- a/code/modules/cargo/packs/food.dm +++ b/code/modules/cargo/packs/food.dm @@ -258,3 +258,41 @@ /obj/effect/spawner/lootdrop/ration) crate_name = "ration crate" crate_type = /obj/structure/closet/crate + +/datum/supply_pack/food/syrup + name = "Coffee Syrups Box" + desc = "A packaged box of various syrups, perfect for making your delicious coffee even more diabetic." + cost = 200 + contains = list( + /obj/item/reagent_containers/food/drinks/bottle/syrup_bottle/caramel, + /obj/item/reagent_containers/food/drinks/bottle/syrup_bottle/liqueur, + ) + crate_name = "coffee syrups box" + crate_type = /obj/structure/closet/crate + +/datum/supply_pack/food/coffeekit + name = "Coffee Equipment Crate" + desc = "A complete kit to setup your own cozy coffee shop, the coffeemaker is for some reason not included." + cost = 1000 + contains = list( + /obj/item/storage/box/coffeepack/robusta, + /obj/item/storage/box/coffeepack, + /obj/item/reagent_containers/food/drinks/bottle/coffeepot, + /obj/item/storage/box/coffee_condi_display, + /obj/item/reagent_containers/food/condiment/milk, + /obj/item/reagent_containers/food/condiment/soymilk, + /obj/item/reagent_containers/food/condiment/sugar, + /obj/item/reagent_containers/food/drinks/bottle/syrup_bottle/caramel, //one extra syrup as a treat + ) + crate_name = "coffee equipment crate" + +/datum/supply_pack/food/coffeemaker + name = "Impressa Coffeemaker Crate" + desc = "An assembled Impressa model coffeemaker." + cost = 500 + contains = list( + /obj/machinery/coffeemaker/impressa, + /obj/item/reagent_containers/food/drinks/bottle/coffeepot, + ) + crate_name = "coffeemaker crate" + crate_type = /obj/structure/closet/crate diff --git a/code/modules/cargo/packs/machinery.dm b/code/modules/cargo/packs/machinery.dm index 20f0af7a03a3..510ec7841417 100644 --- a/code/modules/cargo/packs/machinery.dm +++ b/code/modules/cargo/packs/machinery.dm @@ -326,3 +326,4 @@ contains = list(/obj/machinery/the_singularitygen/tesla) crate_name = "tesla generator crate" crate_type = /obj/structure/closet/crate/secure/engineering + diff --git a/code/modules/food_and_drinks/food/condiment.dm b/code/modules/food_and_drinks/food/condiment.dm index 2baf670d807d..589e986aaeeb 100644 --- a/code/modules/food_and_drinks/food/condiment.dm +++ b/code/modules/food_and_drinks/food/condiment.dm @@ -323,3 +323,13 @@ icon_state = "oliveoil" list_reagents = list(/datum/reagent/consumable/cornoil = 50) +/obj/item/reagent_containers/food/condiment/pack/sugar + name = "sugar pack" + originalname = "sugar" + list_reagents = list(/datum/reagent/consumable/sugar = 5) + +/obj/item/reagent_containers/food/condiment/pack/creamer + name = "creamer" /// dont laugh you child + originalname = "cream" + list_reagents = list(/datum/reagent/consumable/cream = 5) + diff --git a/code/modules/food_and_drinks/kitchen_machinery/coffeemaker.dm b/code/modules/food_and_drinks/kitchen_machinery/coffeemaker.dm new file mode 100644 index 000000000000..7b98be6a16c4 --- /dev/null +++ b/code/modules/food_and_drinks/kitchen_machinery/coffeemaker.dm @@ -0,0 +1,689 @@ +#define BEAN_CAPACITY 10 //amount of coffee beans that can fit inside the impressa coffeemaker + +/obj/machinery/coffeemaker + name = "coffeemaker" + desc = "A Modello 3 Coffeemaker that brews coffee and holds it at the perfect temperature of 176 fahrenheit. Made by Piccionaia Home Appliances." + icon = 'icons/obj/machines/coffeemaker.dmi' + icon_state = "coffeemaker_nopot_nocart" + base_icon_state = "coffeemaker" + resistance_flags = FIRE_PROOF | ACID_PROOF + circuit = /obj/item/circuitboard/machine/coffeemaker + var/obj/item/reagent_containers/food/drinks/bottle/coffeepot/coffeepot = null + var/brewing = FALSE + var/brew_time = 20 SECONDS + var/speed = 1 + /// The coffee cartridge to make coffee from. In the future, coffee grounds are like printer ink. + var/obj/item/coffee_cartridge/cartridge = null + /// The type path to instantiate for the coffee cartridge the device initially comes with, eg. /obj/item/coffee_cartridge + var/initial_cartridge = /obj/item/coffee_cartridge + /// The number of cups left + var/coffee_cups = 15 + var/max_coffee_cups = 15 + /// The amount of sugar packets left + var/sugar_packs = 10 + var/max_sugar_packs = 10 + /// The amount of sweetener packets left + var/sweetener_packs = 10 + var/max_sweetener_packs = 10 + /// The amount of creamer packets left + var/creamer_packs = 10 + var/max_creamer_packs = 10 + + var/static/radial_examine = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_examine") + var/static/radial_brew = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_brew") + var/static/radial_eject_pot = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_eject_pot") + var/static/radial_eject_cartridge = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_eject_cartridge") + var/static/radial_take_cup = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_take_cup") + var/static/radial_take_sugar = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_take_sugar") + var/static/radial_take_sweetener = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_take_sweetener") + var/static/radial_take_creamer = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_take_creamer") + +/obj/machinery/coffeemaker/Initialize(mapload) + . = ..() + if(mapload) + coffeepot = new /obj/item/reagent_containers/food/drinks/bottle/coffeepot(src) + cartridge = new /obj/item/coffee_cartridge(src) + +/obj/machinery/coffeemaker/deconstruct() + coffeepot?.forceMove(drop_location()) + cartridge?.forceMove(drop_location()) + return ..() + +/obj/machinery/coffeemaker/Destroy() + QDEL_NULL(coffeepot) + QDEL_NULL(cartridge) + return ..() + +/obj/machinery/coffeemaker/Exited(atom/movable/gone, direction) + . = ..() + if(gone == coffeepot) + coffeepot = null + update_appearance(UPDATE_OVERLAYS) + if(gone == cartridge) + cartridge = null + update_appearance(UPDATE_OVERLAYS) +/obj/machinery/coffeemaker/examine(mob/user) + . = ..() + if(!in_range(user, src) && !issilicon(user) && !isobserver(user)) + . += span_warning("You're too far away to examine [src]'s contents and display!") + return + + if(brewing) + . += span_warning("\The [src] is brewing.") + return + + if(panel_open) + . += span_notice("[src]'s maintenance hatch is open!") + return + + if(coffeepot || cartridge) + . += span_notice("\The [src] contains:") + if(coffeepot) + . += span_notice("- \A [coffeepot].") + if(cartridge) + . += span_notice("- \A [cartridge].") + return + + if(!(machine_stat & (NOPOWER|BROKEN))) + . += "[span_notice("The status display reads:")]\n"+\ + span_notice("- Brewing coffee at [speed*100]%.") + if(coffeepot) + for(var/datum/reagent/consumable/cawfee as anything in coffeepot.reagents.reagent_list) + . += span_notice("- [cawfee.volume] units of coffee in pot.") + if(cartridge) + if(cartridge.charges < 1) + . += span_notice("- grounds cartridge is empty.") + else + . += span_notice("- grounds cartridge has [cartridge.charges] charges remaining.") + + if (coffee_cups >= 1) + . += span_notice("There [coffee_cups == 1 ? "is" : "are"] [coffee_cups] coffee cup[coffee_cups != 1 && "s"] left.") + else + . += span_notice("There are no cups left.") + + if (sugar_packs >= 1) + . += span_notice("There [sugar_packs == 1 ? "is" : "are"] [sugar_packs] packet[sugar_packs != 1 && "s"] of sugar left.") + else + . += span_notice("There is no sugar left.") + + if (sweetener_packs >= 1) + . += span_notice("There [sweetener_packs == 1 ? "is" : "are"] [sweetener_packs] packet[sweetener_packs != 1 && "s"] of sweetener left.") + else + . += span_notice("There is no sweetener left.") + + if (creamer_packs > 1) + . += span_notice("There [creamer_packs == 1 ? "is" : "are"] [creamer_packs] packet[creamer_packs != 1 && "s"] of creamer left.") + else + . += span_notice("There is no creamer left.") + + +/obj/machinery/coffeemaker/update_overlays() + . = ..() + . += overlay_checks() + +/obj/machinery/coffeemaker/proc/overlay_checks() + . = list() + if(coffeepot) + . += "coffeemaker_pot" + if(cartridge) + . += "coffeemaker_cartidge" + return . + +/obj/machinery/coffeemaker/proc/replace_pot(mob/living/user, /obj/item/reagent_containers/food/drinks/bottle/coffeepot) + if(!user) + return FALSE + if(coffeepot) + try_put_in_hand(coffeepot, user) + balloon_alert(user, "replaced pot") + update_appearance(UPDATE_OVERLAYS) + return TRUE + +/obj/machinery/coffeemaker/proc/replace_cartridge(mob/living/user, obj/item/coffee_cartridge/new_cartridge) + if(!user) + return FALSE + if(cartridge) + try_put_in_hand(cartridge, user) + if(new_cartridge) + cartridge = new_cartridge + update_appearance(UPDATE_OVERLAYS) + return TRUE + +/obj/machinery/coffeemaker/wrench_act(mob/living/user, obj/item/tool) + . = ..() + default_unfasten_wrench(user, tool) + return TOOL_ACT_TOOLTYPE_SUCCESS + +/obj/machinery/coffeemaker/attackby(obj/item/attack_item, mob/living/user, params) + //You can only screw open empty grinder + if(!coffeepot && default_deconstruction_screwdriver(user, icon_state, icon_state, attack_item)) + return FALSE + + if(default_deconstruction_crowbar(attack_item)) + return + + if(panel_open) //Can't insert objects when its screwed open + return TRUE + + if (istype(attack_item, /obj/item/reagent_containers/food/drinks/bottle/coffeepot) && !(attack_item.item_flags & ABSTRACT) && attack_item.is_open_container()) + var/obj/item/reagent_containers/food/drinks/bottle/coffeepot/new_pot = attack_item + . = TRUE //no afterattack + if(!user.transferItemToLoc(new_pot, src)) + return TRUE + replace_pot(user, new_pot) + update_appearance(UPDATE_OVERLAYS) + return TRUE //no afterattack + + if (istype(attack_item, /obj/item/reagent_containers/glass/coffee_cup) && !(attack_item.item_flags & ABSTRACT) && attack_item.is_open_container()) + var/obj/item/reagent_containers/glass/coffee_cup/new_cup = attack_item + if(new_cup.reagents.total_volume > 0) + balloon_alert(user, "the cup must be empty!") + return + if(coffee_cups >= max_coffee_cups) + balloon_alert(user, "the cup holder is full!") + return + if(!user.transferItemToLoc(attack_item, src)) + return + coffee_cups++ + update_appearance(UPDATE_OVERLAYS) + return TRUE //no afterattack + + if (istype(attack_item, /obj/item/reagent_containers/food/condiment/pack/sugar)) + var/obj/item/reagent_containers/food/condiment/pack/sugar/new_pack = attack_item + if(new_pack.reagents.total_volume < new_pack.reagents.maximum_volume) + balloon_alert(user, "the pack must be full!") + return + if(sugar_packs >= max_sugar_packs) + balloon_alert(user, "the sugar compartment is full!") + return + if(!user.transferItemToLoc(attack_item, src)) + return + sugar_packs++ + update_appearance(UPDATE_OVERLAYS) + return TRUE //no afterattack + + if (istype(attack_item, /obj/item/reagent_containers/food/condiment/pack/creamer)) + var/obj/item/reagent_containers/food/condiment/pack/creamer/new_pack = attack_item + if(new_pack.reagents.total_volume < new_pack.reagents.maximum_volume) + balloon_alert(user, "the pack must be full!") + return + if(creamer_packs >= max_creamer_packs) + balloon_alert(user, "the creamer compartment is full!") + return + if(!user.transferItemToLoc(attack_item, src)) + return + creamer_packs++ + update_appearance(UPDATE_OVERLAYS) + return TRUE //no afterattack + + if (istype(attack_item, /obj/item/reagent_containers/food/condiment/pack/astrotame)) + var/obj/item/reagent_containers/food/condiment/pack/astrotame/new_pack = attack_item + if(new_pack.reagents.total_volume < new_pack.reagents.maximum_volume) + balloon_alert(user, "the pack must be full!") + return + else if(sweetener_packs >= max_sweetener_packs) + balloon_alert(user, "the sweetener compartment is full!") + return + else if(!user.transferItemToLoc(attack_item, src)) + return + sweetener_packs++ + update_appearance(UPDATE_OVERLAYS) + return TRUE //no afterattack + + if (istype(attack_item, /obj/item/coffee_cartridge) && !(attack_item.item_flags & ABSTRACT)) + var/obj/item/coffee_cartridge/new_cartridge = attack_item + if(!user.transferItemToLoc(new_cartridge, src)) + return + replace_cartridge(user, new_cartridge) + balloon_alert(user, "added cartridge") + update_appearance(UPDATE_OVERLAYS) + return TRUE //no afterattack + +/obj/machinery/coffeemaker/proc/try_brew() + if(!cartridge) + balloon_alert(usr, "no coffee cartridge inserted!") + return FALSE + if(cartridge.charges < 1) + balloon_alert(usr, "coffee cartridge empty!") + return FALSE + if(!coffeepot) + balloon_alert(usr, "no coffeepot inside!") + return FALSE + if(machine_stat & (NOPOWER|BROKEN)) + balloon_alert(usr, "machine unpowered!") + return FALSE + if(coffeepot.reagents.total_volume >= coffeepot.reagents.maximum_volume) + balloon_alert(usr, "the coffeepot is already full!") + return FALSE + return TRUE + +/obj/machinery/coffeemaker/ui_interact(mob/user) // The microwave Menu //I am reasonably certain that this is not a microwave //I am positively certain that this is not a microwave + . = ..() + + if(brewing || panel_open || !anchored || !user.canUseTopic(src, !issilicon(user))) + return + + var/list/options = list() + + if(coffeepot) + options["Eject Pot"] = radial_eject_pot + + if(cartridge) + options["Eject Cartridge"] = radial_eject_cartridge + + options["Brew"] = radial_brew //brew is always available as an option, when the machine is unable to brew the player is told by balloon alerts whats exactly wrong + + if(coffee_cups > 0) + options["Take Cup"] = radial_take_cup + + if(sugar_packs > 0) + options["Take Sugar"] = radial_take_sugar + + if(sweetener_packs > 0) + options["Take Sweetener"] = radial_take_sweetener + + if(creamer_packs > 0) + options["Take Creamer"] = radial_take_creamer + + if(isAI(user)) + if(machine_stat & NOPOWER) + return + options["Examine"] = radial_examine + + var/choice + + if(length(options) < 1) + return + if(length(options) == 1) + choice = options[1] + else + choice = show_radial_menu(user, src, options, require_near = !issilicon(user)) + + // post choice verification + if(brewing || panel_open || !anchored || !user.canUseTopic(src, !issilicon(user))) + return + + switch(choice) + if("Brew") + brew(user) + if("Eject Pot") + eject_pot(user) + if("Eject Cartridge") + eject_cartridge(user) + if("Examine") + examine(user) + if("Take Cup") + take_cup(user) + if("Take Sugar") + take_sugar(user) + if("Take Sweetener") + take_sweetener(user) + if("Take Creamer") + take_creamer(user) + +/obj/machinery/coffeemaker/proc/eject_pot(mob/user) + if(coffeepot) + replace_pot(user) + +/obj/machinery/coffeemaker/proc/eject_cartridge(mob/user) + if(cartridge) + replace_cartridge(user) + +/obj/machinery/coffeemaker/proc/take_cup(mob/user) + if(!coffee_cups) //shouldn't happen, but we all know how stuff manages to break + balloon_alert(user, "no cups left!") + return + var/obj/item/reagent_containers/glass/coffee_cup/new_cup = new(get_turf(src)) + user.put_in_hands(new_cup) + coffee_cups-- + update_appearance(UPDATE_OVERLAYS) + +/obj/machinery/coffeemaker/proc/take_sugar(mob/user) + if(!sugar_packs) + balloon_alert(user, "no sugar left!") + return + var/obj/item/reagent_containers/food/condiment/pack/sugar/new_pack = new(get_turf(src)) + user.put_in_hands(new_pack) + sugar_packs-- + update_appearance(UPDATE_OVERLAYS) + +/obj/machinery/coffeemaker/proc/take_sweetener(mob/user) + if(!sweetener_packs) + balloon_alert(user, "no sweetener left!") + return + var/obj/item/reagent_containers/food/condiment/pack/astrotame/new_pack = new(get_turf(src)) + user.put_in_hands(new_pack) + sweetener_packs-- + update_appearance(UPDATE_OVERLAYS) + +/obj/machinery/coffeemaker/proc/take_creamer(mob/user) + if(!creamer_packs) + balloon_alert(user, "no creamer left!") + return + var/obj/item/reagent_containers/food/condiment/pack/creamer/new_pack = new(drop_location()) + user.put_in_hands(new_pack) + creamer_packs-- + update_appearance(UPDATE_OVERLAYS) + +///Updates the smoke state to something else, setting particles if relevant +/obj/machinery/coffeemaker/proc/toggle_steam() + QDEL_NULL(particles) + if(brewing) + particles.position = list(-6, 0, 0) + +/obj/machinery/coffeemaker/proc/operate_for(time, silent = FALSE) + brewing = TRUE + if(!silent) + playsound(src, 'sound/machines/coffeemaker_brew.ogg', 20, vary = TRUE) + toggle_steam() + use_power(active_power_usage * time * 0.1) // .1 needed here to convert time (in deciseconds) to seconds such that watts * seconds = joules + addtimer(CALLBACK(src, PROC_REF(stop_operating)), time / speed) + +/obj/machinery/coffeemaker/proc/stop_operating() + brewing = FALSE + toggle_steam() + +/obj/machinery/coffeemaker/proc/brew() + power_change() + if(!try_brew()) + return + operate_for(brew_time) + coffeepot.reagents.add_reagent_list(cartridge.drink_type) + cartridge.charges-- + +//Coffee Cartridges: like toner, but for your coffee! +/obj/item/coffee_cartridge + name = "coffeemaker cartridge- Caffè Generico" + desc = "A coffee cartridge manufactured by Piccionaia Coffee, for use with the Modello 3 system." + icon = 'icons/obj/machines/coffeemaker.dmi' + icon_state = "cartridge_basic" + var/charges = 4 + var/list/drink_type = list(/datum/reagent/consumable/coffee = 120) + +/obj/item/coffee_cartridge/examine(mob/user) + . = ..() + if(charges) + . += span_warning("The cartridge has [charges] portions of grounds remaining.") + else + . += span_warning("The cartridge has no unspent grounds remaining.") + +/obj/item/coffee_cartridge/fancy + name = "coffeemaker cartridge - Caffè Fantasioso" + desc = "A fancy coffee cartridge manufactured by Piccionaia Coffee, for use with the Modello 3 system." + icon_state = "cartridge_blend" + +//Here's the joke before I get 50 issue reports: they're all the same, and that's intentional +/obj/item/coffee_cartridge/fancy/Initialize(mapload) + . = ..() + var/coffee_type = pick("blend", "blue_mountain", "kilimanjaro", "mocha") + switch(coffee_type) + if("blend") + name = "coffeemaker cartridge - Miscela di Piccione" + icon_state = "cartridge_blend" + if("blue_mountain") + name = "coffeemaker cartridge - Montagna Blu" + icon_state = "cartridge_blue_mtn" + if("kilimanjaro") + name = "coffeemaker cartridge - Kilimangiaro" + icon_state = "cartridge_kilimanjaro" + if("mocha") + name = "coffeemaker cartridge - Moka Arabica" + icon_state = "cartridge_mocha" + +/obj/item/coffee_cartridge/decaf + name = "coffeemaker cartridge - Caffè Decaffeinato" + desc = "A decaf coffee cartridge manufactured by Piccionaia Coffee, for use with the Modello 3 system." + icon_state = "cartridge_decaf" + +// no you can't just squeeze the juice bag into a glass! +/obj/item/coffee_cartridge/bootleg + name = "coffeemaker cartridge - Botany Blend" + desc = "A jury-rigged coffee cartridge. Should work with a Modello 3 system, though it might void the warranty." + icon_state = "cartridge_bootleg" + +// blank cartridge for crafting's sake, can be made at the service lathe +/obj/item/blank_coffee_cartridge + name = "blank coffee cartridge" + desc = "A blank coffee cartridge, ready to be filled with coffee paste." + icon = 'icons/obj/machines/coffeemaker.dmi' + icon_state = "cartridge_blank" + +//now, how do you store coffee carts? well, in a rack, of course! +/obj/item/storage/box/coffee_cart_rack + name = "coffeemaker cartridge box" + desc = "A small rack for storing coffeemaker cartridges." + var/cartridge_type = /obj/item/coffee_cartridge + +/obj/item/storage/box/coffee_cart_rack/Initialize(mapload) + . = ..() + var/datum/component/storage/STR = GetComponent(/datum/component/storage) + STR.max_items = 8 + STR.can_hold = typecacheof(list(/obj/item/coffee_cartridge)) + + +/obj/item/storage/box/coffee_cart_rack/PopulateContents() + for(var/i in 1 to 4) + new cartridge_type(src) + new /obj/item/coffee_cartridge/decaf(src) + new /obj/item/coffee_cartridge/fancy(src) + new /obj/item/coffee_cartridge(src) + +/* + * impressa coffee maker + * its supposed to be a premium line product, so its cargo-only, the board cant be therefore researched + */ + +/obj/machinery/coffeemaker/impressa + name = "impressa coffeemaker" + desc = "An industry-grade Impressa Modello 5 Coffeemaker of the Piccionaia Home Appliances premium coffeemakers product line. Makes coffee from fresh dried whole beans." + icon = 'icons/obj/machines/coffeemaker.dmi' + icon_state = "coffeemaker_impressa" + circuit = /obj/item/circuitboard/machine/coffeemaker/impressa + initial_cartridge = null //no cartridge, just coffee beans + brew_time = 15 SECONDS //industrial grade, its faster than the regular one + density = TRUE + pass_flags = PASSTABLE + /// Current amount of coffee beans stored + var/coffee_amount = 0 + /// List of coffee bean objects are stored + var/list/coffee = list() + +/obj/machinery/coffeemaker/impressa/Initialize(mapload) + . = ..() + if(mapload) + coffeepot = new /obj/item/reagent_containers/food/drinks/bottle/coffeepot(src) + cartridge = null + +/obj/machinery/coffeemaker/impressa/Destroy() + QDEL_NULL(coffeepot) + QDEL_NULL(coffee) + return ..() + +/obj/machinery/coffeemaker/impressa/examine(mob/user) + . = ..() + if(coffee) + . += span_notice("The internal grinder contains [length(coffee)] scoop\s of coffee beans") + +/obj/machinery/coffeemaker/impressa/update_overlays() + . = ..() + . += overlay_checks() + +/obj/machinery/coffeemaker/impressa/overlay_checks() + . = list() + if(coffeepot) + if(coffeepot.reagents.total_volume > 0) + . += "pot_full" + else + . += "pot_empty" + if(coffee_cups > 0) + if(coffee_cups >= max_coffee_cups/3) + if(coffee_cups > max_coffee_cups/1.5) + . += "cups_3" + else + . += "cups_2" + else + . += "cups_1" + if(sugar_packs) + . += "extras_1" + if(creamer_packs) + . += "extras_2" + if(sweetener_packs) + . += "extras_3" + if(coffee_amount) + if(coffee_amount < 0.7*BEAN_CAPACITY) + . += "grinder_half" + else + . += "grinder_full" + return . + +/obj/machinery/coffeemaker/impressa/Exited(atom/movable/gone, direction) + . = ..() + if(gone in coffee) + coffee -= gone + update_appearance(UPDATE_OVERLAYS) + +/obj/machinery/coffeemaker/impressa/try_brew(mob/living/user) + if(coffee_amount <= 0) + balloon_alert(user, "no coffee beans added!") + return FALSE + if(!coffeepot) + balloon_alert(user, "no coffeepot inside!") + return FALSE + if(machine_stat & (NOPOWER|BROKEN)) + balloon_alert(user, "machine unpowered!") + return FALSE + if(coffeepot.reagents.total_volume >= coffeepot.reagents.maximum_volume) + balloon_alert(user, "the coffeepot is already full!") + return FALSE + return TRUE + +/obj/machinery/coffeemaker/impressa/attackby(obj/item/attack_item, mob/living/user, params) + //You can only screw open empty grinder + if(!coffeepot && default_deconstruction_screwdriver(user, icon_state, icon_state, attack_item)) + return + + if(default_deconstruction_crowbar(attack_item)) + return + + if(panel_open) //Can't insert objects when its screwed open + return TRUE + + if (istype(attack_item, /obj/item/reagent_containers/food/drinks/bottle/coffeepot) && !(attack_item.item_flags & ABSTRACT) && attack_item.is_open_container()) + var/obj/item/reagent_containers/food/drinks/bottle/coffeepot/new_pot = attack_item + if(!user.transferItemToLoc(new_pot, src)) + return TRUE + replace_pot(user, new_pot) + update_appearance(UPDATE_OVERLAYS) + return TRUE //no afterattack + + if (istype(attack_item, /obj/item/reagent_containers/glass/coffee_cup) && !(attack_item.item_flags & ABSTRACT) && attack_item.is_open_container()) + var/obj/item/reagent_containers/glass/coffee_cup/new_cup = attack_item //different type of cup + if(new_cup.reagents.total_volume > 0) + balloon_alert(user, "the cup must be empty!") + return + if(coffee_cups >= max_coffee_cups) + balloon_alert(user, "the cup holder is full!") + return + if(!user.transferItemToLoc(attack_item, src)) + return + coffee_cups++ + update_appearance(UPDATE_OVERLAYS) + return TRUE //no afterattack + + if (istype(attack_item, /obj/item/reagent_containers/food/condiment/pack/sugar)) + var/obj/item/reagent_containers/food/condiment/pack/sugar/new_pack = attack_item + if(new_pack.reagents.total_volume < new_pack.reagents.maximum_volume) + balloon_alert(user, "the pack must be full!") + return + if(sugar_packs >= max_sugar_packs) + balloon_alert(user, "the sugar compartment is full!") + return + if(!user.transferItemToLoc(attack_item, src)) + return + sugar_packs++ + update_appearance(UPDATE_OVERLAYS) + return TRUE //no afterattack + + if (istype(attack_item, /obj/item/reagent_containers/food/condiment/pack/creamer)) + var/obj/item/reagent_containers/food/condiment/pack/creamer/new_pack = attack_item + if(new_pack.reagents.total_volume < new_pack.reagents.maximum_volume) + balloon_alert(user, "the pack must be full!") + return + if(creamer_packs >= max_creamer_packs) + balloon_alert(user, "the creamer compartment is full!") + return + if(!user.transferItemToLoc(attack_item, src)) + return + creamer_packs++ + update_appearance(UPDATE_OVERLAYS) + return TRUE //no afterattack + + if (istype(attack_item, /obj/item/reagent_containers/food/condiment/pack/astrotame)) + var/obj/item/reagent_containers/food/condiment/pack/astrotame/new_pack = attack_item + if(new_pack.reagents.total_volume < new_pack.reagents.maximum_volume) + balloon_alert(user, "the pack must be full!") + return + if(sweetener_packs >= max_sweetener_packs) + balloon_alert(user, "the sweetener compartment is full!") + return + if(!user.transferItemToLoc(attack_item, src)) + return + sweetener_packs++ + update_appearance(UPDATE_OVERLAYS) + return TRUE //no afterattack + + if (istype(attack_item, /obj/item/reagent_containers/food/snacks/grown/coffee) && !(attack_item.item_flags & ABSTRACT)) + if(coffee_amount >= BEAN_CAPACITY) + balloon_alert(user, "the coffee container is full!") + return + var/obj/item/reagent_containers/food/snacks/grown/coffee/new_coffee = attack_item + if(!user.transferItemToLoc(new_coffee, src)) + return + coffee += new_coffee + coffee_amount++ + balloon_alert(user, "added coffee") + + + if (istype(attack_item, /obj/item/storage/box/coffeepack)) + if(coffee_amount >= BEAN_CAPACITY) + balloon_alert(user, "the coffee container is full!") + return + var/obj/item/storage/box/coffeepack/new_coffee_pack = attack_item + for(var/obj/item/reagent_containers/food/snacks/grown/coffee/new_coffee in new_coffee_pack.contents) + if(coffee_amount < BEAN_CAPACITY) + if(user.transferItemToLoc(new_coffee, src)) + coffee += new_coffee + coffee_amount++ + new_coffee.forceMove(src) + balloon_alert(user, "added coffee") + update_appearance(UPDATE_OVERLAYS) + else + return + update_appearance(UPDATE_OVERLAYS) + return TRUE //no afterattack + +/obj/machinery/coffeemaker/impressa/take_cup(mob/user) + if(!coffee_cups) //shouldn't happen, but we all know how stuff manages to break + balloon_alert(user, "no cups left!") + return + balloon_alert_to_viewers("took cup") + var/obj/item/reagent_containers/food/drinks/coffee/new_cup = new(get_turf(src)) + user.put_in_hands(new_cup) + coffee_cups-- + update_appearance(UPDATE_OVERLAYS) + +/obj/machinery/coffeemaker/impressa/toggle_steam() + QDEL_NULL(particles) + if(brewing) + particles.position = list(-2, 1, 0) + +/obj/machinery/coffeemaker/impressa/brew() + power_change() + if(!try_brew()) + return + operate_for(brew_time) + coffeepot.reagents.add_reagent_list(list(/datum/reagent/consumable/coffee = 120)) + coffee.Cut(1,2) //remove the first item from the list + coffee_amount-- + update_appearance(UPDATE_OVERLAYS) + +#undef BEAN_CAPACITY diff --git a/code/modules/reagents/reagent_containers/bottle.dm b/code/modules/reagents/reagent_containers/bottle.dm index 8ec8cd674642..3710a25f3aab 100644 --- a/code/modules/reagents/reagent_containers/bottle.dm +++ b/code/modules/reagents/reagent_containers/bottle.dm @@ -439,3 +439,39 @@ /obj/item/reagent_containers/glass/bottle/morphine/sleeper cap_on = FALSE + +//types of syrups + +/obj/item/reagent_containers/food/drinks/bottle/syrup_bottle/caramel + name = "bottle of caramel syrup" + desc = "A pump bottle containing caramalized sugar, also known as caramel. Do not lick." + list_reagents = list(/datum/reagent/consumable/caramel = 50) + +/obj/item/reagent_containers/food/drinks/bottle/syrup_bottle/liqueur + name = "bottle of coffee liqueur syrup" + desc = "A pump bottle containing mexican coffee-flavoured liqueur syrup. In production since 1936, HONK." + list_reagents = list(/datum/reagent/consumable/ethanol/kahlua = 50) + +//Coffeepots: for reference, a standard cup is 30u, to allow 20u for sugar/sweetener/milk/creamer +/obj/item/reagent_containers/food/drinks/bottle/coffeepot + icon = 'icons/obj/food/containers.dmi' + name = "coffeepot" + desc = "A large pot for dispensing that ambrosia of corporate life known to mortals only as coffee. Contains 4 standard cups." + volume = 120 + icon_state = "coffeepot" + fill_icon_state = "coffeepot" + fill_icon_thresholds = list(0, 1, 30, 60, 100) + +/obj/item/reagent_containers/glass/coffee_cup + name = "coffee cup" + desc = "A heat-formed plastic coffee cup. Can theoretically be used for other hot drinks, if you're feeling adventurous." + icon = 'icons/obj/machines/coffeemaker.dmi' + icon_state = "coffee_cup_e" + base_icon_state = "coffee_cup" + possible_transfer_amounts = list(10) + volume = 30 + spillable = TRUE + +/obj/item/reagent_containers/glass/coffee_cup/update_icon_state() + icon_state = reagents.total_volume ? base_icon_state : "[base_icon_state]_e" + return ..() diff --git a/icons/mob/radial.dmi b/icons/mob/radial.dmi index 8ec3a0fb4666..9b43fa0d710f 100644 Binary files a/icons/mob/radial.dmi and b/icons/mob/radial.dmi differ diff --git a/icons/obj/machines/coffeemaker.dmi b/icons/obj/machines/coffeemaker.dmi new file mode 100644 index 000000000000..246159f1c54f Binary files /dev/null and b/icons/obj/machines/coffeemaker.dmi differ diff --git a/shiptest.dme b/shiptest.dme index e933dfe020ce..df737d985b37 100644 --- a/shiptest.dme +++ b/shiptest.dme @@ -2135,6 +2135,7 @@ #include "code\modules\food_and_drinks\food\snacks\dough.dm" #include "code\modules\food_and_drinks\food\snacks\meat.dm" #include "code\modules\food_and_drinks\kitchen_machinery\big_mortar.dm" +#include "code\modules\food_and_drinks\kitchen_machinery\coffeemaker.dm" #include "code\modules\food_and_drinks\kitchen_machinery\cutting_board.dm" #include "code\modules\food_and_drinks\kitchen_machinery\deep_fryer.dm" #include "code\modules\food_and_drinks\kitchen_machinery\food_cart.dm" diff --git a/sound/machines/coffeemaker_brew.ogg b/sound/machines/coffeemaker_brew.ogg new file mode 100644 index 000000000000..a8e25c09867a Binary files /dev/null and b/sound/machines/coffeemaker_brew.ogg differ