From c63f696f30b586c985294c722e5edd7b1e01ad6a Mon Sep 17 00:00:00 2001 From: tralezab <40974010+tralezab@users.noreply.github.com> Date: Tue, 2 Mar 2021 19:16:27 -0800 Subject: [PATCH] Chef Produce Console, for cases where there are no botanists (#57298) --- .../map_files/Deltastation/DeltaStation2.dmm | 12 +- .../map_files/IceBoxStation/IceBoxStation.dmm | 8 +- _maps/map_files/KiloStation/KiloStation.dmm | 15 +- _maps/map_files/MetaStation/MetaStation.dmm | 3 + code/controllers/subsystem/shuttle.dm | 1 + .../computer/chef_orders/chef_order.dm | 123 ++++++++ .../computer/chef_orders/order_datum.dm | 169 +++++++++++ .../circuitboards/computer_circuitboards.dm | 5 + code/modules/cargo/orderconsole.dm | 1 + code/modules/shuttle/supply.dm | 17 +- tgstation.dme | 2 + tgui/packages/tgui/interfaces/Cargo.js | 4 + .../tgui/interfaces/ProduceConsole.js | 270 ++++++++++++++++++ 13 files changed, 621 insertions(+), 9 deletions(-) create mode 100644 code/game/machinery/computer/chef_orders/chef_order.dm create mode 100644 code/game/machinery/computer/chef_orders/order_datum.dm create mode 100644 tgui/packages/tgui/interfaces/ProduceConsole.js diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index 6a1ae37a584..f9a426d78e1 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -76247,6 +76247,10 @@ /obj/item/clipboard, /obj/item/toy/figure/chef, /obj/effect/turf_decal/bot, +/obj/item/food/mint, +/obj/item/reagent_containers/food/condiment/enzyme{ + pixel_y = 5 + }, /turf/open/floor/iron, /area/service/kitchen) "khj" = ( @@ -93125,12 +93129,10 @@ /turf/open/floor/iron, /area/science/misc_lab/range) "qhn" = ( -/obj/structure/table/reinforced, -/obj/item/food/mint, -/obj/item/reagent_containers/food/condiment/enzyme{ - pixel_y = 5 - }, /obj/effect/turf_decal/bot, +/obj/machinery/computer/chef_order{ + dir = 1 + }, /turf/open/floor/iron, /area/service/kitchen) "qhE" = ( diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm index bd71d6f9ec6..6bec9c881ed 100644 --- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm +++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm @@ -49286,6 +49286,12 @@ }, /turf/open/floor/iron, /area/engineering/atmos) +"sTS" = ( +/obj/machinery/computer/chef_order{ + dir = 4 + }, +/turf/open/floor/iron/cafeteria, +/area/service/kitchen) "sUk" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt, @@ -95009,7 +95015,7 @@ dfW mjA rLD nUo -nUo +sTS nUo nUo tIN diff --git a/_maps/map_files/KiloStation/KiloStation.dmm b/_maps/map_files/KiloStation/KiloStation.dmm index 2ef9f26539e..3d056720a12 100644 --- a/_maps/map_files/KiloStation/KiloStation.dmm +++ b/_maps/map_files/KiloStation/KiloStation.dmm @@ -65525,6 +65525,10 @@ /obj/item/food/dough, /obj/item/kitchen/rollingpin, /obj/item/kitchen/rollingpin, +/obj/item/paper/crumpled{ + info = "Hey whoever designed this shithole didn't give us space to install the produce computer so it's in maintenance near the theatre."; + name = "hastily written note" + }, /turf/open/floor/iron/dark, /area/service/kitchen) "jAY" = ( @@ -90726,6 +90730,15 @@ }, /turf/open/floor/iron/dark, /area/cargo/qm) +"xwt" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/computer/chef_order{ + dir = 8 + }, +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/maintenance/central) "xww" = ( /obj/structure/closet/secure_closet/personal/cabinet, /obj/machinery/newscaster{ @@ -124428,7 +124441,7 @@ aox aox buB ajq -akM +xwt uuQ sox ref diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index 591c7671472..24a3cc8dcee 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -36431,6 +36431,9 @@ dir = 8 }, /obj/effect/decal/cleanable/dirt, +/obj/machinery/computer/chef_order{ + dir = 1 + }, /turf/open/floor/iron, /area/hallway/secondary/service) "fDd" = ( diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm index 72574e526b5..22f2b8bfd59 100644 --- a/code/controllers/subsystem/shuttle.dm +++ b/code/controllers/subsystem/shuttle.dm @@ -45,6 +45,7 @@ SUBSYSTEM_DEF(shuttle) var/list/discoveredPlants = list() //Typepaths for unusual plants we've already sent CentCom, associated with their potencies var/list/supply_packs = list() + var/list/chef_groceries = list() var/list/shoppinglist = list() var/list/requestlist = list() var/list/orderhistory = list() diff --git a/code/game/machinery/computer/chef_orders/chef_order.dm b/code/game/machinery/computer/chef_orders/chef_order.dm new file mode 100644 index 00000000000..4709a0f46a6 --- /dev/null +++ b/code/game/machinery/computer/chef_orders/chef_order.dm @@ -0,0 +1,123 @@ + +/obj/machinery/computer/chef_order + name = "Produce Orders Console" + desc = "An interface for ordering fresh produce and other. A far more expensive option than the botanists, but oh well." + icon_screen = "request" + icon_keyboard = "generic_key" + circuit = /obj/item/circuitboard/computer/chef_order + light_color = LIGHT_COLOR_ORANGE + + COOLDOWN_DECLARE(order_cooldown) + var/static/list/order_datums = list() + var/list/grocery_list = list() + + var/obj/item/radio/radio + var/radio_channel = RADIO_CHANNEL_SUPPLY + +/obj/machinery/computer/chef_order/Initialize() + . = ..() + radio = new(src) + radio.frequency = FREQ_SUPPLY + radio.subspace_transmission = TRUE + radio.canhear_range = 0 + radio.recalculateChannels() + + if(!order_datums.len) + for(var/path in subtypesof(/datum/orderable_item)) + order_datums += new path + +/obj/machinery/computer/chef_order/Destroy() + QDEL_NULL(radio) + . = ..() + +/obj/machinery/computer/chef_order/proc/get_total_cost() + . = 0 + for(var/datum/orderable_item/item as anything in grocery_list) + for(var/i in 1 to grocery_list[item]) //for how many times we bought it + . += item.cost_per_order //add its price + +/obj/machinery/computer/chef_order/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ProduceConsole", name) + ui.open() + +/obj/machinery/computer/chef_order/ui_data(mob/user) + . = ..() + .["off_cooldown"] = COOLDOWN_FINISHED(src, order_cooldown) + +/obj/machinery/computer/chef_order/ui_static_data(mob/user) + . = ..() + .["total_cost"] = get_total_cost() + .["order_datums"] = list() + for(var/datum/orderable_item/item as anything in order_datums) + .["order_datums"] += list(list( + "name" = item.name, + "desc" = item.desc, + "cat" = item.category_index, + "ref" = REF(item), + "cost" = item.cost_per_order, + "amt" = grocery_list[item] + )) + +/obj/machinery/computer/chef_order/ui_act(action, params) + . = ..() + if(.) + return + if(!isliving(usr)) + return + var/mob/living/chef = usr + //this is null if the action doesn't need it (purchase, quickpurchase) + var/datum/orderable_item/wanted_item = locate(params["target"]) in order_datums + switch(action) + if("cart_set") + grocery_list[wanted_item] = params["amt"] + if(!grocery_list[wanted_item]) + grocery_list -= wanted_item + update_static_data(chef) + if("purchase") + if(!grocery_list.len || !COOLDOWN_FINISHED(src, order_cooldown)) + return + var/obj/item/card/id/chef_card = chef.get_idcard(TRUE) + if(!chef_card || !chef_card.registered_account) + say("No bank account detected!") + return + var/final_cost = get_total_cost() + if(!chef_card.registered_account.adjust_money(-final_cost)) + say("Sorry, but you do not have enough money.") + return + say("Thank you for your purchase! It will arrive on the next cargo shuttle!") + var/message = "The kitchen has ordered groceries which will arrive on the cargo shuttle! Please make sure it gets to them as soon as possible!" + radio.talk_into(src, message, radio_channel) + COOLDOWN_START(src, order_cooldown, 60 SECONDS) + for(var/datum/orderable_item/ordered_item in grocery_list) + if(ordered_item in SSshuttle.chef_groceries) + SSshuttle.chef_groceries[ordered_item] += grocery_list[ordered_item] + else + SSshuttle.chef_groceries[ordered_item] = grocery_list[ordered_item] + grocery_list.Cut() + update_static_data(chef) + if("express") + if(!grocery_list.len || !COOLDOWN_FINISHED(src, order_cooldown)) + return + var/obj/item/card/id/chef_card = chef.get_idcard(TRUE) + if(!chef_card || !chef_card.registered_account) + say("No bank account detected!") + return + var/final_cost = get_total_cost() + final_cost *= 2 + if(!chef_card.registered_account.adjust_money(-final_cost)) + say("Sorry, but you do not have enough money. Remember, Express upcharges the cost!") + return + say("Thank you for your purchase! Please note: The charge of this purchase and machine cooldown has been doubled!") + COOLDOWN_START(src, order_cooldown, 120 SECONDS) + var/obj/structure/closet/supplypod/bluespacepod/pod = new() + pod.explosionSize = list(0,0,0,0) + for(var/datum/orderable_item/item as anything in grocery_list)//every order + for(var/amt in 1 to grocery_list[item])//every order amount + new item.item_instance.type(pod) + var/turf/landing_location = get_turf(chef) + new /obj/effect/pod_landingzone(landing_location, pod) + grocery_list.Cut() + update_static_data(chef) + . = TRUE diff --git a/code/game/machinery/computer/chef_orders/order_datum.dm b/code/game/machinery/computer/chef_orders/order_datum.dm new file mode 100644 index 00000000000..355c0984dcb --- /dev/null +++ b/code/game/machinery/computer/chef_orders/order_datum.dm @@ -0,0 +1,169 @@ + +#define CATEGORY_FRUITS_VEGGIES 1 +#define CATEGORY_MILK_EGGS 2 +#define CATEGORY_SAUCES_REAGENTS 3 + +///A datum for chef ordering options from the chef's computer. +/datum/orderable_item + var/name = "Orderable Item Name" + //description set automatically unless it's hard set by the subtype + var/desc + var/category_index = CATEGORY_FRUITS_VEGGIES + var/obj/item/item_instance + var/cost_per_order = 10 + +/datum/orderable_item/New() + . = ..() + if(type == /datum/orderable_item) + return + if(!item_instance) + CRASH("[type] orderable item datum has NO ITEM PATH!") + item_instance = new item_instance + if(!desc) + desc = item_instance.desc + +/datum/orderable_item/Destroy(force, ...) + . = ..() + qdel(item_instance) + +//Fruits and Veggies + +/datum/orderable_item/potato + name = "Potato" + category_index = CATEGORY_FRUITS_VEGGIES + item_instance = /obj/item/food/grown/potato + +/datum/orderable_item/tomato + name = "Tomato" + category_index = CATEGORY_FRUITS_VEGGIES + item_instance = /obj/item/food/grown/tomato + +/datum/orderable_item/carrot + name = "Carrot" + category_index = CATEGORY_FRUITS_VEGGIES + item_instance = /obj/item/food/grown/carrot + +/datum/orderable_item/eggplant + name = "Eggplant" + category_index = CATEGORY_FRUITS_VEGGIES + item_instance = /obj/item/food/grown/eggplant + +/datum/orderable_item/mushroom + name = "Plump Helmet" + desc = "Plumus Hellmus: Plump, soft and s-so inviting~" + category_index = CATEGORY_FRUITS_VEGGIES + item_instance = /obj/item/food/grown/mushroom/plumphelmet + +/datum/orderable_item/cabbage + name = "Cabbage" + category_index = CATEGORY_FRUITS_VEGGIES + item_instance = /obj/item/food/grown/cabbage + +/datum/orderable_item/beets + name = "Onion" + category_index = CATEGORY_FRUITS_VEGGIES + item_instance = /obj/item/food/grown/onion + +/datum/orderable_item/apple + name = "Apple" + category_index = CATEGORY_FRUITS_VEGGIES + item_instance =/obj/item/food/grown/apple + +/datum/orderable_item/pumpkin + name = "Pumpkin" + category_index = CATEGORY_FRUITS_VEGGIES + item_instance =/obj/item/food/grown/pumpkin + +/datum/orderable_item/watermelon + name = "Watermelon" + category_index = CATEGORY_FRUITS_VEGGIES + item_instance =/obj/item/food/grown/watermelon + +/datum/orderable_item/corn + name = "Corn" + category_index = CATEGORY_FRUITS_VEGGIES + item_instance = /obj/item/food/grown/corn + +/datum/orderable_item/soybean + name = "Soybeans" + category_index = CATEGORY_FRUITS_VEGGIES + item_instance = /obj/item/food/grown/soybeans + +/datum/orderable_item/garlic + name = "Garlic" + category_index = CATEGORY_FRUITS_VEGGIES + item_instance = /obj/item/food/grown/garlic + +/datum/orderable_item/cherries + name = "Cherries" + category_index = CATEGORY_FRUITS_VEGGIES + item_instance = /obj/item/food/grown/cherries + +//Milk and Eggs + +/datum/orderable_item/milk + name = "Milk" + category_index = CATEGORY_MILK_EGGS + item_instance = /obj/item/reagent_containers/food/condiment/milk + cost_per_order = 30 + +/datum/orderable_item/soymilk + name = "Soy Milk" + category_index = CATEGORY_MILK_EGGS + item_instance = /obj/item/reagent_containers/food/condiment/soymilk + cost_per_order = 30 + +/datum/orderable_item/eggs + name = "Egg Carton" + category_index = CATEGORY_MILK_EGGS + item_instance = /obj/item/storage/fancy/egg_box + cost_per_order = 40 + +/datum/orderable_item/fillet + name = "Fish Fillet" + category_index = CATEGORY_MILK_EGGS + item_instance = /obj/item/food/fishmeat + cost_per_order = 12 + +/datum/orderable_item/spider_eggs + name = "Spider Eggs" + category_index = CATEGORY_MILK_EGGS + item_instance = /obj/item/food/spidereggs + +//Reagents + +/datum/orderable_item/flour + name = "Flour Sack" + category_index = CATEGORY_SAUCES_REAGENTS + item_instance = /obj/item/reagent_containers/food/condiment/flour + cost_per_order = 30 + +/datum/orderable_item/sugar + name = "Sugar Sack" + category_index = CATEGORY_SAUCES_REAGENTS + item_instance = /obj/item/reagent_containers/food/condiment/sugar + cost_per_order = 30 + +/datum/orderable_item/enzyme + name = "Universal Enzyme" + category_index = CATEGORY_SAUCES_REAGENTS + item_instance = /obj/item/reagent_containers/food/condiment/enzyme + cost_per_order = 40 + +/datum/orderable_item/salt + name = "Salt Shaker" + category_index = CATEGORY_SAUCES_REAGENTS + item_instance = /obj/item/reagent_containers/food/condiment/saltshaker + cost_per_order = 15 + +/datum/orderable_item/pepper + name = "Pepper Mill" + category_index = CATEGORY_SAUCES_REAGENTS + item_instance = /obj/item/reagent_containers/food/condiment/peppermill + cost_per_order = 15 + +/datum/orderable_item/soysauce + name = "Soy Sauce" + category_index = CATEGORY_SAUCES_REAGENTS + item_instance = /obj/item/reagent_containers/food/condiment/soysauce + cost_per_order = 15 diff --git a/code/game/objects/items/circuitboards/computer_circuitboards.dm b/code/game/objects/items/circuitboards/computer_circuitboards.dm index aed55102c8f..49902e27df7 100644 --- a/code/game/objects/items/circuitboards/computer_circuitboards.dm +++ b/code/game/objects/items/circuitboards/computer_circuitboards.dm @@ -459,6 +459,11 @@ //Service +/obj/item/circuitboard/computer/chef_order + name = "Produce Orders Console (Computer Board)" + icon_state = "supply" + build_path = /obj/machinery/computer/chef_order + //Supply /obj/item/circuitboard/computer/cargo diff --git a/code/modules/cargo/orderconsole.dm b/code/modules/cargo/orderconsole.dm index af797c8c72c..9f7c0fba022 100644 --- a/code/modules/cargo/orderconsole.dm +++ b/code/modules/cargo/orderconsole.dm @@ -82,6 +82,7 @@ var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR) if(D) data["points"] = D.account_balance + data["grocery"] = SSshuttle.chef_groceries.len data["away"] = SSshuttle.supply.getDockedId() == "supply_away" data["self_paid"] = self_paid data["docked"] = SSshuttle.supply.mode == SHUTTLE_IDLE diff --git a/code/modules/shuttle/supply.dm b/code/modules/shuttle/supply.dm index ca05d972e73..f42add42a59 100644 --- a/code/modules/shuttle/supply.dm +++ b/code/modules/shuttle/supply.dm @@ -92,8 +92,6 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list( var/list/obj/miscboxes = list() //miscboxes are combo boxes that contain all goody orders grouped var/list/misc_order_num = list() //list of strings of order numbers, so that the manifest can show all orders in a box var/list/misc_contents = list() //list of lists of items that each box will contain - if(!SSshuttle.shoppinglist.len) - return var/list/empty_turfs = list() for(var/place in shuttle_areas) @@ -103,6 +101,21 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list( continue empty_turfs += T + //quickly and greedily handle chef's grocery runs first, there are a few reasons why this isn't attached to the rest of cargo... + //but the biggest reason is that the chef requires produce to cook and do their job, and if they are using this system they + //already got let down by the botanists. So to open a new chance for cargo to also screw them over any more than is necessary is bad. + if(SSshuttle.chef_groceries.len) + var/obj/structure/closet/crate/freezer/grocery_crate = new(pick_n_take(empty_turfs)) + grocery_crate.name = "kitchen produce freezer" + investigate_log("Chef's [SSshuttle.chef_groceries.len] sized produce order arrived. Cost was deducted from orderer, not cargo.", INVESTIGATE_CARGO) + for(var/datum/orderable_item/item as anything in SSshuttle.chef_groceries)//every order + for(var/amt in 1 to SSshuttle.chef_groceries[item])//every order amount + new item.item_instance.type(grocery_crate) + SSshuttle.chef_groceries.Cut() //This lets the console know it can order another round. + + if(!SSshuttle.shoppinglist.len) + return + var/value = 0 var/purchases = 0 var/list/goodies_by_buyer = list() // if someone orders more than GOODY_FREE_SHIPPING_MAX goodies, we upcharge to a normal crate so they can't carry around 20 combat shotties diff --git a/tgstation.dme b/tgstation.dme index ad3c41731e0..fa30bd7ab54 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -944,6 +944,8 @@ #include "code\game\machinery\computer\station_alert.dm" #include "code\game\machinery\computer\teleporter.dm" #include "code\game\machinery\computer\warrant.dm" +#include "code\game\machinery\computer\chef_orders\chef_order.dm" +#include "code\game\machinery\computer\chef_orders\order_datum.dm" #include "code\game\machinery\computer\prisoner\_prisoner.dm" #include "code\game\machinery\computer\prisoner\gulag_teleporter.dm" #include "code\game\machinery\computer\prisoner\management.dm" diff --git a/tgui/packages/tgui/interfaces/Cargo.js b/tgui/packages/tgui/interfaces/Cargo.js index 01b3e3e29b2..6a73992abb3 100644 --- a/tgui/packages/tgui/interfaces/Cargo.js +++ b/tgui/packages/tgui/interfaces/Cargo.js @@ -73,6 +73,7 @@ export const CargoContent = (props, context) => { const CargoStatus = (props, context) => { const { act, data } = useBackend(context); const { + grocery, away, docked, loan, @@ -98,7 +99,10 @@ const CargoStatus = (props, context) => { {docked && !requestonly && can_send &&(