Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add gui/workorder-details.lua: adjusts input item, material, traits #358

Merged
merged 22 commits into from
Jul 1, 2022
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
8101146
add gui/workorder-finetune.lua
TymurGubayev Mar 26, 2022
9dbe4e6
use reqscript 'gui/workshop-job'
TymurGubayev Mar 27, 2022
ac2f00c
minor changes
TymurGubayev Apr 3, 2022
136e0dd
make everything local
TymurGubayev Apr 3, 2022
80902da
clarify comment regarding `gui/workshop-job`
TymurGubayev Apr 3, 2022
a493869
add test
TymurGubayev Apr 3, 2022
4237c52
remove tab
TymurGubayev Apr 3, 2022
2235a1e
update a comment
TymurGubayev Apr 3, 2022
f448668
rename to `workorder-details`, add a changelog entry
TymurGubayev Apr 13, 2022
706328f
remove trailing spaces
TymurGubayev Apr 13, 2022
ead4f96
Merge branch 'master' into patch-1
TymurGubayev Jun 6, 2022
7c546a2
use the `workorder` script to create the work order for the test
TymurGubayev Jun 10, 2022
da0f5c8
Merge branch 'DFHack:master' into patch-1
TymurGubayev Jun 12, 2022
099dd8a
use `gui.materials.ItemTraitsDialog`
TymurGubayev Jun 12, 2022
db54b5f
check temporary order is removed
TymurGubayev Jun 13, 2022
0c0e9bc
handle `confirm` plugin in test
TymurGubayev Jun 14, 2022
d3f8c98
add a test for removing every possible trait
TymurGubayev Jun 14, 2022
50c29ec
Merge branch 'master' into patch-1
TymurGubayev Jun 20, 2022
e137e2f
use default `ItemTraitsDialog.on_select` handler
TymurGubayev Jun 22, 2022
696bc14
fix merge-error in changelog.txt
TymurGubayev Jun 22, 2022
18f0af3
Merge branch 'master' into patch-1
TymurGubayev Jun 30, 2022
6d7582d
in tests, pause `confirm` plugin instead of using `delay(5)`
TymurGubayev Jul 1, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ that repo.

## New Scripts

- `gui/workorder-details`: adjusts work orders' input item, material, traits
- `assign-minecarts`: assign minecarts to hauling routes that don't have one
TymurGubayev marked this conversation as resolved.
Show resolved Hide resolved
- `deteriorate`: combines, replaces, and extends previous `deteriorateclothes`, `deterioratecorpses`, and `deterioratefood` scripts.
- `gui/petitions`: shows list of fort's petitions
Expand Down
279 changes: 279 additions & 0 deletions gui/workorder-details.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
-- adjust work orders' input item, material, traits
--[====[

gui/workorder-details
=====================
Adjust input items, material, or traits for work orders. Actual
jobs created for it will inherit the details.

This is the equivalent of `gui/workshop-job` for work orders,
with the additional possibility to set input items' traits.

It has to be run from a work order's detail screen
(:kbd:`j-m`, select work order, :kbd:`d`).

For best experience add the following to your ``dfhack*.init``::

keybinding add D@workquota_details gui/workorder-details
myk002 marked this conversation as resolved.
Show resolved Hide resolved

]====]

--[[
Credit goes to the author of `gui/workshop-job`, it showed
me the way. Also, a huge chunk of code could be reused.
]]

local utils = require 'utils'
local gui = require 'gui'
local guimat = require 'gui.materials'
local widgets = require 'gui.widgets'
local dlg = require 'gui.dialogs'

local wsj = reqscript 'gui/workshop-job'

local JobDetails = defclass(JobDetails, gui.FramedScreen)

JobDetails.focus_path = 'workorder-details'

JobDetails.ATTRS {
job = DEFAULT_NIL,
frame_inset = 1,
frame_background = COLOR_BLACK,
}

function JobDetails:init(args)
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') },
{ key = 'CUSTOM_T', text = ': Traits',
enabled = self:callback('canChangeTrait'),
on_activate = self:callback('onChangeTrait') }
}
},
widgets.List{
view_id = 'list',
frame = { t = 6, b = 2 },
row_height = 4,
},
widgets.Label{
frame = { l = 0, b = 0 },
text = {
{ key = 'LEAVESCREEN', text = ': Back',
on_activate = self:callback('dismiss') }
}
},
}

self:initListChoices()
end

function JobDetails:onGetSelectedJob()
return self.job
end

local describe_item_type = wsj.describe_item_type
local is_caste_mat = wsj.is_caste_mat
local describe_material = wsj.describe_material
local list_flags = wsj.list_flags

local function describe_item_traits(iobj)
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, iobj.has_material_reaction_product .. '-producing')
end
if iobj.reaction_class ~= '' then
table.insert(line1, 'reaction 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 traits')
end
return table.concat(line1, ', ')
end

function JobDetails:initListChoices()
if not self.job.items then
self.subviews.list:setChoices({})
return
end

local choices = {}
for i,iobj in ipairs(self.job.items) do
local head = 'Item '..(i+1)..' x'..iobj.quantity
if iobj.min_dimension > 0 then
head = head .. '(size '..iobj.min_dimension..')'
end

table.insert(choices, {
index = i,
iobj = iobj,
text = {
head, NEWLINE,
' ', { text = curry(describe_item_type, iobj) }, NEWLINE,
' ', { text = curry(describe_material, iobj) }, NEWLINE,
' ', { text = curry(describe_item_traits, iobj) }, NEWLINE
}
})
end

self.subviews.list:setChoices(choices)
end

JobDetails.canChangeIType = wsj.JobDetails.canChangeIType
JobDetails.setItemType = wsj.JobDetails.setItemType
JobDetails.onChangeIType = wsj.JobDetails.onChangeIType
JobDetails.canChangeMat = wsj.JobDetails.canChangeMat
JobDetails.setMaterial = wsj.JobDetails.setMaterial
JobDetails.findUnambiguousItem = wsj.JobDetails.findUnambiguousItem
JobDetails.onChangeMat = wsj.JobDetails.onChangeMat

function JobDetails:onInput(keys)
JobDetails.super.onInput(self, keys)
end

function JobDetails:canChangeTrait()
local idx, obj = self.subviews.list:getSelected()
return obj ~= nil and not is_caste_mat(obj.iobj)
end

function JobDetails:toggleFlag(obj, ffield, flag)
local job_item = obj.iobj
job_item[ffield][flag] = not job_item[ffield][flag]
end

function JobDetails:toggleToolUse(obj, tool_use)
local job_item = obj.iobj
tool_use = df.tool_uses[tool_use]
if job_item.has_tool_use == tool_use then
job_item.has_tool_use = df.tool_uses.NONE
else
job_item.has_tool_use = tool_use
end
end

function JobDetails:toggleMetalOre(obj, ore_ix)
local job_item = obj.iobj
if job_item.metal_ore == ore_ix then
job_item.metal_ore = -1
else
job_item.metal_ore = ore_ix
end
end

function JobDetails:toggleReactionClass(obj, reaction_class)
local job_item = obj.iobj
if job_item.reaction_class == reaction_class then
job_item.reaction_class = ''
else
job_item.reaction_class = reaction_class
end
end

function JobDetails:toggleProductMaterial(obj, product_materials)
local job_item = obj.iobj
if job_item.has_material_reaction_product == product_materials then
job_item.has_material_reaction_product = ''
else
job_item.has_material_reaction_product = product_materials
end
end

function JobDetails:unsetFlags(obj)
local job_item = obj.iobj
for flag, ffield in pairs(job_item_flags_map) do
myk002 marked this conversation as resolved.
Show resolved Hide resolved
if job_item[ffield][flag] then
JobDetails:toggleFlag(obj, ffield, flag)
end
end
end

function JobDetails:setTrait(obj, sel)
if sel.ffield then
--print('toggle flag', sel.ffield, sel.flag)
JobDetails:toggleFlag(obj, sel.ffield, sel.flag)
elseif sel.reset_flags then
--print('reset every flag')
JobDetails:unsetFlags(obj)
elseif sel.tool_use then
--print('toggle tool_use', sel.tool_use)
JobDetails:toggleToolUse(obj, sel.tool_use)
elseif sel.ore_ix then
--print('toggle ore', sel.ore_ix)
JobDetails:toggleMetalOre(obj, sel.ore_ix)
elseif sel.reaction_class then
--print('toggle reaction class', sel.reaction_class)
JobDetails:toggleReactionClass(obj, sel.reaction_class)
elseif sel.product_materials then
--print('toggle product materials', sel.product_materials)
JobDetails:toggleProductMaterial(obj, sel.product_materials)
elseif sel.reset_all_traits then
--print('reset every trait')
-- flags
JobDetails:unsetFlags(obj)
-- tool use
JobDetails:toggleToolUse(obj, 'NONE')
-- metal ore
JobDetails:toggleMetalOre(obj, -1)
-- reaction class
JobDetails:toggleReactionClass(obj, '')
-- producing
JobDetails:toggleProductMaterial(obj, '')
else
print('unknown sel')
printall(sel)
end
end

function JobDetails:onChangeTrait()
local idx, obj = self.subviews.list:getSelected()
guimat.ItemTraitsDialog{
job_item = obj.iobj,
prompt = 'Please select traits for input '..idx,
none_caption = 'no traits',
on_select = self:callback('setTrait', obj)
}:show()
end

local scr = dfhack.gui.getCurViewscreen()
if not df.viewscreen_workquota_detailsst:is_instance(scr) then
qerror("This script needs to be run from a work order details screen")
end

-- by opening the viewscreen_workquota_detailsst the
-- work order's .items array is initialized
JobDetails{ job = scr.order }:show()
7 changes: 7 additions & 0 deletions gui/workshop-job.lua
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ and then try to change the input item type, now it won't let you select *plant*;
you have to unset the material first.

]====]

--@ module = true

local utils = require 'utils'
local gui = require 'gui'
local guidm = require 'gui.dwarfmode'
Expand Down Expand Up @@ -323,6 +326,10 @@ function JobDetails:onInput(keys)
end
end

if dfhack_flags.module then
return
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
Expand Down
73 changes: 73 additions & 0 deletions test/gui/workorder-details.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
-- test -dhack/scripts/devel/tests -tworkorder%-details

config.mode = 'fortress'

local gui = require('gui')
local function send_keys(...)
local keys = {...}
for _,key in ipairs(keys) do
gui.simulateInput(dfhack.gui.getCurViewscreen(true), key)
end
end

local xtest = {} -- use to temporarily disable tests (change `function test.somename` to `function xtest.somename`)
local wait = function(n)
--delay(n or 30) -- enable for debugging the tests
end

function test.changeOrderDetails()
--[[ this is not needed because of how gui.simulateInput'D_JOBLIST' works
-- verify expected starting state
expect.eq(df.ui_sidebar_mode.Default, df.global.ui.main.mode)
expect.true_(df.viewscreen_dwarfmodest:is_instance(scr))
--]]

-- get into the orders screen
send_keys('D_JOBLIST', 'UNITJOB_MANAGER')
expect.true_(df.viewscreen_jobmanagementst:is_instance(dfhack.gui.getCurViewscreen(true)), "We need to be in the jobmanagement/Main screen")

local ordercount = #df.global.world.manager_orders

--- create an order
dfhack.run_command [[workorder "{ \"frequency\" : \"OneTime\", \"job\" : \"CutGems\", \"material\" : \"INORGANIC:SLADE\" }"]]
wait()
send_keys('STANDARDSCROLL_UP') -- move cursor to newly created CUT SLADE
wait()
send_keys('MANAGER_DETAILS')
expect.true_(df.viewscreen_workquota_detailsst:is_instance(dfhack.gui.getCurViewscreen(true)), "We need to be in the workquota_details screen")
local job = dfhack.gui.getCurViewscreen(true).order
local item = job.items[0]

dfhack.run_command 'gui/workorder-details'
--[[
input item: boulder
material: slade
traits: none
]]
expect.ne(-1, item.item_type, "Input should not be 'any item'")
expect.ne(-1, item.mat_type, "Material should not be 'any material'")
expect.false_(item.flags2.allow_artifact, "Trait allow_artifact should not be set")

wait()
send_keys('CUSTOM_I', 'SELECT') -- change input to 'any item'
wait()
send_keys('CUSTOM_M', 'SELECT') -- change material to 'any material'
wait()
send_keys('CUSTOM_T', 'STANDARDSCROLL_DOWN', 'STANDARDSCROLL_DOWN', 'SELECT', 'LEAVESCREEN') -- change traits to 'allow_artifact'
--[[
input item: any item
material: any material
traits: allow_artifact
]]
expect.eq(-1, item.item_type, "Input item should change to 'any item'")
expect.eq(-1, item.mat_type, "Material should change to 'any material'")
expect.true_(item.flags2.allow_artifact, "Trait allow_artifact should change to set")

-- cleanup
wait()
send_keys('LEAVESCREEN', 'LEAVESCREEN', 'MANAGER_REMOVE')
myk002 marked this conversation as resolved.
Show resolved Hide resolved
expect.eq(ordercount, #df.global.world.manager_orders, "Test order should've been removed")
-- go back to map screen
wait()
send_keys('LEAVESCREEN', 'LEAVESCREEN')
end