-
Notifications
You must be signed in to change notification settings - Fork 199
/
Copy pathworkshop-job.lua
338 lines (286 loc) · 10.3 KB
/
workshop-job.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
-- Show and modify properties of jobs in a workshop.
--[====[
gui/workshop-job
================
Run 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.
]====]
--@ module = true
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, obj.iobj.item_type)
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 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
local dlg = JobDetails{ job = dfhack.gui.getSelectedJob() }
dlg:show()