I cobbled together a DFHack script for selecting the ingredients for meals. Given that it has to be run when the job is selected (in the kitchen) and the manual entry of items, I'd expect that it wouldn't be used regularly, but if you just HAVE to make a specific batch you can do it.
local dialogs = require 'gui.dialogs'
local gui = require 'gui'
local utils = require 'utils'
local widgets = require 'gui.widgets'
--============================================================
function Chef ()
local job = dfhack.gui.getSelectedJob (true)
local df_selects = "<DF Selects>"
local Main_Page = {}
local persist_screen
local list = {}
local display_list = {}
local keybindings = {
first = {key = "CUSTOM_A",
desc = "Select first (solid) ingredient"},
second = {key = "CUSTOM_B",
desc = "Select second ingredient"},
third = {key = "CUSTOM_C",
desc = "Select third ingredient"},
fourth = {key = "CUSTOM_D",
desc = "Select fourth ingredient"}}
if df.global.ui.main.mode ~= df.ui_sidebar_mode.QueryBuilding then
qerror ("This script must be run when a kitchen job screen ('q'uery) is open and a meal preparation task has been created")
elseif not job or
job.job_type ~= df.job_type.PrepareMeal then
qerror ("This script must be run when a kitchen job screen ('q'uery) is open and a meal preparation task has been created")
end
--==============================================================
Ui = defclass (nil, gui.FramedScreen)
Ui.ATTRS = {
frame_style = gui.GREY_LINE_FRAME,
frame_title = "The Chef",
transparent = false
}
--==============================================================
function description_string_of (item_type, mat_type, mat_index)
if item_type == -1 then
return df_selects
end
if df.item_type.attrs [item_type].is_caste_mat then -- caste mat things don't have a material
if item_type == df.item_type.EGG then
return df.global.world.raws.creatures.all [mat_index].name [0] .. " eggs"
else -- All of those are animals
return df.global.world.raws.creatures.all [mat_index].name [0]
end
else
local mat_info = dfhack.matinfo.decode (mat_type, mat_index)
local description
if mat_info.mode == 'plant' then
description = mat_info.plant.name
else
description = mat_info.creature.name [0]
end
if item_type == df.item_type.DRINK or
item_type == df.item_type.LIQUID_MISC then
return description .. " " .. mat_info.material.state_name [1] -- Liquid
else
return description .. " " .. mat_info.material.state_name [0] -- Solid
end
end
end
--==============================================================
function populate_lists (list, display_list, solids_only)
for i = #list, 1, -1 do
table.remove (list, i)
table.remove (display_list, i)
end
for i, item in ipairs (df.global.world.items.other.ANY_COOKABLE) do
local item_type = item.getType (item)
local mat_info
local mat_type
local mat_index
if (item_type == df.item_type.MEAT or
item_type == df.item_type.FISH or
item_type == df.item_type.FISH_RAW or
item_type == df.item_type.SEEDS or
item_type == df.item_type.PLANT or
item_type == df.item_type.PLANT_GROWTH or
item_type == df.item_type.DRINK or
item_type == df.item_type.POWDER_MISC or
item_type == df.item_type.CHEESE or
item_type == df.item_type.LIQUID_MISC or
item_type == df.item_type.GLOB or
item_type == df.item_type.EGG) and
(not solids_only or
(item_type ~= df.item_type.DRINK and
item_type ~= df.item_type.LIQUID_MISC)) and
not item.flags.in_job and
not item.flags.removed and
not item.flags.in_inventory and
not item.flags.rotten and
not item.flags.encased and
not item.flags.trader and
not item.flags.garbage_collect and
not item.flags.forbid and
not item.flags.dump and
not item.flags.on_fire then
if df.item_type.attrs [item_type].is_caste_mat then -- caste mat things don't have a material
mat_type = -1
mat_index = item.race
else
mat_type = item.mat_type
mat_index = item.mat_index
mat_info = dfhack.matinfo.decode (mat_type, mat_index)
end
if not mat_info or
mat_info.material.flags.EDIBLE_COOKED then
local found = false
for k, element in ipairs (list) do
if element [1] == item_type and
element [2] == mat_type and
element [3] == mat_index then
found = true
break
end
end
if not found then
table.insert (list, {item_type, mat_type, mat_index, description_string_of (item_type, mat_type, mat_index)})
end
end
end
end
local temp
for i, dummy in ipairs (list) do
for k = i + 1, #list do
if list [k] [4] < list [i] [4] then
temp = list [i]
list [i] = list [k]
list [k] = temp
end
end
end
table.insert (list, 1, {-1, -1, -1, df_selects})
for i, element in ipairs (list) do
table.insert (display_list, element [4])
end
end
--==============================================================
function Ui:init ()
self.stack = {}
self.item_count = 0
self.keys = {}
local description
-- local screen_width, screen_height = dfhack.screen.getWindowSize ()
Main_Page.First_Ingredient_Label =
widgets.Label {text = {{text = "",
key = keybindings.first.key,
key_sep = '()'},
{text = " Solid Ingredient:",
pen = COLOR_LIGHTBLUE}},
frame = {l = 0, t = 2, y_align = 0}}
Main_Page.Second_Ingredient_Label =
widgets.Label {text = {{text = "",
key = keybindings.second.key,
key_sep = '()'},
{text = " Second Ingredient:",
pen = COLOR_LIGHTBLUE}},
frame = {l = 0, t = 3, y_align = 0}}
Main_Page.Third_Ingredient_Label =
widgets.Label {text = {{text = "",
key = keybindings.third.key,
key_sep = '()'},
{text = " Third Ingredient:",
pen = COLOR_LIGHTBLUE}},
frame = {l = 0, t = 4, y_align = 0},
visible = job.mat_type > 2}
Main_Page.Fourth_Ingredient_Label =
widgets.Label {text = {{text = "",
key = keybindings.fourth.key,
key_sep = '()'},
{text = " Fourth Ingredient:",
pen = COLOR_LIGHTBLUE}},
frame = {l = 0, t = 5, y_align = 0},
visible = job.mat_type > 3}
Main_Page.Name_Label =
widgets.Label {text = "(Provides the name of the meal)",
frame = {l = 60, t = 3 - 2 + job.mat_type, y_align = 0},
text_pen = COLOR_WHITE}
description = description_string_of (job.job_items [0].item_type,
job.job_items [0].mat_type,
job.job_items [0].mat_index)
Main_Page.First_Item =
widgets.Label {text = description,
frame = {l = 24, t = 2, y_align = 0},
text_pen = COLOR_YELLOW}
description = description_string_of (job.job_items [1].item_type,
job.job_items [1].mat_type,
job.job_items [1].mat_index)
Main_Page.Second_Item =
widgets.Label {text = description,
frame = {l = 24, t = 3, y_align = 0},
text_pen = COLOR_YELLOW}
if job.mat_type <= 2 then
description = df_selects
else
description = description_string_of (job.job_items [2].item_type,
job.job_items [2].mat_type,
job.job_items [2].mat_index)
end
Main_Page.Third_Item =
widgets.Label {text = description,
frame = {l = 24, t = 4, y_align = 0},
text_pen = COLOR_YELLOW,
visible = job.mat_type > 2}
if job.mat_type <= 3 then
description = df_selects
else
description = description_string_of (job.job_items [3].item_type,
job.job_items [3].mat_type,
job.job_items [3].mat_index)
end
Main_Page.Fourth_Item =
widgets.Label {text = description,
frame = {l = 24, t = 5, y_align = 0},
text_pen = COLOR_YELLOW,
visible = job.mat_type > 3}
local mainPage = widgets.Panel {
subviews = {Main_Page.First_Ingredient_Label,
Main_Page.Second_Ingredient_Label,
Main_Page.Third_Ingredient_Label,
Main_Page.Fourth_Ingredient_Label,
Main_Page.Name_Label,
Main_Page.First_Item,
Main_Page.Second_Item,
Main_Page.Third_Item,
Main_Page.Fourth_Item}}
local pages = widgets.Pages
{subviews = {mainPage},view_id = "pages",
}
pages:setSelected (1)
self:addviews {pages}
end
--==============================================================
function Ui:on_select_first_ingredient (index, choice)
job.job_items [0].item_type = list [index] [1]
job.job_items [0].mat_type = list [index] [2]
job.job_items [0].mat_index = list [index] [3]
Main_Page.First_Item:setText (list [index] [4])
end
--==============================================================
function Ui:on_select_second_ingredient (index, choice)
dfhack.println (index, choice, #choice)
job.job_items [1].item_type = list [index] [1]
job.job_items [1].mat_type = list [index] [2]
job.job_items [1].mat_index = list [index] [3]
Main_Page.Second_Item:setText (list [index] [4])
end
--==============================================================
function Ui:on_select_third_ingredient (index, choice)
job.job_items [2].item_type = list [index] [1]
job.job_items [2].mat_type = list [index] [2]
job.job_items [2].mat_index = list [index] [3]
Main_Page.Third_Item:setText (list [index] [4])
end
--==============================================================
function Ui:on_select_fourth_ingredient (index, choice)
job.job_items [3].item_type = list [index] [1]
job.job_items [3].mat_type = list [index] [2]
job.job_items [3].mat_index = list [index] [3]
Main_Page.Fourth_Item:setText (list [index] [4])
end
--==============================================================
function Ui:onInput (keys)
if keys.LEAVESCREEN_ALL then
self:dismiss ()
end
if keys.LEAVESCREEN then
self:dismiss ()
end
if keys [keybindings.first.key] then
populate_lists (list, display_list, true)
dialogs.showListPrompt("Enter first ingredient selection",
"text",
nil,
display_list,
self:callback ("on_select_first_ingredient")) -- Don't think an "on_cancel" is needed
elseif keys [keybindings.second.key] then
populate_lists (list, display_list, false)
dialogs.showListPrompt("Enter second ingredient selection",
"text",
nil,
display_list,
self:callback ("on_select_second_ingredient")) -- Don't think an "on_cancel" is needed
elseif keys [keybindings.third.key] and
job.mat_type > 2 then
populate_lists (list, display_list, false)
dialogs.showListPrompt("Enter third ingredient selection",
"text",
nil,
display_list,
self:callback ("on_select_third_ingredient")) -- Don't think an "on_cancel" is needed
elseif keys [keybindings.fourth.key] and
job.mat_type > 3 then
populate_lists (list, display_list, false)
dialogs.showListPrompt("Enter fourth ingredient selection",
"text",
nil,
display_list,
self:callback ("on_select_fourth_ingredient")) -- Don't think an "on_cancel" is needed
end
self.super.onInput (self, keys)
end
--============================================================
function Show_Viewer ()
local screen = Ui {}
persist_screen = screen
screen:show ()
end
--============================================================
Show_Viewer ()
end
Chef ()