From dee0c97584607d39d0a263a0fdd9024786f0c6e3 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 20 Oct 2012 21:57:36 +0400 Subject: [PATCH] Add a gui script for viewing and changing job_item properties. --- NEWS | 1 + dfhack.init-example | 3 + library/lua/gui/materials.lua | 45 +++---- library/lua/gui/widgets.lua | 16 ++- library/xml | 2 +- scripts/gui/workshop-job.lua | 237 ++++++++++++++++++++++++++++++++++ 6 files changed, 271 insertions(+), 33 deletions(-) create mode 100644 scripts/gui/workshop-job.lua diff --git a/NEWS b/NEWS index 61e8a5b3e..5567258bc 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,7 @@ DFHack future - removebadthoughts: add --dry-run option New GUI scripts: - gui/guide-path: displays the cached path for minecart Guide orders. + - gui/workshop-job: displays inputs of a workshop job and allows tweaking them. DFHack v0.34.11-r2 diff --git a/dfhack.init-example b/dfhack.init-example index 3ecbf4342..239d68a0f 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -75,6 +75,9 @@ keybinding add Ctrl-W@layer_military/Equip/Customize/View gui/choose-weapons # minecart Guide path keybinding add Alt-P@dwarfmode/Hauling/DefineStop/Cond/Guide gui/guide-path +# workshop job details +# keybinding add Alt-A@dwarfmode/QueryBuilding/Some/Workshop/Job gui/workshop-job + ############################ # UI and game logic tweaks # ############################ diff --git a/library/lua/gui/materials.lua b/library/lua/gui/materials.lua index baf641976..871c60014 100644 --- a/library/lua/gui/materials.lua +++ b/library/lua/gui/materials.lua @@ -238,8 +238,7 @@ function MaterialDialog:submitMaterial(typ, index) self:dismiss() if self.on_select then - local info = dfhack.matinfo.decode(typ, index) - self.on_select(info, typ, index) + self.on_select(typ, index) end end @@ -276,25 +275,6 @@ function showMaterialPrompt(title, prompt, on_select, on_cancel, mat_filter) }:show() end -local itemdefs = df.global.world.raws.itemdefs -local itemtype_info = { - TRAPPARTS = { name = 'mechanisms' }, - WEAPON = { defs = itemdefs.weapons }, - TRAPCOMP = { defs = itemdefs.trapcomps }, - TOY = { defs = itemdefs.toys }, - TOOL = { defs = itemdefs.tools }, - INSTRUMENT = { defs = itemdefs.instruments }, - ARMOR = { defs = itemdefs.armor }, - AMMO = { defs = itemdefs.ammo }, - SIEGEAMMO = { defs = itemdefs.siege_ammo }, - GLOVES = { defs = itemdefs.gloves }, - SHOES = { defs = itemdefs.shoes }, - SHIELD = { defs = itemdefs.shields }, - HELM = { defs = itemdefs.helms }, - PANTS = { defs = itemdefs.pants }, - FOOD = { defs = itemdefs.food }, -} - function ItemTypeDialog(args) args.text = args.prompt or 'Type or select an item type' args.text_pen = COLOR_WHITE @@ -307,26 +287,24 @@ function ItemTypeDialog(args) local filter = args.item_filter for itype = 0,df.item_type._last_item do - local key = df.item_type[itype] - local info = itemtype_info[key] + local attrs = df.item_type.attrs[itype] + local defcnt = dfhack.items.getSubtypeCount(itype) if not filter or filter(itype,-1) then - local name = key + local name = attrs.caption or df.item_type[itype] local icon - if info and info.defs then + if defcnt >= 0 then name = 'any '..name icon = '+' end - if info and info.name then - name = info.name - end table.insert(choices, { icon = icon, text = string.lower(name), item_type = itype, item_subtype = -1 }) end - if info and info.defs then - for subtype,def in ipairs(info.defs) do + if defcnt > 0 then + for subtype = 0,defcnt-1 do + local def = dfhack.items.getSubtypeDef(itype, subtype) if not filter or filter(itype,subtype,def) then table.insert(choices, { icon = '\x1e', text = ' '..def.name, item_type = itype, item_subtype = subtype @@ -338,6 +316,13 @@ function ItemTypeDialog(args) args.choices = choices + if args.on_select then + local cb = args.on_select + args.on_select = function(idx, obj) + return cb(obj.item_type, obj.item_subtype) + end + end + return dlg.ListBox(args) end diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 05100c00b..e6a9a4d72 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -211,6 +211,11 @@ function parse_label_text(obj) obj.text_ids = idtab end +local function is_disabled(token) + return (token.disabled ~= nil and getval(token.disabled)) or + (token.enabled ~= nil and not getval(token.enabled)) +end + function render_text(obj,dc,x0,y0,pen,dpen) local width = 0 for iline,line in ipairs(obj.text_lines) do @@ -241,7 +246,7 @@ function render_text(obj,dc,x0,y0,pen,dpen) local keypen if dc then - if getval(token.disabled) then + if is_disabled(token) then dc:pen(getval(token.dpen) or dpen) keypen = COLOR_GREEN else @@ -287,7 +292,7 @@ end function check_text_keys(self, keys) if self.text_active then for _,item in ipairs(self.text_active) do - if item.key and keys[item.key] and not getval(item.disabled) then + if item.key and keys[item.key] and not is_disabled(item) then item.on_activate() return true end @@ -361,6 +366,13 @@ STANDARDSCROLL = { STANDARDSCROLL_PAGEDOWN = '+page', } +SECONDSCROLL = { + SECONDSCROLL_UP = -1, + SECONDSCROLL_DOWN = 1, + SECONDSCROLL_PAGEUP = '-page', + SECONDSCROLL_PAGEDOWN = '+page', +} + List.ATTRS{ text_pen = COLOR_CYAN, cursor_pen = COLOR_LIGHTCYAN, diff --git a/library/xml b/library/xml index 0ed2d1dc5..b9b2e8c6d 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 0ed2d1dc5547737fbb838568fc66671098f2c11a +Subproject commit b9b2e8c6d2141f13966ed965b3f3ffe924e527db diff --git a/scripts/gui/workshop-job.lua b/scripts/gui/workshop-job.lua new file mode 100644 index 000000000..973d5cc17 --- /dev/null +++ b/scripts/gui/workshop-job.lua @@ -0,0 +1,237 @@ +-- 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) + 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.unit.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 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) + if iobj.item_type >= 0 then + return df.item_type.attrs[iobj.item_type].is_caste_mat + end + return false +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[idx] 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: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 = function(item_type, item_subtype) + obj.iobj.item_type = item_type + obj.iobj.item_subtype = item_subtype + end + }: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:onChangeMat() + local idx, obj = self.subviews.list:getSelected() + + if obj.iobj.item_type == -1 and obj.iobj.mat_type == -1 then + 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 + + 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 = function(mat_type, mat_index) + if idx == 1 + 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 + }:show() +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()