dfhack/scripts/gui/workshop-job.lua

285 lines
8.4 KiB
Lua

-- Show and modify properties of jobs in a workshop.
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)
self.building = dfhack.job.getHolder(self.job)
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
status = { text = dfhack.TranslateName(dfhack.units.getVisibleName(worker)), pen = COLOR_GREEN }
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
function JobDetails:onGetSelectedBuilding()
return self.building
end
function JobDetails:onGetSelectedJob()
return self.job
end
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)
return dfhack.items.isCasteMaterial(iobj.item_type)
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
local head = 'Item '..(i+1)..': '..(items[i] or 0)..' of '..iobj.quantity
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
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
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),
on_select = self:callback('setItemType', obj)
}:show()
end
function JobDetails:canChangeMat()
local idx, obj = self.subviews.list:getSelected()
return obj ~= nil and not is_caste_mat(obj.iobj)
end
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
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
function JobDetails:onChangeMat()
local idx, obj = self.subviews.list:getSelected()
if obj.iobj.item_type == -1 and obj.iobj.mat_type == -1 then
-- 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
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,
on_select = self:callback('setMaterial', obj)
}:show()
end
function JobDetails:onInput(keys)
if self:propagateMoveKeys(keys) then
if df.global.world.selected_building ~= self.building then
self:dismiss()
end
else
JobDetails.super.onInput(self, keys)
end
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()