From ba0fd0be33c7075ec60da36d2f18b9eb552b8b1c Mon Sep 17 00:00:00 2001 From: Poojawa Date: Thu, 9 Jul 2026 13:50:35 -0500 Subject: [PATCH] Food Synthesizers (#19569) * Food Synthesizers Initial port, need to figure out what madness you people have done to tgui... and sprite sheets. At this moment, it compiles but I haven't tested it yet. Too eepy * iconforge spritesheet nonsense * Cleaning up tgui menu full disclosure, After six plus hours of trying to figure out how to do the spritesheets and the tgui stuff I just gave up on this particular bit and threw it into Gemini for a looksie over. As it's originally my code I figure it's worth at least a reasonable proof read. The side-by-side comparison is pretty wild. * Passes tgu-lint building * Appease Gripe err Grep checks * Oh these for the grep as well whoops * Sorts out the backend also piss off linter check, it's COMMENTED OUT aaaaaaaaaa. * changes from will * changes from will and some UI splitting, needs proper testing and final adjustments * passadata * . * can be combined * . * ml={1} * id * Crew category * id handling * . * more cleanup * smaller cleanup * why not * deps up, minor tgui core fix * timer * . * . * missed to put that back * order * this * Change preference category for foodsynth cookies * Enforce south facement for cookie image * Maurice I am no longer asking * . --------- Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com> --- code/__defines/_reagents.dm | 4 + code/datums/wires/foodsynth.dm | 69 + code/game/machinery/frame.dm | 19 +- .../assets/spritesheets/foodsynth.dm | 15 + .../preference_setup/general/11_misc.dm | 14 +- .../types/character/general/11_misc.dm | 11 + .../cooking_machines/foodsynthesizer.dm | 715 ++++++++++ code/modules/food/recipes_foodsynthesizer.dm | 1150 +++++++++++++++++ code/modules/mob/living/carbon/taste.dm | 2 +- code/modules/resleeving/infocore_records.dm | 5 +- icons/obj/machines/foodsynthesizer.dmi | Bin 0 -> 34723 bytes tgui/bun.lock | 60 +- tgui/package.json | 4 +- tgui/packages/tgfont/package.json | 2 +- tgui/packages/tgui-panel/package.json | 2 +- tgui/packages/tgui-say/package.json | 2 +- .../FoodSynthesizer/CrewCookieIcon.tsx | 22 + .../FoodSynthesizer/SubTabs/FoodMenuTabs.tsx | 31 + .../SubTabs/FoodSelectionMenu.tsx | 182 +++ .../SubTabs/SynthCartGuage.tsx | 33 + .../tgui/interfaces/FoodSynthesizer/index.tsx | 29 + .../tgui/interfaces/FoodSynthesizer/types.ts | 35 + .../bay_prefs/general/SubtabSettings.tsx | 10 + .../PreferencesMenu/bay_prefs/general/data.ts | 1 + tgui/packages/tgui/package.json | 4 +- vorestation.dme | 4 + 26 files changed, 2378 insertions(+), 47 deletions(-) create mode 100644 code/datums/wires/foodsynth.dm create mode 100644 code/modules/asset_cache/assets/spritesheets/foodsynth.dm create mode 100644 code/modules/food/kitchen/cooking_machines/foodsynthesizer.dm create mode 100644 code/modules/food/recipes_foodsynthesizer.dm create mode 100644 icons/obj/machines/foodsynthesizer.dmi create mode 100644 tgui/packages/tgui/interfaces/FoodSynthesizer/CrewCookieIcon.tsx create mode 100644 tgui/packages/tgui/interfaces/FoodSynthesizer/SubTabs/FoodMenuTabs.tsx create mode 100644 tgui/packages/tgui/interfaces/FoodSynthesizer/SubTabs/FoodSelectionMenu.tsx create mode 100644 tgui/packages/tgui/interfaces/FoodSynthesizer/SubTabs/SynthCartGuage.tsx create mode 100644 tgui/packages/tgui/interfaces/FoodSynthesizer/index.tsx create mode 100644 tgui/packages/tgui/interfaces/FoodSynthesizer/types.ts diff --git a/code/__defines/_reagents.dm b/code/__defines/_reagents.dm index 25ea062068..602177c799 100644 --- a/code/__defines/_reagents.dm +++ b/code/__defines/_reagents.dm @@ -724,6 +724,10 @@ #define REAGENT_ID_GELATIN "gelatin" #define REAGENT_MUSTARDPODS "Mustard Pods" #define REAGENT_ID_MUSTARDPODS "mustardpods" +#define REAGENT_NUTRIPASTE "Nutriment Paste" +#define REAGENT_ID_NUTRIPASTE "nutripaste" +#define REAGENT_NUTRIPASTE_SOYLENT "Soylent Agent Green" +#define REAGENT_ID_NUTRIPASTE_SOYLENT "synthsoygreen" #define REAGENT_DRINK "Drink" #define REAGENT_ID_DRINK "drink" diff --git a/code/datums/wires/foodsynth.dm b/code/datums/wires/foodsynth.dm new file mode 100644 index 0000000000..4a757acc48 --- /dev/null +++ b/code/datums/wires/foodsynth.dm @@ -0,0 +1,69 @@ +//Totally not just a blatant copypasta. Nooooo... +/datum/wires/synthesizer + holder_type = /obj/machinery/synthesizer + wire_count = 6 + proper_name = "Food Synthesizer" + +/datum/wires/synthesizer/New(atom/_holder) + wires = list(WIRE_LATHE_HACK, WIRE_ELECTRIFY, WIRE_LATHE_DISABLE) + var/obj/machinery/synthesizer/A = _holder + if(A.hacked) + cut_wires += WIRE_LATHE_HACK + return ..() + +/datum/wires/synthesizer/get_status() + . = ..() + var/obj/machinery/synthesizer/A = holder + . += "The red light is [A.disabled ? "off" : "on"]." + . += "The green light is [A.shocked ? "off" : "on"]." + . += "The blue light is [A.hacked ? "off" : "on"]." + +/datum/wires/synthesizer/interactable(mob/user) + var/obj/machinery/synthesizer/A = holder + if(A.panel_open) + return TRUE + return FALSE + +/datum/wires/synthesizer/on_cut(wire, mend) + var/obj/machinery/synthesizer/A = holder + switch(wire) + if(WIRE_LATHE_HACK) + A.hacked = !mend + A.update_tgui_static_data(usr) + if(WIRE_ELECTRIFY) + A.shocked = !mend + if(WIRE_LATHE_DISABLE) + A.disabled = !mend + ..() + +/datum/wires/synthesizer/on_pulse(wire) + if(is_cut(wire)) + return + var/obj/machinery/synthesizer/A = holder + switch(wire) + if(WIRE_LATHE_HACK) + A.hacked = !A.hacked + A.update_tgui_static_data(usr) + addtimer(CALLBACK(src, PROC_REF(reset_hacked), WIRE_LATHE_HACK, usr), 5 SECONDS) + if(WIRE_ELECTRIFY) + A.shocked = !A.shocked + addtimer(CALLBACK(src, PROC_REF(reset_electrify), WIRE_ELECTRIFY), 5 SECONDS) + if(WIRE_LATHE_DISABLE) + A.disabled = !A.disabled + addtimer(CALLBACK(src, PROC_REF(reset_disable), WIRE_LATHE_DISABLE), 5 SECONDS) + +/datum/wires/synthesizer/proc/reset_hacked(wire, mob/user) + var/obj/machinery/synthesizer/A = holder + if(A && !is_cut(wire)) + A.hacked = FALSE + A.update_tgui_static_data(user) + +/datum/wires/synthesizer/proc/reset_electrify(wire) + var/obj/machinery/synthesizer/A = holder + if(A && !is_cut(wire)) + A.shocked = FALSE + +/datum/wires/synthesizer/proc/reset_disable(wire) + var/obj/machinery/synthesizer/A = holder + if(A && !is_cut(wire)) + A.disabled = FALSE diff --git a/code/game/machinery/frame.dm b/code/game/machinery/frame.dm index 610d5e1404..175f49ceac 100644 --- a/code/game/machinery/frame.dm +++ b/code/game/machinery/frame.dm @@ -24,6 +24,7 @@ GLOBAL_LIST(construction_frame_floor) var/frame_style = FRAME_STYLE_FLOOR // "floor" or "wall" var/x_offset // For wall frames: pixel_x var/y_offset // For wall frames: pixel_y + var/is_dense = TRUE // Lazy workaround for wall-mounted, upgradable machines // Get the icon state to use at a given state. Default implementation is based on the frame's name /datum/frame/frame_types/proc/get_icon_state(state) @@ -217,6 +218,22 @@ GLOBAL_LIST(construction_frame_floor) circuit = /obj/item/circuitboard/injector_maker frame_size = 3 +/datum/frame/frame_types/foodsynthesizer + name = "Food Synthesizer" + icon_override = 'icons/obj/machines/foodsynthesizer.dmi' + frame_class = FRAME_CLASS_MACHINE + frame_size = 5 + frame_style = FRAME_STYLE_WALL + x_offset = 24 + y_offset = 30 + is_dense = FALSE + +/datum/frame/frame_types/foodsynthesizer/mini + name = "Mini Food Synthesizer" + frame_class = FRAME_CLASS_MACHINE + frame_size = 3 // smaller, so slightly fewer sheets + frame_style = FRAME_STYLE_WALL + // Refinery machines /datum/frame/frame_types/industrial_reagent_grinder name = "Industrial Chemical Grinder" @@ -350,7 +367,7 @@ GLOBAL_LIST(construction_frame_floor) if(frame_type.name == "Computer") density = TRUE - if(frame_type.frame_class == FRAME_CLASS_MACHINE) + if(frame_type.frame_class == FRAME_CLASS_MACHINE && frame_type.is_dense) density = TRUE update_icon() diff --git a/code/modules/asset_cache/assets/spritesheets/foodsynth.dm b/code/modules/asset_cache/assets/spritesheets/foodsynth.dm new file mode 100644 index 0000000000..b6bbeb0379 --- /dev/null +++ b/code/modules/asset_cache/assets/spritesheets/foodsynth.dm @@ -0,0 +1,15 @@ +//Let's be optimal with our menu preview icons, shall we? +/datum/asset/spritesheet_batched/synthesizer + name = "synthesizer" + +/datum/asset/spritesheet_batched/synthesizer/create_spritesheets() + for(var/datum/category_item/synthesizer/path as anything in subtypesof(/datum/category_item/synthesizer)) + var/datum/universal_icon/icon = get_display_icon_for(path::build_path) + + if(!icon || !icon.icon_file) + continue + + icon.scale(128, 128) //enbiggen for the menu UI + + var/key = sanitize_css_class_name("[path::type]") + insert_icon(key, icon) diff --git a/code/modules/client/preference_setup/general/11_misc.dm b/code/modules/client/preference_setup/general/11_misc.dm index e2e78aeea5..c3aef661a5 100644 --- a/code/modules/client/preference_setup/general/11_misc.dm +++ b/code/modules/client/preference_setup/general/11_misc.dm @@ -1,10 +1,10 @@ // Define a place to save in character setup /datum/preferences - var/vantag_volunteer = 0 // What state I want to be in, in terms of being affected by antags. + var/vantag_volunteer = FALSE // What state I want to be in, in terms of being affected by antags. var/vantag_preference = VANTAG_NONE // Whether I'd like to volunteer to be an antag at some point. - var/resleeve_lock = 0 // Whether movs should have OOC reslieving protection. Default false. - var/resleeve_scan = 1 // Whether mob should start with a pre-spawn body scan. Default true. - var/mind_scan = 1 // Whether mob should start with a pre-spawn mind scan. Default true. + var/resleeve_lock = FALSE // Whether movs should have OOC reslieving protection. Default false. + var/resleeve_scan = TRUE // Whether mob should start with a pre-spawn body scan. Default true. + var/mind_scan = TRUE // Whether mob should start with a pre-spawn mind scan. Default true. var/custom_species // Custom species name, can't be changed due to it having been used in savefiles already. @@ -95,7 +95,7 @@ var/want_body_save = pref.resleeve_scan var/want_mind_save = pref.mind_scan - spawn(50) + spawn(5 SECONDS) if(QDELETED(character) || QDELETED(pref)) return // They might have been deleted during the wait if(!character.virtual_reality_mob && !(/mob/living/carbon/human/proc/perform_exit_vr in character.verbs)) //Janky fix to prevent resleeving VR avatars but beats refactoring transcore @@ -155,6 +155,7 @@ data["borg_petting"] = pref.borg_petting data["ignore_shoes"] = pref.read_preference(/datum/preference/toggle/human/ignore_shoes) + data["synth_cookie"] = pref.read_preference(/datum/preference/toggle/living/foodsynth_cookies) data["resleeve_lock"] = pref.resleeve_lock data["resleeve_scan"] = pref.resleeve_scan @@ -236,6 +237,9 @@ if("toggle_resleeve_scan") pref.resleeve_scan = pref.resleeve_scan ? 0 : 1; return TOPIC_REFRESH + if("toggle_synth_cookie") + pref.update_preference_by_type(/datum/preference/toggle/living/foodsynth_cookies, !pref.read_preference(/datum/preference/toggle/living/foodsynth_cookies)) + return TOPIC_REFRESH if("toggle_mind_scan") pref.mind_scan = pref.mind_scan ? 0 : 1; return TOPIC_REFRESH diff --git a/code/modules/client/preferences/types/character/general/11_misc.dm b/code/modules/client/preferences/types/character/general/11_misc.dm index 53e183b689..2984218213 100644 --- a/code/modules/client/preferences/types/character/general/11_misc.dm +++ b/code/modules/client/preferences/types/character/general/11_misc.dm @@ -7,3 +7,14 @@ /datum/preference/toggle/human/ignore_shoes/apply_to_human(mob/living/carbon/human/target, value) return + +// Whether mob can be printed as a snack. requires body scan. Default false. +/datum/preference/toggle/living/foodsynth_cookies + category = PREFERENCE_CATEGORY_MANUALLY_RENDERED + savefile_key = "vore_fsynth_cookie" + default_value = FALSE + savefile_identifier = PREFERENCE_CHARACTER + can_randomize = FALSE + +/datum/preference/toggle/living/foodsynth_cookies/apply_to_living(mob/living/target, value) + return diff --git a/code/modules/food/kitchen/cooking_machines/foodsynthesizer.dm b/code/modules/food/kitchen/cooking_machines/foodsynthesizer.dm new file mode 100644 index 0000000000..92dd0e4879 --- /dev/null +++ b/code/modules/food/kitchen/cooking_machines/foodsynthesizer.dm @@ -0,0 +1,715 @@ +//Poojy's miracle 'I don't want generic pizza' / there's noone working kitchen machine +//Yes it's a generic food 3d printer. ~ +// in here because makes sense, if really it's just a refillable autolathe of food + +#define SYNTH_FOOD_COST 5 + +/obj/machinery/synthesizer + name = "Food Synthesizer" + desc = "Sabresnacks brand device able to produce an incredible array of conventional foods. Although only the most ascetic of users claim it produces truly good tasting products." + icon = 'icons/obj/machines/foodsynthesizer.dmi' + icon_state = "synthesizer" + density = FALSE + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 10 + active_power_usage = 2000 + clicksound = "keyboard" + clickvol = 30 + + var/hacked = FALSE + var/disabled = FALSE + var/shocked = FALSE + var/busy = FALSE + var/usage_amt = SYNTH_FOOD_COST + + light_system = STATIC_LIGHT + light_range = 3 + light_power = 1 + light_on = FALSE + + var/menu_grade //how tasty is it? + var/speed_grade //how fast can it be? + var/filtertext + + circuit = /obj/item/circuitboard/synthesizer + + //loaded cartridge + var/obj/item/reagent_containers/synthdispcart/cart = /obj/item/reagent_containers/synthdispcart + var/cart_type = ITEMSIZE_LARGE + + //all of our food + var/static/datum/category_collection/synthesizer/synthesizer_recipes + var/active_menu = "appasnacc" + var/activefood + + //Voice activation stuff + var/activator = "computer" + var/list/voicephrase + + //crew printing required stuff. + var/tgui_icons + var/icon/crewpicture + var/activecrew + var/refresh_delay = 10 SECONDS + +/obj/machinery/synthesizer/Initialize(mapload) + . = ..() + if(!synthesizer_recipes) + synthesizer_recipes = new() + cart = new cart(src) + wires = new/datum/wires/synthesizer(src) + default_apply_parts() + update_icon() + + if(!pixel_x && !pixel_y) + offset_synth() + +/obj/machinery/synthesizer/mini + name = "Mini Food Synthesizer" + icon_state = "portsynth" + cart_type = ITEMSIZE_NORMAL + circuit = /obj/item/circuitboard/synthesizer/mini + cart = /obj/item/reagent_containers/synthdispcart/small + +/obj/machinery/synthesizer/Destroy() + QDEL_NULL(wires) + + if(cart) + QDEL_NULL(cart) + + clear_tgui_icons() + return ..() + +/obj/machinery/synthesizer/examine(mob/user) + . = ..() + if(panel_open) + . += "A cartridge is [cart ? "installed" : "missing"]." + if(cart && (!(stat & (NOPOWER|BROKEN)))) + var/obj/item/reagent_containers/synthdispcart/C = cart + if(istype(C) && C.reagents && C.reagents.total_volume) + var/percent = round((C.reagents.total_volume / C.volume) * 100) + . += "The installed cartridge has [percent]% remaining." + +//offsets to make the machine squish to a wall. They're all south facing so it looks weird but every other direction is AWFUL +/obj/machinery/synthesizer/proc/offset_synth() + pixel_x = (dir & 3) ? 0 : (dir == 4 ? -24 : 24) + pixel_y = (dir & 3) ? (dir == 1 ? -22 : 30) : 0 + + +// TGUI +// Crew Cookie backend stuff... I can't even fuckin' believe this is Janicart stuff +/obj/machinery/synthesizer/proc/set_tgui_icon(mob/L) + if(!isliving(L)) + return + if(ishuman(L)) + crewpicture = getFlatIcon(L, defdir = SOUTH, no_anim = TRUE, force_south = TRUE) + tgui_icons = "'data:image/png;base64,[icon2base64(crewpicture)]'" + + else //Simple animals, Silicons, etc don't have records, so we'll just grab their current state. + var/icon/F = getFlatIcon(L, defdir = SOUTH, no_anim = TRUE, force_south = TRUE) + crewpicture = F + tgui_icons = "'data:image/png;base64,[icon2base64(F)]'" + SStgui.update_uis(src) + +/obj/machinery/synthesizer/proc/clear_tgui_icons() + tgui_icons = null + crewpicture = null + +// And literally this is all that's needed for conventional meals. lmao. +/obj/machinery/synthesizer/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet_batched/synthesizer), + ) + +/obj/machinery/synthesizer/tgui_interact(mob/user, datum/tgui/ui) + if(stat & (BROKEN|NOPOWER)) + return + + if(shocked) + shock(user, 100) + + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "FoodSynthesizer") + ui.open() + +/obj/machinery/synthesizer/tgui_status(mob/user) + if(disabled) + return STATUS_CLOSE + return ..() + +/obj/machinery/synthesizer/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = list( + "busy" = busy, + "isThereCart" = cart, + "active_menu" = active_menu, + "activefood" = activefood, + "activecrew" = activecrew, + "crewicon" = tgui_icons + ) + + if(cart) + var/percent = round((cart.reagents.total_volume / cart.reagents.maximum_volume) * 100) + data["cartFillStatus"] = percent + return data + +/obj/machinery/synthesizer/tgui_static_data(mob/user) + var/list/data = ..() + var/list/menucatagories = list() + for(var/datum/category_group/synthesizer/menulist in synthesizer_recipes.categories) + var/list/recipes = list() + for(var/datum/category_item/synthesizer/food in menulist.items) + UNTYPED_LIST_ADD(recipes, list( + "name" = food.name, + "type" = food.type, + "desc" = food.desc, + "hidden" = food.hidden, + "ref" = text_ref(food) + )) + UNTYPED_LIST_ADD(menucatagories, list( + "name" = menulist.name, + "id" = menulist.id, + "sortorder" = menulist.sortorder, + "ref" = text_ref(menulist), + "recipes" = recipes + )) + + data["menucatagories"] = menucatagories + + var/list/crew_cookies = list() + for(var/client/C in GLOB.clients) + // Check if the client allows crew cookies in their currently toggled prefs. Clients must be ingame to even be in GLOB.clients, so logged out players won't show up here. + if(!C.prefs?.read_preference(/datum/preference/toggle/living/foodsynth_cookies)) + continue + + var/name = null + var/species = null + + if(ishuman(C.mob)) + var/mob/living/carbon/human/H = C.mob + //Utilize the body records for humans to avoid metagaming problems (EX: using the cookie printer to check if someone is ghosting from the main menu) + var/datum/transcore_db/db = SStranscore.db_by_key() + if(db) + var/datum/transhuman/body_record/b_rec = db.body_scans[H.mind.name] + if(!b_rec) //extra check to make sure people have a body record, and no-one will immediately on start. + continue + name = H.real_name + species = "[H.custom_species ? H.custom_species : H.species.name]" + + if(issilicon(C.mob)) + if(isAI(C.mob)) + var/mob/living/silicon/ai/A = C.mob + name = A.name + species = "Artificial Intelligence" + + if(isrobot(C.mob)) + var/mob/living/silicon/robot/R = C.mob + if(R.scrambledcodes || (R.module && R.module.hide_on_manifest)) //Not sure if admeme events want valid cookie print outs + continue + name = R.name + species = "[R.modtype] [R.braintype]" + + if(isanimal(C.mob)) + var/mob/living/simple_mob/SM = C.mob + name = SM.name + species = initial(SM.name) //most mobs are simply named the species they are, so this ought to be useful for named critters. + + if(!name) + continue + + // our crew cookies are only applicable on the crew menu, and we're reusing our category sorting as much as possible! + crew_cookies.Add(list(list( + "category" = "crew", + "name" = name, + "species" = species + ))) + + data["crew_cookies"] = crew_cookies + return data + +/obj/machinery/synthesizer/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + if(stat & (BROKEN|NOPOWER)) + return FALSE + + if(ui.user.stat || ui.user.restrained()) + return FALSE + + add_fingerprint(ui.user) + + if(busy) + to_chat(ui.user, span_notice("The synthesizer is busy. Please wait for completion of previous operation.")) + return FALSE + + switch(action) + if("setactive_menu") + if(active_menu == params["setactive_menu"]) + return FALSE + active_menu = params["setactive_menu"] + activecrew = null + activefood = null + if(tgui_icons) + clear_tgui_icons() + return TRUE + + if("setactive_food") + if(activefood == params["setactive_food"]) + return FALSE + activefood = params["setactive_food"] + return TRUE + + if("setactive_crew") + if(activecrew == params["setactive_crew"]) + return FALSE + activecrew = params["setactive_crew"] + if(tgui_icons) + clear_tgui_icons() + + var/mob/found + for(var/mob/living/L in GLOB.player_list) + if(L.real_name == activecrew) + found = L + break + + if(!found) + return FALSE + + if(!get_mob_for_picture(ui.user, found)) + return FALSE + set_tgui_icon(found) + return TRUE + + if("make") + var/datum/category_item/synthesizer/making = locate(params["make"]) + if(!istype(making)) + return + if(making.hidden && !hacked) + return + + //Check if we still have the materials. + var/obj/item/reagent_containers/synthdispcart/C = cart + if(check_cart(C, ui.user)) + //Sanity check. + if(!making || !src) + return + busy = TRUE + update_use_power(USE_POWER_ACTIVE) + update_icon() // light up time + C.reagents.remove_reagent(REAGENT_ID_NUTRIPASTE_SOYLENT, SYNTH_FOOD_COST) //Drain our fuel + var/obj/item/reagent_containers/food/snacks/food_mimic = new making.build_path(src) //Let's get this on a tray + addtimer(CALLBACK(src, PROC_REF(finish_production), food_mimic, ui.user), speed_grade, TIMER_DELETE_ME) + return TRUE + + audible_message(span_notice("Error: Insufficent Materials. SabreSnacks recommends you have a genuine replacement cartridge available to install."), runemessage = "Error: Insufficent Materials!") + return FALSE + + if("refresh") + update_tgui_static_data(ui.user, ui) + return TRUE + + if("crewprint") + var/active_crew = params["crewprint"] + var/mob/found + for(var/mob/living/L in GLOB.player_list) + if(L.real_name == active_crew) + found = L + break + + if(found && !get_mob_for_picture(ui.user, found)) //verify that we can still print this person + to_chat(ui.user, "Warning: Invalid selection.") + return FALSE + + //Check if we still have the materials. + var/obj/item/reagent_containers/synthdispcart/C = cart + if(check_cart(C, ui.user)) + //Sanity check. + busy = TRUE + update_use_power(USE_POWER_ACTIVE) + update_icon() // light up time + C.reagents.remove_reagent(REAGENT_ID_NUTRIPASTE_SOYLENT, SYNTH_FOOD_COST) //Drain our fuel + sleep(speed_grade) //machine go brrr + + //Create the cookie base. + var/obj/item/reagent_containers/food/snacks/synthsized_meal/crewblock/meal = new /obj/item/reagent_containers/food/snacks/synthsized_meal/crewblock(loc) + + //Begin mimicking the micro + var/vore_flavor + if(found?.vore_taste) + vore_flavor = found.vore_taste + else + vore_flavor = "Something impalpable" + + meal.name = found.real_name + meal.desc = "A tiny replica of a crewmate!" + var/icon/F = crewpicture + F.Scale(16, 16) //Half size + meal.icon = F + meal.icon_state = null + + //flavor mixing, make the cookie taste somewhat like the real thing! + for(var/datum/reagent/foodpaste in meal.reagents.reagent_list) + if(foodpaste.id == REAGENT_ID_NUTRIPASTE) //This should be the only reagent, actually. + foodpaste.taste_description += " as well as [vore_flavor]" + foodpaste.data = list(foodpaste.taste_description = 1) + meal.nutriment_desc = list(foodpaste.taste_description = 1) + + if(menu_grade >= 2) //Is the machine upgraded? + meal.reagents.add_reagent(REAGENT_ID_NUTRIPASTE, ((1 + menu_grade) - 1)) //add the missing Nutriment bonus, subtracting the one we've already added in. + + audible_message(span_notice("Please take your miniature [meal.name]."), runemessage = "Minature [meal.name] is complete!") + if(Adjacent(ui.user)) + ui.user.put_in_any_hand_if_possible(meal) //Autoplace in hands to save a click + else + meal.forceMove(get_turf(src)) //otherwise we anti-clump layer onto the floor + meal.randpixel_xy() + busy = FALSE + update_icon() //turn off lights, please. + else + audible_message(span_notice("Error: Insufficent Materials. SabreSnacks recommends you have a genuine replacement cartridge available to install."), runemessage = "Error: Insufficent Materials!") + return FALSE + return TRUE + +/obj/machinery/synthesizer/proc/finish_production(obj/item/reagent_containers/food/snacks/food_mimic, mob/user) + //Create the desired item. + var/obj/item/reagent_containers/food/snacks/synthsized_meal/meal = new /obj/item/reagent_containers/food/snacks/synthsized_meal(loc) + + //Begin mimicking the food + meal.name = food_mimic.name + meal.desc = food_mimic.desc + meal.icon = food_mimic.icon + meal.icon_state = food_mimic.icon_state + meal.center_of_mass_x = food_mimic.center_of_mass_x + meal.center_of_mass_y = food_mimic.center_of_mass_y + + //flavor mixing + var/taste_output = food_mimic.reagents.generate_taste_message() + for(var/datum/reagent/F in meal.reagents.reagent_list) + if(F.id == REAGENT_ID_NUTRIPASTE) //This should be the only reagent, actually. + F.taste_description += " as well as [taste_output]" + F.data = list(F.taste_description = 1) + meal.nutriment_desc = list(F.taste_description = 1) + + if(menu_grade >= 2) //Is the machine upgraded? + meal.reagents.add_reagent(REAGENT_ID_NUTRIPASTE, ((1 + menu_grade) - 1)) //add the missing Nutriment bonus, subtracting the one we've already added in. + + meal.bitesize = food_mimic?.bitesize //suffer your aerogel like 1 Nutriment turkey, nerds. + meal.filling_color = food_mimic?.filling_color + meal.trash = food_mimic?.trash //If this can lead to exploits then we'll remove it, but I like the idea. + qdel(food_mimic) + audible_message(span_notice("Please take your [meal.name]."), runemessage = "[meal.name] is complete!") + if(Adjacent(user)) + user.put_in_any_hand_if_possible(meal) //Autoplace in hands to save a click + else + meal.forceMove(get_turf(src)) //otherwise we anti-clump layer onto the floor + meal.randpixel_xy() + busy = FALSE + update_icon() //turn off lights, please. + +/obj/machinery/synthesizer/update_icon() + cut_overlays() + set_light_on(FALSE) + icon_state = initial(icon_state) //we use this to reduce code bloat. It's nice. + if(panel_open) //add service panels just above our machine + if(!(stat & (NOPOWER|BROKEN))) + add_overlay("[initial(icon_state)]_ppanel") + else + add_overlay("[initial(icon_state)]_panel") + if(cart) + var/obj/item/reagent_containers/synthdispcart/C = cart + if(C.reagents && C.reagents.total_volume) + var/image/filling_overlay = image("[icon]", src, "[initial(icon_state)]fill_0") //Modular filling + var/percent = round((C.reagents.total_volume / C.volume) * 100) + switch(percent) + if(0 to 9) filling_overlay.icon_state = "[initial(icon_state)]fill_0" + if(10 to 35) filling_overlay.icon_state = "[initial(icon_state)]fill_25" + if(36 to 74) filling_overlay.icon_state = "[initial(icon_state)]fill_50" + if(75 to 90) filling_overlay.icon_state = "[initial(icon_state)]fill_75" + if(91 to INFINITY) filling_overlay.icon_state = "[initial(icon_state)]fill_100" + filling_overlay.color = C.reagents.get_color() + //Add our filling, if any. + add_overlay(filling_overlay) + //Then add our cart so the filling is inside of the canister. + add_overlay("[initial(icon_state)]_cart") + return //don't stack additional panel screen states, please. + + if((stat && BROKEN)) + icon_state = "[initial(icon_state)]x" + return + + if(stat & NOPOWER) + return + + if(!cart) + add_overlay("[initial(icon_state)]_error") + return + + if(cart && !busy) + var/obj/item/reagent_containers/synthdispcart/C = cart + if(C.reagents && C.reagents.total_volume) + add_overlay("[initial(icon_state)]_on") + else + add_overlay("[initial(icon_state)]_error") + return + + if(busy) + add_overlay("[initial(icon_state)]_busy") + set_light_color("#faebd7") // "antique white" + set_light_on(TRUE) + +//Cartridge Interactions in Machine +/obj/machinery/synthesizer/proc/add_cart(obj/item/C, mob/user) + if(!Adjacent(user)) + return //How did you even try? + if(!panel_open) //just in case + to_chat(user, "The hatch must be open to insert a [C].") + return + if(cart) // let's hot swap that bad boy. + remove_cart(user) + return + + user.drop_from_inventory(C) + cart = C + C.forceMove(src) + C.add_fingerprint(user) + to_chat(user, span_notice("You add [C] to \the [src].")) + update_icon() + SStgui.update_uis(src) + return + +/obj/machinery/synthesizer/proc/remove_cart(mob/user) + var/obj/item/reagent_containers/synthdispcart/C = cart + if(!C) + to_chat(user, span_notice("There's no cartridge here...")) //Sanity checks aren't ever a bad thing + return + if(!Adjacent(user)) //gotta, y'know, be in touch range to pull a physical canister out + return + C.forceMove(get_turf(loc)) + C.update_icon() + cart = null + to_chat(user, span_notice("You remove [C] from \the [src].")) + + if(Adjacent(user)) + user.put_in_hands(C) //pick up your trash, nerd. and don't hand it to the AI. They will be upset. + update_icon() + SStgui.update_uis(src) + +/obj/machinery/synthesizer/proc/check_cart(obj/item/reagent_containers/synthdispcart/C, mob/user) + if(!istype(C)) + to_chat(user, span_notice("The synthesizer cartridge is nonexistant.")) + return FALSE + if((!(C.reagents)) || (C.reagents.total_volume <= 0) || (!C.reagents.has_reagent(REAGENT_ID_NUTRIPASTE_SOYLENT))) + to_chat(user, span_notice("The synthesizer cartridge depleted, replace with a genuine Sabresnack Co cartridge.")) + return FALSE + if((C.reagents) && (C.reagents.total_volume <= 0) && (!C.reagents.has_reagent(REAGENT_ID_NUTRIPASTE_SOYLENT))) + to_chat(user, span_notice("Used or Counterfeit synthesizer cartridge detected.")) + return FALSE + else if(C.reagents && C.reagents.has_reagent(REAGENT_ID_NUTRIPASTE_SOYLENT) && (C.reagents.total_volume >= SYNTH_FOOD_COST)) + SStgui.update_uis(src) + return TRUE + +/obj/machinery/synthesizer/proc/get_mob_for_picture(mob/user, mob/living/LM) + if(!istype(LM)) + to_chat(user, span_warning("Warning: Invalid selection. Please refresh the database.")) + return FALSE + + if(LM.client?.prefs?.read_preference(/datum/preference/toggle/living/foodsynth_cookies)) + return TRUE + + to_chat(user, span_warning("Warning: Invalid selection. Please refresh the database.")) + return FALSE + +/obj/machinery/synthesizer/attackby(obj/item/W, mob/user) + if(busy) + audible_message(span_notice("\The [src] is busy. Please wait for completion of previous operation."), runemessage = "The Synthesizer is busy.") + return + if(default_part_replacement(user, W)) + return + if(stat) + update_icon() + return + if(W.is_screwdriver()) + panel_open = !panel_open + playsound(src, W.usesound, 50, 1) + user.visible_message(span_notice("[user] [panel_open ? "opens" : "closes"] the hatch on the [src]."), span_notice("You [panel_open ? "open" : "close"] the hatch on the [src].")) + update_icon() + return + if(panel_open) + if(istype(W, /obj/item/reagent_containers/synthdispcart)) + if(!anchored) + to_chat(user, span_warning("Anchor its bolts first.")) + return + if(W.w_class > cart_type) //since we confirmed it's a Cart, make sure it fits! + to_chat(user, span_warning("\The [src] only accepts smaller synthiziser cartridges.")) + return + if(cart) + var/choice = alert(user, "Replace the loaded cartridge?", "", "Yes", "Cancel") + switch(choice) + if("Cancel") + return FALSE + if("Yes") + add_cart(W, user) + else + add_cart(W, user) + + if(W.is_wrench()) + playsound(src, W.usesound, 50, 1) + to_chat(user, span_notice("You begin to [anchored ? "un" : ""]fasten \the [src].")) + if (do_after(user, 20 * W.toolspeed)) + user.visible_message( + span_notice("\The [user] [anchored ? "un" : ""]fastens \the [src]."), + span_notice("You have [anchored ? "un" : ""]fastened \the [src]."), + "You hear a ratchet.") + anchored = !anchored + update_icon() + else + to_chat(user, span_notice("You decide not to [anchored ? "un" : ""]fasten \the [src].")) + + if(default_deconstruction_crowbar(user, W)) + return + + return ..() + +/obj/machinery/synthesizer/attack_hand(mob/user as mob) + if(stat & (BROKEN|NOPOWER)) + return + if(!panel_open) + tgui_interact(user) + else if(panel_open) + if(cart) + var/choice = alert(user, "Removing the Cartridge?", "", "Yes", "Cancel", "Wires Menu") + switch(choice) + if("Cancel") + return FALSE + if("Yes") + remove_cart(user) + if("Wires Menu") + wires.Interact(user) + else + wires.Interact(user) + +/obj/machinery/synthesizer/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/synthesizer/interact(mob/user) + if(panel_open) + return wires.Interact(user) + + if(disabled) + to_chat(user, span_danger("\The [src] is disabled!")) + return + + if(shocked) + shock(user, 50) + + tgui_interact(user) + +//Updates performance +/obj/machinery/synthesizer/RefreshParts() + ..() + menu_grade = 0 + speed_grade = 0 + + for(var/obj/item/stock_parts/manipulator/M in component_parts) + speed_grade = (10 SECONDS) / M.rating //let's try to make it worthwhile to upgrade 'em 10s, 5s, 3.3s, 2.5s + for(var/obj/item/stock_parts/scanning_module/S in component_parts) + menu_grade = S.rating //how much bonus Nutriment is added to the printed food. the regular wafer is only 5. + // Science parts will be of help if they bother. + +#undef SYNTH_FOOD_COST +//Cartridge Item handling +/obj/item/reagent_containers/synthdispcart + name = "Synthesizer cartridge" + desc = "Genuine replacement cartridge for SabreSnacks brand Food Synthesizers. It's too large for the Portable models." + icon = 'icons/obj/machines/foodsynthesizer.dmi' + icon_state = "bigcart" + + w_class = ITEMSIZE_LARGE + + volume = 250 //enough for feeding folk, but not so much it won't be needing replacment + //No way to refill or drain them. Like printer ink, it's only good in the machine! + amount_per_transfer_from_this = 0 + max_transfer_amount = 0 + min_transfer_amount = 0 + flags = NONE + +/obj/item/reagent_containers/synthdispcart/small + name = "Portable Synthesizer Cartridge" + desc = "Genuine replacement cartrifge SabreSnacks brand Portable Food Synthesizers. It can also fit within standard sized models." + icon_state = "Scart" + w_class = ITEMSIZE_NORMAL + volume = 100 + +/obj/item/reagent_containers/synthdispcart/Initialize(mapload) + . = ..() + reagents.add_reagent(REAGENT_ID_NUTRIPASTE_SOYLENT, volume) + update_icon() + +/obj/item/reagent_containers/synthdispcart/update_icon() + cut_overlays() + if(reagents.total_volume) + var/image/filling_overlay = image("[icon]", src, "[initial(icon_state)]fill_0", layer = layer - 0.1) + var/percent = round((reagents.total_volume / volume) * 100) + switch(percent) + if(0 to 9) filling_overlay.icon_state = "[initial(icon_state)]fill_0" + if(10 to 35) filling_overlay.icon_state = "[initial(icon_state)]fill_25" + if(36 to 74) filling_overlay.icon_state = "[initial(icon_state)]fill_50" + if(75 to 90) filling_overlay.icon_state = "[initial(icon_state)]fill_75" + if(91 to 100) filling_overlay.icon_state = "[initial(icon_state)]fill_100" + if(100 to INFINITY) filling_overlay.icon_state = "[initial(icon_state)]fill_100" + filling_overlay.color = reagents.get_color() + add_overlay(filling_overlay) + +/obj/item/reagent_containers/synthdispcart/examine(mob/user) + . = ..() + if(reagents && reagents.total_volume) + var/percent = round((reagents.total_volume / volume) * 100) + . += "The cartridge has [percent]% remaining." + +/obj/item/reagent_containers/synthdispcart/is_open_container() + return FALSE //sealed, proprietary container. aka preventing alternative beaker memes. + +//Circuits for contruction + +/datum/design_techweb/board/synthesizer + SET_CIRCUIT_DESIGN_NAMEDESC("food synthesizer") + id = "food_synthesizer" + // req_tech = list(TECH_DATA = 3, TECH_BLUESPACE = 3, TECH_ENGINEERING = 2) + build_path = /obj/item/circuitboard/synthesizer + category = list( + RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_KITCHEN + ) + departmental_flags = DEPARTMENT_BITFLAG_SERVICE | DEPARTMENT_BITFLAG_SCIENCE + +/datum/design_techweb/board/synthesizer/mini + SET_CIRCUIT_DESIGN_NAMEDESC("compact food synthesizer") + id = "compactfood_synthesizer" + // req_tech = list(TECH_DATA = 3, TECH_BLUESPACE = 3, TECH_ENGINEERING = 2) + build_path = /obj/item/circuitboard/synthesizer/mini + category = list( + RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_KITCHEN + ) + departmental_flags = DEPARTMENT_BITFLAG_SERVICE | DEPARTMENT_BITFLAG_SCIENCE + +// Physical Boards for Food Synthesizer Construction +/obj/item/circuitboard/synthesizer + name = T_BOARD("Food Synthesizer") + desc = "The circuitboard for a Food Synthesizer." + build_path = /obj/machinery/synthesizer + board_type = new /datum/frame/frame_types/foodsynthesizer + matter = list(MAT_STEEL = 50, MAT_GLASS = 50) + req_components = list( + /obj/item/stock_parts/manipulator = 1, + /obj/item/stock_parts/scanning_module = 1) + +/obj/item/circuitboard/synthesizer/mini + name = T_BOARD("Compact Food Synthesizer") + desc = "The circuitboard for a compact food synthesizer." + build_path = /obj/machinery/synthesizer/mini + board_type = new /datum/frame/frame_types/foodsynthesizer/mini + matter = list(MAT_STEEL = 50, MAT_GLASS = 50) + req_components = list( + /obj/item/stock_parts/manipulator = 1, + /obj/item/stock_parts/scanning_module = 1) diff --git a/code/modules/food/recipes_foodsynthesizer.dm b/code/modules/food/recipes_foodsynthesizer.dm new file mode 100644 index 0000000000..1e128477f9 --- /dev/null +++ b/code/modules/food/recipes_foodsynthesizer.dm @@ -0,0 +1,1150 @@ +#define VOICE_TEMP_COLD "cold" +#define VOICE_TEMP_HOT "hot" + +/datum/category_item/synthesizer/New() + ..() + var/obj/item/reagent_containers/food/snacks/snacc = build_path + if(!snacc) // Something has gone horribly wrong, or right. + log_game("[name] created an Synthesizer design without an assigned build_path.") + return + desc = initial(snacc.desc) //Let's get our description text + +/********************* +* Synthed Food Setup * +**********************/ +/obj/item/reagent_containers/food/snacks/synthsized_meal + name = "Nutrient paste wafer" + desc = "It's a synthisized edible wafer of nutrients. Everything you need and makes field rations a delicacy in comparison." + icon = 'icons/obj/machines/foodsynthesizer.dmi' + icon_state = "pasteblock" + filling_color = "#c5e384" + center_of_mass_x = 16 + center_of_mass_y = 6 + w_class = ITEMSIZE_SMALL + bitesize = 5 + +/obj/item/reagent_containers/food/snacks/synthsized_meal/Initialize(mapload) + . = ..() + reagents.add_reagent(REAGENT_ID_NUTRIPASTE, 5) + +/obj/item/reagent_containers/food/snacks/synthsized_meal/crewblock + name = "Crew paste block" + desc = "It's a synthisized edible wafer of nutrients. Everything you need and makes field rations a delicacy in comparison." + icon_state = "crewblock" + bitesize = 30 //one chomp. bitesize = amount taken per bite? I guess? weird. + eating_sound = 'sound/vore/sunesound/pred/swallow_01.ogg' //hehe + +/datum/reagent/nutriment/synthmealgoop + name = REAGENT_NUTRIPASTE + id = REAGENT_ID_NUTRIPASTE + description = "a revoltingly bland paste of nutrition." + taste_description = "undefinable blandness" //This gets updated with our printed food's taste descriptor + taste_mult = 1 + nutriment_factor = 6 // 2/3rds the power of real Nutriment (10). for balance. + reagent_state = SOLID + color = "#c5e384" + +//gotta make the fuel a thing, might as well make it horrid, amirite. Should only be a cargo import. Shouldn't be aquirable! +/datum/reagent/nutriment/synthsoylent + name = REAGENT_NUTRIPASTE_SOYLENT + id = REAGENT_ID_NUTRIPASTE_SOYLENT + description = "An thick, horridly rubbery fluid that somehow can be synthisized into 'edible' meals." + taste_description = "unrefined cloying oil" + taste_mult = 1.3 + nutriment_factor = 1 + reagent_state = LIQUID + color = "#4b0082" + +//Supply pack refills +/datum/supply_pack/vending_refills/synthesizer + contains = list(/obj/item/reagent_containers/synthdispcart) + name = "Food Synthesizer Cartridge (Standard)" + cost = 20 //pricy so chef value is ever better. + containername = "food synthesizer cartridge crate" + +/datum/supply_pack/vending_refills/synthesizer/smol + contains = list(/obj/item/reagent_containers/synthdispcart/small) + name = "Food Synthesizer Cartridge (Portable)" + cost = 10 + containername = "portable food synthesizer cartridge crate" + +/**************************** +* Category Collection Setup * +****************************/ + +/datum/category_collection/synthesizer + category_group_type = /datum/category_group/synthesizer + +/************* +* Categories * +*************/ +#define MENU_SNACC 0 +#define MENU_BREKKIE 1 +#define MENU_LONCH 2 +#define MENU_DINNAH 3 +#define MENU_DESLUT 4 +#define MENU_EROTIC 5 +#define MENU_RHAWH 6 +#define MENU_MEHARTY 7 + +/datum/category_group/synthesizer + var/id + var/sortorder + +/datum/category_group/synthesizer/New() + ..() + +/datum/category_group/synthesizer/appasnack + name = "Appetizers" + id = "appasnacc" + sortorder = MENU_SNACC + category_item_type = /datum/category_item/synthesizer/appasnack + +/datum/category_group/synthesizer/breakfastmenu + name = "Breakfasts" + id = "breakfast" + sortorder = MENU_BREKKIE + category_item_type = /datum/category_item/synthesizer/breakfastmenu + +/datum/category_group/synthesizer/lunchmenu + name = "Lunches" + id = "lunch" + sortorder = MENU_LONCH + category_item_type = /datum/category_item/synthesizer/lunchmenu + +/datum/category_group/synthesizer/dinnermenu + name = "Dinners" + id = "dinner" + sortorder = MENU_DINNAH + category_item_type = /datum/category_item/synthesizer/dinnermenu + +/datum/category_group/synthesizer/dessertmenu + name = "Desserts" + id = "dessert" + sortorder = MENU_DESLUT + category_item_type = /datum/category_item/synthesizer/dessert + +/datum/category_group/synthesizer/exoticmenu + name = "Exotics" + id = "exotic" + sortorder = MENU_EROTIC + category_item_type = /datum/category_item/synthesizer/exotic + +/datum/category_group/synthesizer/rawmenu + name = "Raw Offerings" + id = "raw" + sortorder = MENU_RHAWH + category_item_type = /datum/category_item/synthesizer/raw + +/datum/category_group/synthesizer/crewmenu + name = "Crew Cookies" + id = "crew" + sortorder = MENU_MEHARTY + category_item_type = /datum/category_item/synthesizer/crew + +#undef MENU_SNACC +#undef MENU_BREKKIE +#undef MENU_LONCH +#undef MENU_DINNAH +#undef MENU_DESLUT +#undef MENU_EROTIC +#undef MENU_RHAWH +#undef MENU_MEHARTY + +/******************* +* Category entries * +*******************/ + +/datum/category_item/synthesizer + var/desc //food description to be applied to the UI. + var/build_path //food item build_path + var/hidden = FALSE //is it illegal/nonstandard? + var/list/voice_order //what can we say to get this? Avoid exact same phrases. + var/voice_temp //mostly flavor but maybe also setting stuff that will warm/cool you off? idk + +/********* +* Snacks * +**********/ + +/datum/category_item/synthesizer/appasnack/popcorn + name = "Popcorn" + build_path = /obj/item/reagent_containers/food/snacks/popcorn + voice_order = list("popcorn", "corn of popping") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/appasnack/nuggies + name = "Chicken Nugget" + build_path = /obj/item/reagent_containers/food/snacks/nugget + voice_order = list("chimkin nuggy", "chicken nugget") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/appasnack/flatbread + name = "Flatbread" + build_path = /obj/item/reagent_containers/food/snacks/flatbread + voice_order = list("flatbread") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/appasnack/tortilla + name = "Flour Tortilla" + build_path = /obj/item/reagent_containers/food/snacks/tortilla + voice_order = list("tortilla", "flour tortilla") + voice_temp = VOICE_TEMP_HOT //imagine liking *cold* Tortillas. Ugh. It offends my Texan sensibilites. + +/datum/category_item/synthesizer/appasnack/bun + name = "Burger Bun" + build_path = /obj/item/reagent_containers/food/snacks/bun + voice_order = list("Burger bun", "plain bun") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/appasnack/eggroll + name = "Egg Roll" + build_path = /obj/item/reagent_containers/food/snacks/eggroll + voice_order = list("egg roll", "happy ending") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/appasnack/friedmushroom + name = "Fried Mushroom" + build_path = /obj/item/reagent_containers/food/snacks/friedmushroom + voice_order = list("fried mushroom","fried cap") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/appasnack/watermelonslice + name = "Watermelon Slice" + build_path = /obj/item/reagent_containers/food/snacks/watermelonslice + voice_order = list("watermelon slice") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/appasnack/truffle + name = "Candy (Truffle)" + build_path = /obj/item/reagent_containers/food/snacks/truffle + voice_order = list("chocolate truffle") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/appasnack/mint + name = "Candy (Mint)" + build_path = /obj/item/reagent_containers/food/snacks/mint + voice_order = list("candy mints", "mint candy") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/appasnack/candybar + name = "Candy (Bar)" + build_path = /obj/item/reagent_containers/food/snacks/candy + voice_order = list("candy bar", "chocolate bar") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/appasnack/proteinbar + name = "Candy (Protein)" + build_path = /obj/item/reagent_containers/food/snacks/candy/proteinbar + voice_order = list("protein candy bar", "protein bar") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/appasnack/cb10 + name = "Candy (Nutty)" + build_path = /obj/item/reagent_containers/food/snacks/cb10 + voice_order = list("shantak","shantak bar", "chocolate nut bar") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/appasnack/skrellsnacks + name = "Skrell Snacks" + build_path = /obj/item/reagent_containers/food/snacks/skrellsnacks + voice_order = list("skrell snacks", "skrell snacc", "skrell treats") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/appasnack/admints + name = "Admints" + build_path = /obj/item/reagent_containers/food/snacks/mint/admints + voice_order = list("admints", "admin mints") + voice_temp = VOICE_TEMP_COLD + +/********* +* Breakfast * +**********/ + +/datum/category_item/synthesizer/breakfastmenu/muffin + name = "Muffin (Plain)" + build_path = /obj/item/reagent_containers/food/snacks/muffin + voice_order = list("plain muffin", "bald cupcake") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/breakfastmenu/bagelplain + name = "Bagel (Plain)" + build_path = /obj/item/reagent_containers/food/snacks/bagelplain + voice_order = list("bagel", "plain bagel") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/breakfastmenu/bagelsunflower + name = "Bagel (Sunflower)" + build_path = /obj/item/reagent_containers/food/snacks/bagelsunflower + voice_order = list("sunflower bagel", "bagel sunflower", "bagel with sunflower") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/breakfastmenu/bagelcheese + name = "Bagel (Cheese)" + build_path = /obj/item/reagent_containers/food/snacks/bagelcheese + voice_order = list("cream cheese bagel","cheese bagel", "bagel with cheese") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/breakfastmenu/bagelraisin + name = "Bagel (Raisin)" + build_path = /obj/item/reagent_containers/food/snacks/bagelraisin + voice_order = list("raisin bagel", "raisen bagel", "bagel with raisins", "bagel with raisens") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/breakfastmenu/bagelpoppy + name = "Bagel (Poppyseed)" + build_path = /obj/item/reagent_containers/food/snacks/bagelpoppy + voice_order = list("poppyseed bagel", "bagel with poppyseeds") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/breakfastmenu/croissant + name = "Croissant" + build_path = /obj/item/reagent_containers/food/snacks/croissant + voice_order = list("croissant", "crossant") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/breakfastmenu/pancakes + name = "Pancakes" + build_path = /obj/item/reagent_containers/food/snacks/pancakes + voice_order = list("plain pancakes") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/breakfastmenu/berrypancake + name = "Berry Pancakes" + build_path = /obj/item/reagent_containers/food/snacks/pancakes/berry + voice_order = list("berry pancakes", "pancakes with berries") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/breakfastmenu/ntmuffin + name = "Dwarven Breakfast Muffin" + build_path = /obj/item/reagent_containers/food/snacks/nt_muffin + voice_order = list("breakfast muffin", "nt muffin") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/breakfastmenu/breakfastburrito + name = "Breakfast Burrito" + build_path = /obj/item/reagent_containers/food/snacks/breakfast_wrap + voice_order = list("Breakfast burrito") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/breakfastmenu/quicheslice + name = "Quiche Slice" + build_path = /obj/item/reagent_containers/food/snacks/quicheslice/filled + voice_order = list("quiche") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/breakfastmenu/poachedegg + name = "Egg (Poached)" + build_path = /obj/item/reagent_containers/food/snacks/poachedegg + voice_order = list("poached egg") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/breakfastmenu/boiledegg + name = "Egg (Boiled)" + build_path = /obj/item/reagent_containers/food/snacks/boiledegg + voice_order = list("boiled egg") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/breakfastmenu/friedegg + name = "Egg (Fried)" + build_path = /obj/item/reagent_containers/food/snacks/friedegg + voice_order = list("fried egg", "egg well") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/breakfastmenu/chiliedegg + name = "Egg (Chilied)" + build_path = /obj/item/reagent_containers/food/snacks/chilied_eggs + voice_order = list("redeemed eggs", "chili eggs") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/breakfastmenu/bacon + name = "Bacon wafer" + build_path = /obj/item/reagent_containers/food/snacks/bacon_stick + voice_order = list("bacon stick", "stick of bacon") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/breakfastmenu/ovenbacon + name = "Oven-baked bacon" + build_path = /obj/item/reagent_containers/food/snacks/bacon/oven + voice_order = list("oven bacon", "epic bacon") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/breakfastmenu/eggbacon + name = "Bacon and Eggs" + build_path = /obj/item/reagent_containers/food/snacks/bacon_and_eggs + voice_order = list("bacon and eggs", "eggs and bacon") + voice_temp = VOICE_TEMP_HOT + +/******** +* Lunch * +*********/ + +/datum/category_item/synthesizer/lunchmenu/blt + name = "BLT sandwich" + build_path = /obj/item/reagent_containers/food/snacks/blt + voice_order = list("blt sandwich", "sandwich blt") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/lunchmenu/genericsandwich + name = "Sandvich" + build_path = /obj/item/reagent_containers/food/snacks/sandwich + voice_order = list("lunchmenu sandwich", "sandwich") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/lunchmenu/meatbreadslice + name = "Meat bread (Slice)" + build_path = /obj/item/reagent_containers/food/snacks/slice/meatbread/filled + voice_order = list("meat bread slice", "slice of meat bread") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/lunchmenu/tofubreadslice + name = "Tofu bread (Slice)" + build_path = /obj/item/reagent_containers/food/snacks/slice/tofubread/filled + voice_order = list("tofu bread slice", "slice of tofu bread") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/lunchmenu/creamcheesebreadslice + name = "Cream cheese bread (slice)" + build_path = /obj/item/reagent_containers/food/snacks/slice/creamcheesebread/filled + voice_order = list("cream cheese bread slice", "slice of cream cheese bread") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/lunchmenu/margheritaslice + name = "Margherita Pizza (Slice)" + build_path = /obj/item/reagent_containers/food/snacks/slice/margherita/filled + voice_order = list("margherita pizza", "neapolitan pizza") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/lunchmenu/meatpizzaslice + name = "Meat Lovers Pizza (Slice)" + build_path = /obj/item/reagent_containers/food/snacks/slice/meatpizza/filled + voice_order = list("meat pizza", "meat lovers pizza", "pizza with meat") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/lunchmenu/mushroompizzasilce + name = "Mushroom Pizza (Slice)" + build_path = /obj/item/reagent_containers/food/snacks/slice/mushroompizza/filled + voice_order = list("mushroom pizza", "pizza with mushroom") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/lunchmenu/veggiepizzaslice + name = "Vegetable Pizza (Slice)" + build_path = /obj/item/reagent_containers/food/snacks/slice/vegetablepizza/filled + voice_order = list("veggie pizza", "vegetable pizza", "pizza with veggies", "pizza with vegetable") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/lunchmenu/pineapplepizzaslice + name = "Pineapple Pizza (Slice)" + build_path = /obj/item/reagent_containers/food/snacks/pineappleslice/filled + voice_order = list("pineapple pizza", "pizza with pineapple", "abomination pizza", "best pizza") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/lunchmenu/chickensandwich + name = "Chicken Fillet Sandwich" + build_path = /obj/item/reagent_containers/food/snacks/chickenfillet + voice_order = list("Chicken sandwich", "Chimkin sammich", "Chimkin sandwich") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/lunchmenu/corndog + name = "County Fair Corndog" + build_path = /obj/item/reagent_containers/food/snacks/corn_dog + voice_order = list("corndog", "hotdog on a stick") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/lunchmenu/ovenfries + name = "Oven baked fries" + build_path = /obj/item/reagent_containers/food/snacks/ovenfries + voice_order = list("oven fries") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/lunchmenu/bread + name = "Bread Loaf" + build_path = /obj/item/reagent_containers/food/snacks/sliceable/bread + voice_order = list("bread loaf", "loaf of bread") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/lunchmenu/baguette + name = "Baguette" + build_path = /obj/item/reagent_containers/food/snacks/baguette + voice_order = list("baguette", "french bread") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/lunchmenu/tofubread + name = "Tofu Bread Loaf" + build_path = /obj/item/reagent_containers/food/snacks/sliceable/tofubread + voice_order = list("tofu bread loaf") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/lunchmenu/macncheese + name = "Mac and Cheese" + build_path = /obj/item/reagent_containers/food/snacks/macncheese + voice_order = list("Mac n cheese", "macaroni and cheese", "mac and cheese") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/lunchmenu/chickenmomo + name = "Chicken MoMo" + build_path = /obj/item/reagent_containers/food/snacks/chickenmomo + voice_order = list("chicken momo", "chicken dumplings") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/lunchmenu/veggiemomo + name = "Veggie MoMo" + build_path = /obj/item/reagent_containers/food/snacks/veggiemomo + voice_order = list("veggie momo", "veggie dumplings", "vegetarian dumplings") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/lunchmenu/eggpancake + name = "Burger Omelette" + build_path = /obj/item/reagent_containers/food/snacks/egg_pancake + voice_order = list("egg pancake", "burger omelette") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/lunchmenu/mysteryburrito + name = "Burrito (Mystery Meat)" + build_path = /obj/item/reagent_containers/food/snacks/burrito_mystery + voice_order = list("mystery burrito") + voice_temp = VOICE_TEMP_HOT + hidden = TRUE + +/datum/category_item/synthesizer/lunchmenu/fuegoburrito + name = "Burrito (Fuego Phoron)" + build_path = /obj/item/reagent_containers/food/snacks/fuegoburrito + voice_order = list("fire veggie burrito", "fuego phoron burrito") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/lunchmenu/meatburrito + name = "Burrito (carne asada)" + build_path = /obj/item/reagent_containers/food/snacks/meatburrito + voice_order = list("steak burrito", "carne asada burrito") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/lunchmenu/cheeseburrito + name = "Burrito (Cheese)" + build_path = /obj/item/reagent_containers/food/snacks/cheeseburrito + voice_order = list("cheese burrito", "burrito with cheese") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/lunchmenu/burrito + name = "Burrito (Generic)" + build_path = /obj/item/reagent_containers/food/snacks/burrito + voice_order = list("plain burrito") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/lunchmenu/spicyburrito + name = "Burrito (Spicy)" + build_path = /obj/item/reagent_containers/food/snacks/burrito_spicy + voice_order = list("spicy burrito") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/lunchmenu/meatcheeseburrito + name = "Burrito (Carne Queso)" + build_path = /obj/item/reagent_containers/food/snacks/burrito_cheese + voice_order = list("meat and cheese burrito", "carne queso burrito") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/lunchmenu/spicycheeseburrito + name = "Burrito (Spicy Cheese)" + build_path = /obj/item/reagent_containers/food/snacks/burrito_cheese_spicy + voice_order = list("spicy cheese burrito", "calente queso burrito") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/lunchmenu/veganburrito + name = "Burrito (Vegan)" + build_path = /obj/item/reagent_containers/food/snacks/burrito_vegan + voice_order = list("veggie burrito", "vegan burrito") + voice_temp = VOICE_TEMP_HOT + +/********* +* Dinner * +**********/ + +//American Continents +/datum/category_item/synthesizer/dinnermenu/ribplate + name = "Rib plate" + build_path = /obj/item/reagent_containers/food/snacks/ribplate + voice_order = list("plate of ribs", "rib plate") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/baconburger + name = "Burger (Bacon)" + build_path = /obj/item/reagent_containers/food/snacks/burger/bacon + voice_order = list("bacon burger", "burger with bacon") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/taco + name = "Taco" + build_path = /obj/item/reagent_containers/food/snacks/taco + voice_order = list("regular taco") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/fishtaco + name = "Fish Taco" + build_path = /obj/item/reagent_containers/food/snacks/fish_taco + voice_order = list("fish taco") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/enchiladas + name = "Enchiladas" + build_path = /obj/item/reagent_containers/food/snacks/enchiladas + voice_order = list("enchiladas") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/turkey + name = "Turkey (Whole)" + build_path = /obj/item/reagent_containers/food/snacks/sliceable/turkey + voice_order = list("turkey", "whole turkey") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/tofurkey + name = "Tofurkey (Whole)" + build_path = /obj/item/reagent_containers/food/snacks/tofurkey + voice_order = list("tofurkey", "tofu turkey", "whole tofurkey") + voice_temp = VOICE_TEMP_HOT + +//European + +/datum/category_item/synthesizer/dinnermenu/loadedpotato + name = "Loaded Baked Potato" + build_path = /obj/item/reagent_containers/food/snacks/loadedbakedpotato + voice_order = list("loaded potato", "baked loaded potato", "loaded baked potato") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/britishpotatos + name = "Bangers and Mash" + build_path = /obj/item/reagent_containers/food/snacks/bangersandmash + voice_order = list("bangers and mash", "sausage and potatoes") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/cheesemash + name = "Cheesy Mashed Potato" + build_path = /obj/item/reagent_containers/food/snacks/cheesymash + voice_order = list("cheese potatoes", "cheese mashed potatoes") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/zestfish + name = "Zesty Fish" + build_path = /obj/item/reagent_containers/food/snacks/zestfish + voice_order = list("zesty fish", "fish dinner") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/meatpie + name = "Pie (Meat)" + build_path = /obj/item/reagent_containers/food/snacks/meatpie + voice_order = list("meat pie", "pie of meat") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/tofupie + name = "Pie (Tofu)" + build_path = /obj/item/reagent_containers/food/snacks/tofupie + voice_order = list("tofu pie", "pie of tofu") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/plumppie + name = "Pie (Mushroom)" + build_path = /obj/item/reagent_containers/food/snacks/plump_pie + voice_order = list("mushroom pie", "plump hemlet pie") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/lasagna + name = "Lasagne" + build_path = /obj/item/reagent_containers/food/snacks/lasagna + voice_order = list("lasagne", "garfield special") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/baconflatbread + name = "Bacon Flatbread" + build_path = /obj/item/reagent_containers/food/snacks/bacon_flatbread + voice_order = list("bacon flatbread", "bacon with flatbread") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/meatpocket + name = "Meat and Cheese Flatbread" + build_path = /obj/item/reagent_containers/food/snacks/meat_pocket + voice_order = list("meat and cheese flatbread") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/risotto + name = "Risotto" + build_path = /obj/item/reagent_containers/food/snacks/risotto + voice_order = list("bowl of risotto", "regular risotto") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/risottoballs + name = "Risotto Balls" + build_path = /obj/item/reagent_containers/food/snacks/risottoballs + voice_order = list("risotto balls") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/stuffedmeatball + name = "Stuffed Meatball" + build_path = /obj/item/reagent_containers/food/snacks/stuffed_meatball + voice_order = list("stuffed meatball") + voice_temp = VOICE_TEMP_HOT + +//Asian + +/datum/category_item/synthesizer/dinnermenu/redcurry + name = "Red Curry" + build_path = /obj/item/reagent_containers/food/snacks/redcurry + voice_order = list("red curry", "curry red") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/greencurry + name = "Green Curry" + build_path = /obj/item/reagent_containers/food/snacks/greencurry + voice_order = list("green curry", "curry green") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/yellowcurry + name = "Yellow Curry" + build_path = /obj/item/reagent_containers/food/snacks/yellowcurry + voice_order = list("yellow curry", "curry yellow") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/bibimbap + name = "Bibimbap Bowl" //The best thing. Seriously. + build_path = /obj/item/reagent_containers/food/snacks/bibimbap + voice_order = list("bibimbap", "korean hot pot") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/lomein + name = "Lo Mein" + build_path = /obj/item/reagent_containers/food/snacks/lomein + voice_order = list("lo mein", "low main", "chinese noodles") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/friedrice + name = "Fried Rice (Plain)" + build_path = /obj/item/reagent_containers/food/snacks/friedrice + voice_order = list("egg fried rice", "plain fried rice") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/porkbowl + name = "Fried Rice (Pork)" + build_path = /obj/item/reagent_containers/food/snacks/porkbowl + voice_order = list("pork bowl", "pork fried rice") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/sweetnsour + name = "Sweet and Sour Pork" + build_path = /obj/item/reagent_containers/food/snacks/sweet_and_sour + voice_order = list("sweet and sour pork", "sweet sour pork") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/meatbun + name = "Meat and Leaf Bun" + build_path = /obj/item/reagent_containers/food/snacks/meatbun + voice_order = list("baozi", "meat and leaf dumpling", "meat and leaf bun") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/spicedmeatbun + name = "Spiced Meat Bun" + build_path = /obj/item/reagent_containers/food/snacks/spicedmeatbun + voice_order = list("spiced meat bun", "char sui", "char sui bun") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/eggrice + name = "Omelette Rice (regular)" + build_path = /obj/item/reagent_containers/food/snacks/omurice + voice_order = list("regular omelette rice", "regular omurice") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/eggriceheart + name = "Omelette Rice (heart)" + build_path = /obj/item/reagent_containers/food/snacks/omurice/heart + voice_order = list("omelette rice with heart", "omurice heart", "omurice with heart", "heart omurice") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dinnermenu/eggriceface + name = "Omelette Rice (face)" + build_path = /obj/item/reagent_containers/food/snacks/omurice/face + voice_order = list("omelette rice with face", "omurice face", "omurice with face", "face omurice") + voice_temp = VOICE_TEMP_HOT + +/********** +* Dessert * +***********/ + +/datum/category_item/synthesizer/dessert/donut + name = "Donut (Plain)" + build_path = /obj/item/reagent_containers/food/snacks/donut/plain + voice_order = list("plain donut", "donut with nothing") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/dessert/chocolate_donut + name = "Donut (Chocolate)" + build_path = /obj/item/reagent_containers/food/snacks/donut/choc + voice_order = list("chocolate donut", "donut with chocolate") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/dessert/pink_donut + name = "Donut (Pink Frosting)" + build_path = /obj/item/reagent_containers/food/snacks/donut/pink + voice_order = list("pink frosted donut", "donut with pink frosting") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/dessert/homer + name = "Donut (Sprinkles)" + build_path = /obj/item/reagent_containers/food/snacks/donut/homer + voice_order = list("homer donut", "donut with sprinkles") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/dessert/jelly_donut + name = "Donut (Jelly)" + build_path = /obj/item/reagent_containers/food/snacks/donut/plain/jelly + voice_order = list("jelly donut", "donut with jelly") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/dessert/meat_donut + name = "Donut (Meat)" + build_path = /obj/item/reagent_containers/food/snacks/donut/meat + voice_order = list("meat donut", "donut with meat") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/dessert/chaos_donut + name = "Donut (Chaos)" + build_path = /obj/item/reagent_containers/food/snacks/donut/plain + voice_order = list("chaos donut", "donut with chaos", "madness donut") + voice_temp = VOICE_TEMP_COLD + hidden = TRUE + +/datum/category_item/synthesizer/dessert/candyapple + name = "Candied Apple" + build_path = /obj/item/reagent_containers/food/snacks/candiedapple + voice_order = list("candy apple", "candied apple") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/dessert/applepie + name = "Pie (Apple)" + build_path = /obj/item/reagent_containers/food/snacks/applepie + voice_order = list("apple pie", "pie with apple", "American pie") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dessert/cherrypie + name = "Pie (Cherry)" + build_path = /obj/item/reagent_containers/food/snacks/cherrypie + voice_order = list("cherry pie", "pie with cherry") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dessert/appletart + name = "Apple Tart" + build_path = /obj/item/reagent_containers/food/snacks/appletart + voice_order = list("apple tart") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dessert/cinnamonbun + name = "cinnamon bun" + build_path = /obj/item/reagent_containers/food/snacks/cinnamonbun + voice_order = list("Cinnamon bun","sweetroll", "cinnabun") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dessert/tastybread + name = "Tubular Sweet Bread" + build_path = /obj/item/reagent_containers/food/snacks/tastybread + voice_order = list("bread tube") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/dessert/carrotcake + name = "Carrot Cake" + build_path = /obj/item/reagent_containers/food/snacks/slice/carrotcake/filled + voice_order = list("carrot cake") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/dessert/cheesecake + name = "Cheesecake" + build_path = /obj/item/reagent_containers/food/snacks/slice/cheesecake/filled + voice_order = list("cheese cake") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/dessert/plaincake + name = "Plain Cake" + build_path = /obj/item/reagent_containers/food/snacks/slice/plaincake/filled + voice_order = list("plain cake") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/dessert/orangecake + name = "Orange Cake" + build_path = /obj/item/reagent_containers/food/snacks/slice/orangecake/filled + voice_order = list("orange cake") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/dessert/limecake + name = "Lime Cake" + build_path = /obj/item/reagent_containers/food/snacks/slice/limecake/filled + voice_order = list("lime cake") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/dessert/lemoncake + name = "Lemon Cake" + build_path = /obj/item/reagent_containers/food/snacks/slice/lemoncake/filled + voice_order = list("lemon cake") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/dessert/chocolatecake + name = "Chocolate Cake" + build_path = /obj/item/reagent_containers/food/snacks/slice/chocolatecake/filled + voice_order = list("chocolate cake", "mudpie") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/dessert/birthdaycake + name = "Birthday Cake" + build_path = /obj/item/reagent_containers/food/snacks/slice/birthdaycake/filled + voice_order = list("Birthday cake") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/dessert/applecake + name = "Apple Cake" + build_path = /obj/item/reagent_containers/food/snacks/slice/applecake/filled + voice_order = list("apple cake") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/dessert/pumpkinpie + name = "Pumpkin Pie" + build_path = /obj/item/reagent_containers/food/snacks/slice/pumpkinpie/filled + voice_order = list("pumpkin pie") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dessert/keylimepieslice + name = "Key Lime Pie" + build_path = /obj/item/reagent_containers/food/snacks/keylimepieslice/filled + voice_order = list("key lime pie") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/dessert/browniesslice + name = "Brownie" + build_path = /obj/item/reagent_containers/food/snacks/browniesslice/filled + voice_order = list("brownie") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dessert/sugarcookie + name = "Sugar Cookie" + build_path = /obj/item/reagent_containers/food/snacks/sugarcookie + voice_order = list("sugar cookie") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/dessert/custardbun + name = "Custard Bun" + build_path = /obj/item/reagent_containers/food/snacks/custardbun + voice_order = list("custard bun") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/dessert/honeybun + name = "Honey Bun" + build_path = /obj/item/reagent_containers/food/snacks/honeybun + voice_order = list("honey bun") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/dessert/cookie + name = "Cookie" + build_path = /obj/item/reagent_containers/food/snacks/cookie + voice_order = list("plain cookie", "chocolate chip cookie") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dessert/fruitbar + name = "Fruit Bar" + build_path = /obj/item/reagent_containers/food/snacks/fruitbar + voice_order = list("fruit bar") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/dessert/pie + name = "Plain Pie" + build_path = /obj/item/reagent_containers/food/snacks/pie + voice_order = list("plain pie", "nothing pie") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dessert/amanita_pie + name = "Amanita Pie" + build_path = /obj/item/reagent_containers/food/snacks/amanita_pie + voice_order = list("amanita pie", "weed pie") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/dessert/funnelcake + name = "Funnel Cake" + build_path = /obj/item/reagent_containers/food/snacks/funnelcake + voice_order = list("funnel cake", "fair cake") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dessert/icecreamsandwich + name = "Ice Cream Sandwich" + build_path = /obj/item/reagent_containers/food/snacks/icecreamsandwich + voice_order = list("icecream sandwich", "ice cream sandwich", "sandwich with ice cream") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/dessert/pisanggoreng + name = "Pisang Goreng" + build_path = /obj/item/reagent_containers/food/snacks/pisanggoreng + voice_order = list("pisang goreng", "banana fritter") + voice_temp = VOICE_TEMP_HOT + +/********* +* Exotic * +**********/ + +/datum/category_item/synthesizer/exotic/hatchling_suprise + name = "Hatchling Suprise" + build_path = /obj/item/reagent_containers/food/snacks/hatchling_suprise + voice_order = list("hatchling surpise") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/exotic/red_sun_special + name = "Red Sun Special" + build_path = /obj/item/reagent_containers/food/snacks/red_sun_special + voice_order = list() + voice_temp = "" + +/datum/category_item/synthesizer/exotic/riztizkzi_sea + name = "Moghesian Sea Delight" + build_path = /obj/item/reagent_containers/food/snacks/riztizkzi_sea + voice_order = list() + voice_temp = "" + +/datum/category_item/synthesizer/exotic/father_breakfast + name = "Fatherly Breakfast" + build_path = /obj/item/reagent_containers/food/snacks/father_breakfast + voice_order = list() + voice_temp = "" + +/datum/category_item/synthesizer/exotic/bearburger + name = "Bear Burger" + build_path = /obj/item/reagent_containers/food/snacks/bearburger + voice_order = list() + voice_temp = "" + +/datum/category_item/synthesizer/exotic/namagashi + name = "Ryo-kucha Namagashi" + build_path = /obj/item/reagent_containers/food/snacks/namagashi + voice_order = list() + voice_temp = "" + +/datum/category_item/synthesizer/exotic/dionaroast + name = "Diona Roast" + build_path = /obj/item/reagent_containers/food/snacks/dionaroast + voice_order = list("diona roast", "plant roast") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/exotic/burnedmess + name = "Dubious Food" + build_path = /obj/item/reagent_containers/food/snacks/badrecipe + voice_order = list("right proper fuckup", "Whatever", "my college existance", "chef's disappointment", "Ramsey's nightmare") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/exotic/monkeydelight + name = "Monkey's Delight" + build_path = /obj/item/reagent_containers/food/snacks/monkeysdelight + voice_order = list("fancy banana", "monkey delight") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/exotic/xenopie + name = "Pie (Xeno)" + build_path = /obj/item/reagent_containers/food/snacks/xemeatpie + voice_order = list("xeno pie", "pie of xeno") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/exotic/xenobreadslice + name = "Xeno Bread (Slice)" + build_path = /obj/item/reagent_containers/food/snacks/sliceable/xenomeatbread + voice_order = list("meat bread slice", "slice of meat bread") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/exotic/liquidfood + name = "Liquid Ration (Generic)" + build_path = /obj/item/reagent_containers/food/snacks/liquidfood + voice_order = list("Liquid ration", "food drink", "Liquidfood") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/exotic/liquidprotein + name = "Liquid Ration (Protein)" + build_path = /obj/item/reagent_containers/food/snacks/liquidprotein + voice_order = list("protein liquid ration", "protein ration") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/exotic/liquidvitamin + name = "Liquid Ration (Vitamin)" + build_path = /obj/item/reagent_containers/food/snacks/liquidvitamin + voice_order = list("vitamin liquid ration", "vitamin ration") + voice_temp = VOICE_TEMP_COLD + +/***************** + * FOOKIN RAAAWH * + *****************/ + +/datum/category_item/synthesizer/raw/meat + name = "Meat Steak (raw)" + build_path = /obj/item/reagent_containers/food/snacks/meat + voice_order = list("meat slab","slab of meat","raw steak", "raw meat steak") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/raw/bacon + name = "Bacon (raw)" + build_path = /obj/item/reagent_containers/food/snacks/rawbacon + voice_order = list("raw bacon") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/raw/mushroom + name = "Mushroom Slice" + build_path = /obj/item/reagent_containers/food/snacks/mushroomslice + voice_order = list("mushroom slice", "raw mushroom") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/raw/tomato + name = "Tomato Steak (raw)" + build_path = /obj/item/reagent_containers/food/snacks/tomatomeat + voice_order = list("tomato slice") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/raw/bear + name = "Bear Steak (raw)" + build_path = /obj/item/reagent_containers/food/snacks/bearmeat + voice_order = list("raw bear steak", "slice of bear") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/raw/xeno + name = "Xeno Steak (raw)" + build_path = /obj/item/reagent_containers/food/snacks/xenomeat + voice_order = list("raw xeno steak") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/raw/spider + name = "Spider Steak (raw)" + build_path = /obj/item/reagent_containers/food/snacks/xenomeat/spidermeat + voice_order = list("raw spider steak") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/raw/spahgetti + name = "Spaghetti (raw)" + build_path = /obj/item/reagent_containers/food/snacks/spagetti + voice_order = list("raw spaghetti", "uncooked spaghett") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/raw/corgi + name = "Corgi Steak (raw)" + build_path = /obj/item/reagent_containers/food/snacks/meat/corgi + voice_order = list("Dog steak", "Dog", "Canine steak") + voice_temp = VOICE_TEMP_COLD + hidden = TRUE + +/datum/category_item/synthesizer/raw/waferblock + name = "Nutrient paste wafer" + build_path = /obj/item/reagent_containers/food/snacks/synthsized_meal + voice_order = list("paste block") + voice_temp = VOICE_TEMP_COLD + +/datum/category_item/synthesizer/raw/wafercrewblock + name = "Generic Crew Cookie" + build_path = /obj/item/reagent_containers/food/snacks/synthsized_meal/crewblock + voice_order = list("crew block") + voice_temp = VOICE_TEMP_COLD + +/********* +* Crew Cookie * +**********/ +/datum/category_item/synthesizer/crew/crewcookie + name = "Generic Crew Cookie" + build_path = /obj/item/reagent_containers/food/snacks/synthsized_meal/crewblock + voice_order = list("crew cookie") + voice_temp = VOICE_TEMP_HOT + +/datum/category_item/synthesizer/dd_SortValue() + return name + +#undef VOICE_TEMP_COLD +#undef VOICE_TEMP_HOT diff --git a/code/modules/mob/living/carbon/taste.dm b/code/modules/mob/living/carbon/taste.dm index 205a875d7e..bf044d1960 100644 --- a/code/modules/mob/living/carbon/taste.dm +++ b/code/modules/mob/living/carbon/taste.dm @@ -38,7 +38,7 @@ calculate text size per text. for(var/datum/reagent/R in reagent_list) if(!R.taste_mult) continue - if(R.id == REAGENT_ID_NUTRIMENT) //this is ugly but apparently only nutriment (not subtypes) has taste data TODO figure out why + if(R.id == REAGENT_ID_NUTRIMENT || R.id == REAGENT_ID_NUTRIPASTE) //this is ugly but apparently only nutriment (not subtypes) has taste data TODO figure out why var/list/taste_data = R.get_data() for(var/taste in taste_data) if(taste in tastes) diff --git a/code/modules/resleeving/infocore_records.dm b/code/modules/resleeving/infocore_records.dm index 6e16b5e554..7b2f1c07a0 100644 --- a/code/modules/resleeving/infocore_records.dm +++ b/code/modules/resleeving/infocore_records.dm @@ -101,7 +101,7 @@ var/aflags var/breath_type = GAS_O2 -/datum/transhuman/body_record/New(copyfrom, add_to_db = 0, ckeylock = 0) +/datum/transhuman/body_record/New(copyfrom, add_to_db = FALSE, ckeylock = FALSE) ..() if(istype(copyfrom, /datum/transhuman/body_record)) init_from_br(copyfrom) @@ -118,7 +118,7 @@ ..() return QDEL_HINT_HARDDEL // For now at least there is no easy way to clear references to this in GLOB.machines etc. -/datum/transhuman/body_record/proc/init_from_mob(mob/living/carbon/human/M, add_to_db = 0, ckeylock = 0, database_key) +/datum/transhuman/body_record/proc/init_from_mob(mob/living/carbon/human/M, add_to_db = FALSE, ckeylock = FALSE, database_key) ASSERT(!QDELETED(M)) ASSERT(istype(M)) @@ -212,7 +212,6 @@ for(var/datum/modifier/mod as anything in M.modifiers) if(mod.flags & MODIFIER_GENETIC) genetic_modifiers.Add(mod.type) - if(add_to_db) SStranscore.add_body(src, database_key = database_key) diff --git a/icons/obj/machines/foodsynthesizer.dmi b/icons/obj/machines/foodsynthesizer.dmi new file mode 100644 index 0000000000000000000000000000000000000000..e70eb13b2b735592373ead071a42f8bf57246151 GIT binary patch literal 34723 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 T;m{WVT%J6Bs$BNSGVFf=Uu!UG literal 0 HcmV?d00001 diff --git a/tgui/bun.lock b/tgui/bun.lock index 731e55e201..c809ff4c71 100644 --- a/tgui/bun.lock +++ b/tgui/bun.lock @@ -6,8 +6,8 @@ "name": "tgui-workspace", "devDependencies": { "@happy-dom/global-registrator": "^20.10.6", - "@rspack/cli": "^2.1.1", - "@rspack/core": "^2.1.1", + "@rspack/cli": "^2.1.2", + "@rspack/core": "^2.1.2", "@testing-library/react": "^16.3.2", "@types/bun": "^1.3.14", "@types/react": "^19.2.17", @@ -33,7 +33,7 @@ "version": "2.0.0", "dependencies": { "svgo": "^4.0.1", - "svgtofont": "^6.5.2", + "svgtofont": "^6.5.3", }, }, "packages/tgui": { @@ -46,14 +46,14 @@ "es-toolkit": "^1.49.0", "highlight.js": "^11.11.1", "jotai": "^2.20.1", - "js-yaml": "^5.2.0", + "js-yaml": "^5.2.1", "marked": "^18.0.5", "marked-base-url": "^1.1.9", "marked-smartypants": "^1.1.12", "react": "^19.2.7", "react-dom": "^19.2.7", "react-json-tree": "^0.20.0", - "tgui-core": "^6.1.0", + "tgui-core": "^6.1.1", "tgui-dev-server": "workspace:*", }, }, @@ -77,7 +77,7 @@ "react": "^19.2.7", "react-dom": "^19.2.7", "tgui": "workspace:*", - "tgui-core": "^6.1.0", + "tgui-core": "^6.1.1", "tgui-dev-server": "workspace:*", "zod": "^4.4.3", }, @@ -90,7 +90,7 @@ "react": "^19.2.7", "react-dom": "^19.2.7", "tgui": "workspace:*", - "tgui-core": "^6.1.0", + "tgui-core": "^6.1.1", }, }, "packages/tgui-setup": { @@ -143,7 +143,7 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], - "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.5", "", { "dependencies": { "@tybys/wasm-util": "^0.10.2" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q=="], + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.6", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg=="], "@nozbe/microfuzz": ["@nozbe/microfuzz@1.0.0", "", {}, "sha512-XKIg/guk+s1tkPTkHch9hfGOWgsKojT7BqSQddXTppOfVr3SWQhhTCqbgQaPTbppf9gc2kFeG0gpBZZ612UXHA=="], @@ -181,35 +181,35 @@ "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], - "@rspack/binding": ["@rspack/binding@2.1.1", "", { "optionalDependencies": { "@rspack/binding-darwin-arm64": "2.1.1", "@rspack/binding-darwin-x64": "2.1.1", "@rspack/binding-linux-arm64-gnu": "2.1.1", "@rspack/binding-linux-arm64-musl": "2.1.1", "@rspack/binding-linux-riscv64-gnu": "2.1.1", "@rspack/binding-linux-riscv64-musl": "2.1.1", "@rspack/binding-linux-x64-gnu": "2.1.1", "@rspack/binding-linux-x64-musl": "2.1.1", "@rspack/binding-wasm32-wasi": "2.1.1", "@rspack/binding-win32-arm64-msvc": "2.1.1", "@rspack/binding-win32-ia32-msvc": "2.1.1", "@rspack/binding-win32-x64-msvc": "2.1.1" } }, "sha512-6062hZP33fn1w51ClpQnum19GGXl/3eS754xrRYbQ7wwBNZk94HVwfSyiqcNCtY/pB1lsFjnaTKiW/Q+jv0axQ=="], + "@rspack/binding": ["@rspack/binding@2.1.2", "", { "optionalDependencies": { "@rspack/binding-darwin-arm64": "2.1.2", "@rspack/binding-darwin-x64": "2.1.2", "@rspack/binding-linux-arm64-gnu": "2.1.2", "@rspack/binding-linux-arm64-musl": "2.1.2", "@rspack/binding-linux-riscv64-gnu": "2.1.2", "@rspack/binding-linux-riscv64-musl": "2.1.2", "@rspack/binding-linux-x64-gnu": "2.1.2", "@rspack/binding-linux-x64-musl": "2.1.2", "@rspack/binding-wasm32-wasi": "2.1.2", "@rspack/binding-win32-arm64-msvc": "2.1.2", "@rspack/binding-win32-ia32-msvc": "2.1.2", "@rspack/binding-win32-x64-msvc": "2.1.2" } }, "sha512-/mFcRSUW7Pl19KeaBIujJvZYNJQu0wD5D3aa5h+Qcph26v7nmLYlX7eajIHGi8tt2qTZX1lXifw2KLIXKwYaRQ=="], - "@rspack/binding-darwin-arm64": ["@rspack/binding-darwin-arm64@2.1.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-6K8jA3pZphBwLt5DU78wl0IzY1myHrqNSvFVCd9CHa+szlOwv2iMQpZdYdpupIBxwiZ39rrDyQKtoZw7lsAG8w=="], + "@rspack/binding-darwin-arm64": ["@rspack/binding-darwin-arm64@2.1.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-IYcxareUOYJZz+uNMSIwn+iDRiVyjZNOjoxO/zL4OFaPK8Ncrw0ka/9DqL9Gd7OpnAXN1zK3uS8yD0O1yIYI3Q=="], - "@rspack/binding-darwin-x64": ["@rspack/binding-darwin-x64@2.1.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-RdqtEfIbSe5ucLNVvMac1k88UwmL4Gil8uXqlEFcSZ3VC2jTOkecadd+B0RYqJ+PirSInrRmKm1cr0740zppOA=="], + "@rspack/binding-darwin-x64": ["@rspack/binding-darwin-x64@2.1.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-aoifkILvx/XEHyvg8yW57xu95nx7f9f/3ah1+RguHSNKcJMcoCep9VX1Ct1N0ftqg8MC0JUObc7xWL5W14hmjA=="], - "@rspack/binding-linux-arm64-gnu": ["@rspack/binding-linux-arm64-gnu@2.1.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-ROKtqXoLpbZO1vYEcNxQg6COvRhdlJcoib1Z4pzG1nIyctEAUuFmnD7pIvJiVe6M7YuJNXW+1p0BhpptT2XEUQ=="], + "@rspack/binding-linux-arm64-gnu": ["@rspack/binding-linux-arm64-gnu@2.1.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-My4m40tyJSgiCEf3bB2KIEX710q3nZg99LIjy+8Zxgi3oZTkg1bFmFRusFU5U4eN5408zfSqDDGvjDE3Yv7o4w=="], - "@rspack/binding-linux-arm64-musl": ["@rspack/binding-linux-arm64-musl@2.1.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-QdQpG9dK0GfPASjBd/8rUwDNW0ShfFfYJfcoGLNTM6A8EqmgvWwklXDiCVF5dXALbZIksAuRznIKnLDsUoXi9Q=="], + "@rspack/binding-linux-arm64-musl": ["@rspack/binding-linux-arm64-musl@2.1.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-yt+GGWUH7WPE8K97cRc8OpZhH7Pbj1vU+lkvKbDtF/rR8X9a/bJsA/nBqyUV2oBKOVbrp5I8rFZlnDskMqgvKw=="], - "@rspack/binding-linux-riscv64-gnu": ["@rspack/binding-linux-riscv64-gnu@2.1.1", "", { "os": "linux", "cpu": "none" }, "sha512-LGdYCAvblZp2qtHk9UseYftIRyaBzSWqEzd1toaS08sYESXERm+YBbrYKKiyq1yXiU1L7BkhMUxiWc3GCYmKcA=="], + "@rspack/binding-linux-riscv64-gnu": ["@rspack/binding-linux-riscv64-gnu@2.1.2", "", { "os": "linux", "cpu": "none" }, "sha512-uys8Jyw8Z3ralvICbN/L/nZfy5qELIwpOY72rhIqhoDYwFcL4fmMaY7WsvUcJOjCB2rqOcWPaWKuF2oPvo9iDQ=="], - "@rspack/binding-linux-riscv64-musl": ["@rspack/binding-linux-riscv64-musl@2.1.1", "", { "os": "linux", "cpu": "none" }, "sha512-87phiiPGFT1p4gy3Oz6YdNijCPXdjJy43LFdE8AFiZsV4ChFXQPqnVCF2aMRLybYa51A+9wGYlTvxt09yUQUww=="], + "@rspack/binding-linux-riscv64-musl": ["@rspack/binding-linux-riscv64-musl@2.1.2", "", { "os": "linux", "cpu": "none" }, "sha512-JYNVQwqCaRGQWvjHQYzZkIzQiwllMaJwh4Rdu3ww6W2OJcJUqT08sL1pkOtU0iCxT4VUYiRRcp93VGTGpHr8fg=="], - "@rspack/binding-linux-x64-gnu": ["@rspack/binding-linux-x64-gnu@2.1.1", "", { "os": "linux", "cpu": "x64" }, "sha512-om0/PvZzxISsnyz3hdmI3xBi3Qsn6faRGrfp6WJpGHa5JYYaRVWz1MJ9fe+/cQE9ON9r32HpMOeuataCLv+Owg=="], + "@rspack/binding-linux-x64-gnu": ["@rspack/binding-linux-x64-gnu@2.1.2", "", { "os": "linux", "cpu": "x64" }, "sha512-KDoPy0Msf/JLhxgPPrJQzZeB4Qpqd32em8AP5lSW2s6jR5I35dHgAe9xc2A++EQtnSrU4GTn6DBvFC7q84SihQ=="], - "@rspack/binding-linux-x64-musl": ["@rspack/binding-linux-x64-musl@2.1.1", "", { "os": "linux", "cpu": "x64" }, "sha512-YgAyTOM5ZWvWT0Exwcg7QkGUv9PsHT4yIsEyJbH+a99uAKliGMwkHIOP/aJgrCF8G+lTfigY2wyl9cWo4TA4aw=="], + "@rspack/binding-linux-x64-musl": ["@rspack/binding-linux-x64-musl@2.1.2", "", { "os": "linux", "cpu": "x64" }, "sha512-66hWmIGvn4zCKAYXJE9Bp5SNSLYnLFq2Ke/efE+ZtWy43Dd5vk9AAOmThVGBwdwmIxmGtHGCp+cAuS4G0wu0TA=="], - "@rspack/binding-wasm32-wasi": ["@rspack/binding-wasm32-wasi@2.1.1", "", { "dependencies": { "@emnapi/core": "1.11.1", "@emnapi/runtime": "1.11.1", "@napi-rs/wasm-runtime": "1.1.5" }, "cpu": "none" }, "sha512-OicgaB3Dcd+KXsH/I3DkJG7XQyREJScg/jLkCtycFn9BhT0IVJvgi37F8QNJKl8Bp9YZIGehsqK9HpQWav6SLw=="], + "@rspack/binding-wasm32-wasi": ["@rspack/binding-wasm32-wasi@2.1.2", "", { "dependencies": { "@emnapi/core": "1.11.1", "@emnapi/runtime": "1.11.1", "@napi-rs/wasm-runtime": "1.1.6" }, "cpu": "none" }, "sha512-EB4SqH8DW/E/OmqssNQvnIVGQiVUyYNlA/pcc6Ia4MlTNwu6eNDppcNLrToH+kSZpL4CpHSFfSM3eIsSuar2Rw=="], - "@rspack/binding-win32-arm64-msvc": ["@rspack/binding-win32-arm64-msvc@2.1.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-fwL4P3SsC0r/vVGgnmexnc4vkvf+9vWMcGw5fdlPBJNR/zWlZRSXR5V6eXcHrpYVLhBsLYvZYE3NZa9paIUGxw=="], + "@rspack/binding-win32-arm64-msvc": ["@rspack/binding-win32-arm64-msvc@2.1.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-T6Fs/g32MRja/UpCq4AdyPRj8tA0cOkcEa4PrAcn/ztUgK8b/qMVxj5mhMI+n7k+kHZQnpeB1Q4HqdSJi6OocA=="], - "@rspack/binding-win32-ia32-msvc": ["@rspack/binding-win32-ia32-msvc@2.1.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-j3BISXbjPyI57HeHxW9Jx+UP7RebNQ4t62uCbRP29caf9aghYJP+ZA5nJ2X7pol7N5Je2/V6WNahuNd0zQFDOA=="], + "@rspack/binding-win32-ia32-msvc": ["@rspack/binding-win32-ia32-msvc@2.1.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-OtxkFVz14mVL4QK8QriSELn9B6PaYGHw1jGJwVDEzpu2ZxSHCTQPz9dVE1ekYtREEqZUkRU7Fp7VfhJSmjTt2Q=="], - "@rspack/binding-win32-x64-msvc": ["@rspack/binding-win32-x64-msvc@2.1.1", "", { "os": "win32", "cpu": "x64" }, "sha512-aPNNFuZT5iBM8+S9J4P5ywJbf3tUvZN3Ppe6mXJ8/jTVDUDhe0YVj3AgD/AuxnUvl+yHrLfQkN1nNwUfIcomfA=="], + "@rspack/binding-win32-x64-msvc": ["@rspack/binding-win32-x64-msvc@2.1.2", "", { "os": "win32", "cpu": "x64" }, "sha512-Am+nx9fLF3nzgD/K05Bs1Bb+WO8SFLWAYRbXkymaL1r+RQxjRj7jd5ap2PhGOCcfaNA4yVWkAFvmFP92eRu7bQ=="], - "@rspack/cli": ["@rspack/cli@2.1.1", "", { "peerDependencies": { "@rspack/core": "^2.0.0-0", "@rspack/dev-server": "^2.0.0-0" }, "optionalPeers": ["@rspack/dev-server"], "bin": { "rspack": "./bin/rspack.js" } }, "sha512-zsJFurwCDesy0dud7cejObagbbXsvKcQ6j485nmnytBsXV17Uf6XfedQMxnOXBZeGIel+I7Jl/Jp3Sn08TsZuw=="], + "@rspack/cli": ["@rspack/cli@2.1.2", "", { "peerDependencies": { "@rspack/core": "^2.0.0-0", "@rspack/dev-server": "^2.0.0-0" }, "optionalPeers": ["@rspack/dev-server"], "bin": { "rspack": "./bin/rspack.js" } }, "sha512-GTBsKuaeqDii6NaGPkQwvycMSjqMUMMRGiAwH0sXsFIn8X7sFa4xpoL7pIhbebVBDGzYhUBqWzNQFuL5u2FeoQ=="], - "@rspack/core": ["@rspack/core@2.1.1", "", { "dependencies": { "@rspack/binding": "2.1.1" }, "peerDependencies": { "@module-federation/runtime-tools": "^0.24.1 || ^2.0.0", "@swc/helpers": "^0.5.23" }, "optionalPeers": ["@module-federation/runtime-tools", "@swc/helpers"] }, "sha512-Rgz6xZcD0rm7ejZhYNi13qvW2dSnIQQt/7D3xbYoT5hOU838JbkF0P3SAMFGKE+FyLZswnyRpxGfnYHZQqDmJA=="], + "@rspack/core": ["@rspack/core@2.1.2", "", { "dependencies": { "@rspack/binding": "2.1.2" }, "peerDependencies": { "@module-federation/runtime-tools": "^0.24.1 || ^2.0.0", "@swc/helpers": "^0.5.23" }, "optionalPeers": ["@module-federation/runtime-tools", "@swc/helpers"] }, "sha512-crpNQKhHfnzrIl4Sa4fjH30Ho5aAPgyqpmJZ41SkUFOzyKHdZKYfE5LF3CMh7MiFQFPPxiiKf5BcpxmtZZx4MQ=="], "@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="], @@ -507,7 +507,7 @@ "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - "js-yaml": ["js-yaml@5.2.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.mjs" } }, "sha512-YeLUMlvR4Ou1B119LIaM0r65JvbOBooJDc9yEu0dClb/uSC5P4FrLU8OCCz/HXWvtPoIrR0dRzABTjo1sTN9Bw=="], + "js-yaml": ["js-yaml@5.2.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.mjs" } }, "sha512-zfLtNfQqxVqq3uaTqSkh4x4hZw3KHobGUA0fJUj4wawW8bsQLTVqpHdXSIzidh7o+4lEW36tANuAGdaFx6Zgnw=="], "jsonc-eslint-parser": ["jsonc-eslint-parser@2.4.2", "", { "dependencies": { "acorn": "^8.5.0", "eslint-visitor-keys": "^3.0.0", "espree": "^9.0.0", "semver": "^7.3.5" } }, "sha512-1e4qoRgnn448pRuMvKGsFFymUCquZV0mpGgOyIKNgD3JVDTsVJyRBGH/Fm0tBb8WsWGgmB1mDe6/yJMQM37DUA=="], @@ -609,7 +609,7 @@ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - "picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + "picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], "pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="], @@ -757,7 +757,7 @@ "svgpath": ["svgpath@2.6.0", "", {}, "sha512-OIWR6bKzXvdXYyO4DK/UWa1VA1JeKq8E+0ug2DG98Y/vOmMpfZNj+TIG988HjfYSqtcy/hFOtZq/n/j5GSESNg=="], - "svgtofont": ["svgtofont@6.5.2", "", { "dependencies": { "auto-config-loader": "^2.0.0", "cheerio": "~1.0.0", "colors-cli": "~1.0.28", "fs-extra": "~11.2.0", "image2uri": "^2.1.2", "nunjucks": "^3.2.4", "svg2ttf": "~6.1.0", "svgicons2svgfont": "~15.0.0", "svgo": "~3.3.0", "ttf2eot": "~3.1.0", "ttf2woff": "~3.0.0", "ttf2woff2": "~8.0.0", "yargs": "^17.7.2" }, "peerDependencies": { "@types/svg2ttf": "~5.0.1" }, "optionalPeers": ["@types/svg2ttf"], "bin": { "svgtofont": "lib/cli.js" } }, "sha512-D9aqybvt3yXIi4RLPbPDXwX9fvoTKN8HeiBeRA7Yb+5TtfdF9Bz9/TGZHEu+hlRtAmlZ3YBBluElfy9BMitcoA=="], + "svgtofont": ["svgtofont@6.5.3", "", { "dependencies": { "auto-config-loader": "^2.0.0", "cheerio": "~1.0.0", "colors-cli": "~1.0.28", "fs-extra": "~11.2.0", "image2uri": "^2.1.2", "nunjucks": "^3.2.4", "svg2ttf": "~6.1.0", "svgicons2svgfont": "~15.0.0", "svgo": "~3.3.0", "ttf2eot": "~3.1.0", "ttf2woff": "~3.0.0", "ttf2woff2": "~8.0.0", "yargs": "^17.7.2" }, "peerDependencies": { "@types/svg2ttf": "~5.0.1" }, "optionalPeers": ["@types/svg2ttf"], "bin": { "svgtofont": "lib/cli.js" } }, "sha512-koKMwoA+olMx7qY2LzdVF1+5/y0c1Tu1iZinTCClF4c5UVrSpExayMoyHqLB/58aTe5EUuE6htg5S2Y/MN4Azw=="], "sync-child-process": ["sync-child-process@1.0.2", "", { "dependencies": { "sync-message-port": "^1.0.0" } }, "sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA=="], @@ -773,7 +773,7 @@ "tgui": ["tgui@workspace:packages/tgui"], - "tgui-core": ["tgui-core@6.1.0", "", { "dependencies": { "@floating-ui/react": "^0.27.16", "@nozbe/microfuzz": "^1.0.0" }, "peerDependencies": { "react": "^19.1.0", "react-dom": "^19.1.0" } }, "sha512-j512gHwvK8aPILIxS26LLHMRpO/EXn5pIiIdGphpqbDIe6ir3SpXDK+wfuEZ/5LxXlP6UQCxSSduR3JI8frIIg=="], + "tgui-core": ["tgui-core@6.1.1", "", { "dependencies": { "@floating-ui/react": "^0.27.16", "@nozbe/microfuzz": "^1.0.0" }, "peerDependencies": { "react": "^19.1.0", "react-dom": "^19.1.0" } }, "sha512-RiINJPsKLvaC4qkKtPdwoe9t08bTuUW8bpHDEIv3P6eZYJZfNSJwPy/hvdiVSeBA1V4ZtDTd+XADWsUzUVlMZA=="], "tgui-dev-server": ["tgui-dev-server@workspace:packages/tgui-dev-server"], @@ -861,7 +861,7 @@ "@npmcli/agent/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], - "@parcel/watcher/picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], + "anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], "cacache/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], @@ -893,6 +893,8 @@ "parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], + "readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + "sass/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], "socks-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], @@ -909,8 +911,6 @@ "terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], - "tinyglobby/picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], - "wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], diff --git a/tgui/package.json b/tgui/package.json index 472e8963b4..05b3f08d73 100644 --- a/tgui/package.json +++ b/tgui/package.json @@ -4,8 +4,8 @@ "type": "module", "devDependencies": { "@happy-dom/global-registrator": "^20.10.6", - "@rspack/cli": "^2.1.1", - "@rspack/core": "^2.1.1", + "@rspack/cli": "^2.1.2", + "@rspack/core": "^2.1.2", "@testing-library/react": "^16.3.2", "@types/bun": "^1.3.14", "@types/react": "^19.2.17", diff --git a/tgui/packages/tgfont/package.json b/tgui/packages/tgfont/package.json index 6734261534..5b8cd2ee5c 100644 --- a/tgui/packages/tgfont/package.json +++ b/tgui/packages/tgfont/package.json @@ -8,6 +8,6 @@ }, "dependencies": { "svgo": "^4.0.1", - "svgtofont": "^6.5.2" + "svgtofont": "^6.5.3" } } diff --git a/tgui/packages/tgui-panel/package.json b/tgui/packages/tgui-panel/package.json index d7e19a918c..7b6e1964ed 100644 --- a/tgui/packages/tgui-panel/package.json +++ b/tgui/packages/tgui-panel/package.json @@ -9,7 +9,7 @@ "react": "^19.2.7", "react-dom": "^19.2.7", "tgui": "workspace:*", - "tgui-core": "^6.1.0", + "tgui-core": "^6.1.1", "tgui-dev-server": "workspace:*", "zod": "^4.4.3" }, diff --git a/tgui/packages/tgui-say/package.json b/tgui/packages/tgui-say/package.json index af0f719675..4db11b002e 100644 --- a/tgui/packages/tgui-say/package.json +++ b/tgui/packages/tgui-say/package.json @@ -6,7 +6,7 @@ "react": "^19.2.7", "react-dom": "^19.2.7", "tgui": "workspace:*", - "tgui-core": "^6.1.0" + "tgui-core": "^6.1.1" }, "private": true } diff --git a/tgui/packages/tgui/interfaces/FoodSynthesizer/CrewCookieIcon.tsx b/tgui/packages/tgui/interfaces/FoodSynthesizer/CrewCookieIcon.tsx new file mode 100644 index 0000000000..fe425d790a --- /dev/null +++ b/tgui/packages/tgui/interfaces/FoodSynthesizer/CrewCookieIcon.tsx @@ -0,0 +1,22 @@ +import { useBackend } from 'tgui/backend'; +import { Icon, Image } from 'tgui-core/components'; +import type { Data } from './types'; + +export const CrewCookieIcon = (props) => { + const { act, data } = useBackend(); + const { crewicon } = data; + + return ( + <> + {crewicon ? ( + + ) : ( + + )} + + ); +}; diff --git a/tgui/packages/tgui/interfaces/FoodSynthesizer/SubTabs/FoodMenuTabs.tsx b/tgui/packages/tgui/interfaces/FoodSynthesizer/SubTabs/FoodMenuTabs.tsx new file mode 100644 index 0000000000..422c467027 --- /dev/null +++ b/tgui/packages/tgui/interfaces/FoodSynthesizer/SubTabs/FoodMenuTabs.tsx @@ -0,0 +1,31 @@ +import { useBackend } from 'tgui/backend'; +import { Stack, Tabs } from 'tgui-core/components'; +import type { Data } from '../types'; + +export const FoodMenuTabs = (props) => { + const { act, data } = useBackend(); + const { active_menu, menucatagories } = data; + + const menusToShow = [...menucatagories].sort( + (a, b) => a.sortorder - b.sortorder, + ); + + return ( + + + + {menusToShow.map((menu) => ( + act('setactive_menu', { setactive_menu: menu.id })} + > + {menu.name} + + ))} + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/FoodSynthesizer/SubTabs/FoodSelectionMenu.tsx b/tgui/packages/tgui/interfaces/FoodSynthesizer/SubTabs/FoodSelectionMenu.tsx new file mode 100644 index 0000000000..801dfbbf12 --- /dev/null +++ b/tgui/packages/tgui/interfaces/FoodSynthesizer/SubTabs/FoodSelectionMenu.tsx @@ -0,0 +1,182 @@ +import { sanitizeCssClassName } from 'common/css_sanity'; +import { useEffect } from 'react'; +import { useBackend } from 'tgui/backend'; +import { + Box, + Button, + LabeledList, + Section, + Stack, + Tabs, +} from 'tgui-core/components'; +import { classes } from 'tgui-core/react'; +import { CrewCookieIcon } from '../CrewCookieIcon'; +import type { Data } from '../types'; + +export const FoodSelectionMenu = (props) => { + const { act, data } = useBackend(); + const { + busy, + menucatagories, + active_menu, + activefood, + crew_cookies, + activecrew, + } = data; + crew_cookies; + + const recipesToShow = menucatagories + .find((category) => category.id === active_menu) + ?.recipes.filter((recipe) => !recipe.hidden) + .sort((a, b) => a.name.localeCompare(b.name)); + + const selectedFood = recipesToShow?.find((c) => c.type === activefood); + const selectedCrew = crew_cookies.find((c) => c.name === activecrew); + + useEffect(() => { + if (active_menu !== 'crew' && !selectedFood && recipesToShow?.length) { + act('setactive_food', { setactive_food: recipesToShow[0].type }); + } + }, [active_menu]); + + if (!recipesToShow) { + return No recipes found!; + } + + const cookiesToShow = crew_cookies + .filter((cookie) => cookie.category === active_menu) + .sort((a, b) => a.name.localeCompare(b.name)); + + return ( +
+ + +
act('refresh')} + tooltip="Refresh" + icon="arrows-rotate" + /> + ) + } + > + + {active_menu === 'crew' + ? cookiesToShow.map((cookie) => ( + + + + )) + : recipesToShow.map((recipe) => ( + + + + ))} + +
+
+ + {active_menu === 'crew' ? ( + selectedCrew ? ( +
+ + + + + {selectedCrew.name} + + + {selectedCrew.species} + + + + + + + +
+ ) : ( + Please select an offering. + ) + ) : selectedFood ? ( +
+ + + + + {selectedFood.name} + + + + {selectedFood.desc || 'No description available.'} + + + + + + + + +
+ ) : ( + Please select an offering. + )} +
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/FoodSynthesizer/SubTabs/SynthCartGuage.tsx b/tgui/packages/tgui/interfaces/FoodSynthesizer/SubTabs/SynthCartGuage.tsx new file mode 100644 index 0000000000..6c712e6650 --- /dev/null +++ b/tgui/packages/tgui/interfaces/FoodSynthesizer/SubTabs/SynthCartGuage.tsx @@ -0,0 +1,33 @@ +import { useBackend } from 'tgui/backend'; +import { Box, LabeledList, ProgressBar, Section } from 'tgui-core/components'; +import type { Data } from '../types'; + +/** Displays the current Cartridge status. */ +export const SynthCartGuage = (props) => { + const { data } = useBackend(); + const { isThereCart, cartFillStatus = 0 } = data; + const adjustedCartChange = cartFillStatus / 100; + + return ( +
+ {isThereCart ? ( + + + + ) : ( + + + One or more cartridges are missing or damaged.
+
+ Sabresnacks Co. recommends ordering a genuine Sabresnacks + replacement cartridge through your local logistical cargo service. +
+
+ )} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/FoodSynthesizer/index.tsx b/tgui/packages/tgui/interfaces/FoodSynthesizer/index.tsx new file mode 100644 index 0000000000..0f972c507e --- /dev/null +++ b/tgui/packages/tgui/interfaces/FoodSynthesizer/index.tsx @@ -0,0 +1,29 @@ +import { Window } from 'tgui/layouts'; +import { Section, Stack } from 'tgui-core/components'; +import { FoodMenuTabs } from './SubTabs/FoodMenuTabs'; +import { FoodSelectionMenu } from './SubTabs/FoodSelectionMenu'; +import { SynthCartGuage } from './SubTabs/SynthCartGuage'; + +export const FoodSynthesizer = (props) => { + return ( + + + + +
+ +
+
+ +
+ +
+
+ + + +
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/FoodSynthesizer/types.ts b/tgui/packages/tgui/interfaces/FoodSynthesizer/types.ts new file mode 100644 index 0000000000..3fe7dff377 --- /dev/null +++ b/tgui/packages/tgui/interfaces/FoodSynthesizer/types.ts @@ -0,0 +1,35 @@ +import type { BooleanLike } from 'tgui-core/react'; + +type MenuCategory = { + id: string; + name: string; + ref: string; + recipes: Recipe[]; + sortorder: number; +}; + +type Recipe = { + type: string; + name: string; + ref: string; + desc?: string; + hidden?: BooleanLike; +}; + +type CrewCookie = { + name: string; + species: string; + category: string; +}; + +export type Data = { + busy: BooleanLike; + isThereCart: BooleanLike; + cartFillStatus?: number; + active_menu: string; + menucatagories: MenuCategory[]; + activefood: string | null; + crew_cookies: CrewCookie[]; + activecrew: string | null; + crewicon?: string; +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/SubtabSettings.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/SubtabSettings.tsx index 36ed06f6ca..4139cd219c 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/SubtabSettings.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/bay_prefs/general/SubtabSettings.tsx @@ -66,6 +66,7 @@ export const SubtabSettings = (props: { borg_petting, resleeve_lock, resleeve_scan, + synth_cookie, mind_scan, vantag_volunteer, vantag_preference, @@ -239,6 +240,15 @@ export const SubtabSettings = (props: { {resleeve_scan ? 'Yes' : 'No'} + + +