2012-10-20 11:57:36 -06:00
|
|
|
-- Show and modify properties of jobs in a workshop.
|
2015-10-23 08:37:39 -06:00
|
|
|
--[[=begin
|
2012-10-20 11:57:36 -06:00
|
|
|
|
2015-10-23 08:37:39 -06:00
|
|
|
gui/workshop-job
|
|
|
|
================
|
|
|
|
Bind to a key (the example config uses :kbd:`Alt`:kbd:`a`), and activate with a job selected in
|
|
|
|
a workshop in the :kbd:`q` mode.
|
|
|
|
|
|
|
|
.. image:: /docs/images/workshop-job.png
|
|
|
|
|
|
|
|
The script shows a list of the input reagents of the selected job, and allows changing
|
|
|
|
them like the `job` ``item-type`` and `job` ``item-material`` commands.
|
|
|
|
|
|
|
|
Specifically, pressing the :kbd:`i` key pops up a dialog that lets you select an item
|
|
|
|
type from a list.
|
|
|
|
|
|
|
|
.. image:: /docs/images/workshop-job-item.png
|
|
|
|
|
|
|
|
Pressing :kbd:`m`, unless the item type does not allow a material,
|
|
|
|
lets you choose a material.
|
|
|
|
|
|
|
|
.. image:: /docs/images/workshop-job-material.png
|
|
|
|
|
|
|
|
Since there are a lot more materials than item types, this dialog is more complex
|
|
|
|
and uses a hierarchy of sub-menus. List choices that open a sub-menu are marked
|
|
|
|
with an arrow on the left.
|
|
|
|
|
|
|
|
.. warning::
|
|
|
|
|
|
|
|
Due to the way input reagent matching works in DF, you must select an item type
|
|
|
|
if you select a material, or the material will be matched incorrectly in some cases.
|
|
|
|
If you press :kbd:`m` without choosing an item type, the script will auto-choose
|
|
|
|
if there is only one valid choice, or pop up an error message box instead of the
|
|
|
|
material selection dialog.
|
|
|
|
|
|
|
|
Note that both materials and item types presented in the dialogs are filtered
|
|
|
|
by the job input flags, and even the selected item type for material selection,
|
|
|
|
or material for item type selection. Many jobs would let you select only one
|
|
|
|
input item type.
|
|
|
|
|
|
|
|
For example, if you choose a *plant* input item type for your prepare meal job,
|
|
|
|
it will only let you select cookable materials.
|
|
|
|
|
|
|
|
If you choose a *barrel* item instead (meaning things stored in barrels, like
|
|
|
|
drink or milk), it will let you select any material, since in this case the
|
|
|
|
material is matched against the barrel itself. Then, if you select, say, iron,
|
|
|
|
and then try to change the input item type, now it won't let you select *plant*;
|
|
|
|
you have to unset the material first.
|
|
|
|
|
|
|
|
=end]]
|
2012-10-20 11:57:36 -06:00
|
|
|
local utils = require 'utils'
|
|
|
|
local gui = require 'gui'
|
|
|
|
local guidm = require 'gui.dwarfmode'
|
|
|
|
local guimat = require 'gui.materials'
|
|
|
|
local widgets = require 'gui.widgets'
|
|
|
|
local dlg = require 'gui.dialogs'
|
|
|
|
|
|
|
|
JobDetails = defclass(JobDetails, guidm.MenuOverlay)
|
|
|
|
|
|
|
|
JobDetails.focus_path = 'workshop-job'
|
|
|
|
|
|
|
|
JobDetails.ATTRS {
|
|
|
|
job = DEFAULT_NIL,
|
|
|
|
frame_inset = 1,
|
|
|
|
frame_background = COLOR_BLACK,
|
|
|
|
}
|
|
|
|
|
|
|
|
function JobDetails:init(args)
|
2012-10-21 11:45:51 -06:00
|
|
|
self.building = dfhack.job.getHolder(self.job)
|
|
|
|
|
2012-10-20 11:57:36 -06:00
|
|
|
local status = { text = 'No worker', pen = COLOR_DARKGREY }
|
|
|
|
local worker = dfhack.job.getWorker(self.job)
|
|
|
|
if self.job.flags.suspend then
|
|
|
|
status = { text = 'Suspended', pen = COLOR_RED }
|
|
|
|
elseif worker then
|
2012-10-21 11:45:51 -06:00
|
|
|
status = { text = dfhack.TranslateName(dfhack.units.getVisibleName(worker)), pen = COLOR_GREEN }
|
2012-10-20 11:57:36 -06:00
|
|
|
end
|
|
|
|
|
|
|
|
self:addviews{
|
|
|
|
widgets.Label{
|
|
|
|
frame = { l = 0, t = 0 },
|
|
|
|
text = {
|
|
|
|
{ text = df.job_type.attrs[self.job.job_type].caption }, NEWLINE, NEWLINE,
|
|
|
|
' ', status
|
|
|
|
}
|
|
|
|
},
|
|
|
|
widgets.Label{
|
|
|
|
frame = { l = 0, t = 4 },
|
|
|
|
text = {
|
|
|
|
{ key = 'CUSTOM_I', text = ': Input item, ',
|
|
|
|
enabled = self:callback('canChangeIType'),
|
|
|
|
on_activate = self:callback('onChangeIType') },
|
|
|
|
{ key = 'CUSTOM_M', text = ': Material',
|
|
|
|
enabled = self:callback('canChangeMat'),
|
|
|
|
on_activate = self:callback('onChangeMat') }
|
|
|
|
}
|
|
|
|
},
|
|
|
|
widgets.List{
|
|
|
|
view_id = 'list',
|
|
|
|
frame = { t = 6, b = 2 },
|
|
|
|
row_height = 4,
|
|
|
|
scroll_keys = widgets.SECONDSCROLL,
|
|
|
|
},
|
|
|
|
widgets.Label{
|
|
|
|
frame = { l = 0, b = 0 },
|
|
|
|
text = {
|
|
|
|
{ key = 'LEAVESCREEN', text = ': Back',
|
|
|
|
on_activate = self:callback('dismiss') }
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
self:initListChoices()
|
|
|
|
end
|
|
|
|
|
2012-10-21 11:45:51 -06:00
|
|
|
function JobDetails:onGetSelectedBuilding()
|
|
|
|
return self.building
|
|
|
|
end
|
|
|
|
|
|
|
|
function JobDetails:onGetSelectedJob()
|
|
|
|
return self.job
|
|
|
|
end
|
|
|
|
|
2012-10-20 11:57:36 -06:00
|
|
|
function describe_item_type(iobj)
|
|
|
|
local itemline = 'any item'
|
|
|
|
if iobj.item_type >= 0 then
|
|
|
|
itemline = df.item_type.attrs[iobj.item_type].caption or iobj.item_type
|
|
|
|
local def = dfhack.items.getSubtypeDef(iobj.item_type, iobj.item_subtype)
|
|
|
|
local count = dfhack.items.getSubtypeCount(iobj.item_type, iobj.item_subtype)
|
|
|
|
if def then
|
|
|
|
itemline = def.name
|
|
|
|
elseif count >= 0 then
|
|
|
|
itemline = 'any '..itemline
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return itemline
|
|
|
|
end
|
|
|
|
|
|
|
|
function is_caste_mat(iobj)
|
2012-10-21 11:45:51 -06:00
|
|
|
return dfhack.items.isCasteMaterial(iobj.item_type)
|
2012-10-20 11:57:36 -06:00
|
|
|
end
|
|
|
|
|
|
|
|
function describe_material(iobj)
|
|
|
|
local matline = 'any material'
|
|
|
|
if is_caste_mat(iobj) then
|
|
|
|
matline = 'material not applicable'
|
|
|
|
elseif iobj.mat_type >= 0 then
|
|
|
|
local info = dfhack.matinfo.decode(iobj.mat_type, iobj.mat_index)
|
|
|
|
if info then
|
|
|
|
matline = info:toString()
|
|
|
|
else
|
|
|
|
matline = iobj.mat_type..':'..iobj.mat_index
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return matline
|
|
|
|
end
|
|
|
|
|
|
|
|
function list_flags(list, bitfield)
|
|
|
|
for name,val in pairs(bitfield) do
|
|
|
|
if val then
|
|
|
|
table.insert(list, name)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function JobDetails:initListChoices()
|
|
|
|
local items = {}
|
|
|
|
for i,ref in ipairs(self.job.items) do
|
|
|
|
local idx = ref.job_item_idx
|
|
|
|
if idx >= 0 then
|
|
|
|
items[idx] = (items[idx] or 0) + 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local choices = {}
|
|
|
|
for i,iobj in ipairs(self.job.job_items) do
|
2012-10-21 11:45:51 -06:00
|
|
|
local head = 'Item '..(i+1)..': '..(items[i] or 0)..' of '..iobj.quantity
|
2012-10-20 11:57:36 -06:00
|
|
|
if iobj.min_dimension > 0 then
|
|
|
|
head = head .. '(size '..iobj.min_dimension..')'
|
|
|
|
end
|
|
|
|
|
|
|
|
local line1 = {}
|
|
|
|
local reaction = df.reaction.find(iobj.reaction_id)
|
|
|
|
if reaction and #iobj.contains > 0 then
|
|
|
|
for _,ri in ipairs(iobj.contains) do
|
|
|
|
table.insert(line1, 'has '..utils.call_with_string(
|
|
|
|
reaction.reagents[ri],'getDescription',iobj.reaction_id
|
|
|
|
))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if iobj.metal_ore >= 0 then
|
|
|
|
local ore = dfhack.matinfo.decode(0, iobj.metal_ore)
|
|
|
|
if ore then
|
|
|
|
table.insert(line1, 'ore of '..ore:toString())
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if iobj.has_material_reaction_product ~= '' then
|
|
|
|
table.insert(line1, 'product '..iobj.has_material_reaction_product)
|
|
|
|
end
|
|
|
|
if iobj.reaction_class ~= '' then
|
|
|
|
table.insert(line1, 'class '..iobj.reaction_class)
|
|
|
|
end
|
|
|
|
if iobj.has_tool_use >= 0 then
|
|
|
|
table.insert(line1, 'has use '..df.tool_uses[iobj.has_tool_use])
|
|
|
|
end
|
|
|
|
list_flags(line1, iobj.flags1)
|
|
|
|
list_flags(line1, iobj.flags2)
|
|
|
|
list_flags(line1, iobj.flags3)
|
|
|
|
if #line1 == 0 then
|
|
|
|
table.insert(line1, 'no flags')
|
|
|
|
end
|
|
|
|
|
|
|
|
table.insert(choices, {
|
|
|
|
index = i,
|
|
|
|
iobj = iobj,
|
|
|
|
text = {
|
|
|
|
head, NEWLINE,
|
|
|
|
' ', { text = curry(describe_item_type, iobj) }, NEWLINE,
|
|
|
|
' ', { text = curry(describe_material, iobj) }, NEWLINE,
|
|
|
|
' ', table.concat(line1, ', '), NEWLINE
|
|
|
|
}
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
self.subviews.list:setChoices(choices)
|
|
|
|
end
|
|
|
|
|
|
|
|
function JobDetails:canChangeIType()
|
|
|
|
local idx, obj = self.subviews.list:getSelected()
|
|
|
|
return obj ~= nil
|
|
|
|
end
|
|
|
|
|
2012-10-21 11:45:51 -06:00
|
|
|
function JobDetails:setItemType(obj, item_type, item_subtype)
|
|
|
|
obj.iobj.item_type = item_type
|
|
|
|
obj.iobj.item_subtype = item_subtype
|
|
|
|
|
|
|
|
if is_caste_mat(obj.iobj) then
|
|
|
|
self:setMaterial(obj, -1, -1)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-10-20 11:57:36 -06:00
|
|
|
function JobDetails:onChangeIType()
|
|
|
|
local idx, obj = self.subviews.list:getSelected()
|
|
|
|
guimat.ItemTypeDialog{
|
|
|
|
prompt = 'Please select a new item type for input '..idx,
|
|
|
|
none_caption = 'any item',
|
|
|
|
item_filter = curry(dfhack.job.isSuitableItem, obj.iobj),
|
2012-10-21 11:45:51 -06:00
|
|
|
on_select = self:callback('setItemType', obj)
|
2012-10-20 11:57:36 -06:00
|
|
|
}:show()
|
|
|
|
end
|
|
|
|
|
|
|
|
function JobDetails:canChangeMat()
|
|
|
|
local idx, obj = self.subviews.list:getSelected()
|
|
|
|
return obj ~= nil and not is_caste_mat(obj.iobj)
|
|
|
|
end
|
|
|
|
|
2012-10-21 11:45:51 -06:00
|
|
|
function JobDetails:setMaterial(obj, mat_type, mat_index)
|
|
|
|
if obj.index == 0
|
|
|
|
and self.job.mat_type == obj.iobj.mat_type
|
|
|
|
and self.job.mat_index == obj.iobj.mat_index
|
|
|
|
and self.job.job_type ~= df.job_type.PrepareMeal
|
|
|
|
then
|
|
|
|
self.job.mat_type = mat_type
|
|
|
|
self.job.mat_index = mat_index
|
|
|
|
end
|
|
|
|
|
|
|
|
obj.iobj.mat_type = mat_type
|
|
|
|
obj.iobj.mat_index = mat_index
|
|
|
|
end
|
|
|
|
|
2012-10-25 02:15:18 -06:00
|
|
|
function JobDetails:findUnambiguousItem(iobj)
|
|
|
|
local count = 0
|
|
|
|
local itype
|
|
|
|
|
|
|
|
for i = 0,df.item_type._last_item do
|
|
|
|
if dfhack.job.isSuitableItem(iobj, i, -1) then
|
|
|
|
count = count + 1
|
|
|
|
if count > 1 then return nil end
|
|
|
|
itype = i
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return itype
|
|
|
|
end
|
|
|
|
|
2012-10-20 11:57:36 -06:00
|
|
|
function JobDetails:onChangeMat()
|
|
|
|
local idx, obj = self.subviews.list:getSelected()
|
|
|
|
|
|
|
|
if obj.iobj.item_type == -1 and obj.iobj.mat_type == -1 then
|
2012-10-25 02:15:18 -06:00
|
|
|
-- If the job allows only one specific item type, use it
|
|
|
|
local vitype = self:findUnambiguousItem(obj.iobj)
|
|
|
|
|
|
|
|
if vitype then
|
|
|
|
obj.iobj.item_type = vitype
|
|
|
|
else
|
|
|
|
dlg.showMessage(
|
|
|
|
'Bug Alert',
|
|
|
|
{ 'Please set a specific item type first.\n\n',
|
|
|
|
'Otherwise the material will be matched\n',
|
|
|
|
'incorrectly due to a limitation in DF code.' },
|
|
|
|
COLOR_YELLOW
|
|
|
|
)
|
|
|
|
return
|
|
|
|
end
|
2012-10-20 11:57:36 -06:00
|
|
|
end
|
|
|
|
|
|
|
|
guimat.MaterialDialog{
|
|
|
|
prompt = 'Please select a new material for input '..idx,
|
|
|
|
none_caption = 'any material',
|
|
|
|
mat_filter = function(mat,parent,mat_type,mat_index)
|
|
|
|
return dfhack.job.isSuitableMaterial(obj.iobj, mat_type, mat_index)
|
|
|
|
end,
|
2012-10-21 11:45:51 -06:00
|
|
|
on_select = self:callback('setMaterial', obj)
|
|
|
|
}:show()
|
|
|
|
end
|
2012-10-20 11:57:36 -06:00
|
|
|
|
2012-10-21 11:45:51 -06:00
|
|
|
function JobDetails:onInput(keys)
|
|
|
|
if self:propagateMoveKeys(keys) then
|
|
|
|
if df.global.world.selected_building ~= self.building then
|
|
|
|
self:dismiss()
|
2012-10-20 11:57:36 -06:00
|
|
|
end
|
2012-10-21 11:45:51 -06:00
|
|
|
else
|
|
|
|
JobDetails.super.onInput(self, keys)
|
|
|
|
end
|
2012-10-20 11:57:36 -06:00
|
|
|
end
|
|
|
|
|
|
|
|
if not string.match(dfhack.gui.getCurFocus(), '^dwarfmode/QueryBuilding/Some/Workshop/Job') then
|
|
|
|
qerror("This script requires a workshop job selected in the 'q' mode")
|
|
|
|
end
|
|
|
|
|
|
|
|
local dlg = JobDetails{ job = dfhack.gui.getSelectedJob() }
|
|
|
|
dlg:show()
|