@@ -15,11 +15,15 @@ local validArgs = utils.invert({
15
15
16
16
-- Functions
17
17
18
+ -- @param item df.item
19
+ -- @return string
18
20
local function item_description (item )
19
- return dfhack .df2console (dfhack .items .getDescription (item , 0 , true ))
21
+ return " item # " .. item . id .. " ' " .. dfhack .df2console (dfhack .items .getDescription (item , 0 , true )) .. " ' "
20
22
end
21
23
22
- local function get_item_pos (item )
24
+ -- @param item df.item
25
+ -- @return df.coord|nil
26
+ local function get_visible_item_pos (item )
23
27
local x , y , z = dfhack .items .getPosition (item )
24
28
if not x or not y or not z then
25
29
return
@@ -30,24 +34,30 @@ local function get_item_pos(item)
30
34
end
31
35
end
32
36
33
- local function get_squad_position (unit , unit_name )
37
+ -- @param unit df.unit
38
+ -- @return df.squad_position|nil
39
+ local function get_squad_position (unit )
34
40
local squad = df .squad .find (unit .military .squad_id )
35
- if squad then
36
- if squad . entity_id ~= df . global . plotinfo . group_id then
37
- print ( " WARNING: Unit " .. unit_name .. " is a member of a squad from another site! " ..
38
- " This may be preventing them from doing any useful work. " ..
39
- " You can fix this by assigning them to a local squad and then unassigning them. " )
40
- print ()
41
- return
42
- end
43
- else
41
+ if not squad then
42
+ return
43
+ end
44
+
45
+ if squad . entity_id ~= df . global . plotinfo . group_id then
46
+ print ( " WARNING: Unit " .. dfhack . df2console ( dfhack . units . getReadableName ( unit )) .. " is a member of a squad from another site! " ..
47
+ " This may be preventing them from doing any useful work. " ..
48
+ " You can fix this by assigning them to a local squad and then unassigning them. " )
49
+ print ()
44
50
return
45
51
end
52
+
46
53
if # squad .positions > unit .military .squad_position then
47
54
return squad .positions [unit .military .squad_position ]
48
55
end
49
56
end
50
57
58
+ -- @param unit df.unit
59
+ -- @param item df.item
60
+ -- @return number[] list of body part ids
51
61
local function bodyparts_that_can_wear (unit , item )
52
62
local bodyparts = {}
53
63
local unitparts = dfhack .units .getCasteRaw (unit ).body_info .body_parts
@@ -89,47 +99,61 @@ local function bodyparts_that_can_wear(unit, item)
89
99
return bodyparts
90
100
end
91
101
92
- -- returns new value of need_newline
93
- local function print_line (text , need_newline )
94
- if need_newline then
95
- print ()
96
- end
97
- print (text )
98
- return false
102
+ -- @param unit_name string
103
+ -- @param labor_name string
104
+ local function print_bad_labor (unit_name , labor_name )
105
+ return print (" WARNING: Unit " .. unit_name .. " has the " .. labor_name ..
106
+ " labor enabled, which conflicts with military uniforms." )
99
107
end
100
108
101
- local function print_bad_labor (unit_name , labor_name , need_newline )
102
- return print_line (" WARNING: Unit " .. unit_name .. " has the " .. labor_name ..
103
- " labor enabled, which conflicts with military uniforms." , need_newline )
109
+ -- @param squad_position df.squad_position
110
+ -- @param item_id number
111
+ local function remove_item_from_position (squad_position , item_id )
112
+ for _ , uniform_slot_specs in ipairs (squad_position .equipment .uniform ) do
113
+ for _ , uniform_spec in ipairs (uniform_slot_specs ) do
114
+ for idx , assigned_item_id in ipairs (uniform_spec .assigned ) do
115
+ if assigned_item_id == item_id then
116
+ uniform_spec .assigned :erase (idx )
117
+ return
118
+ end
119
+ end
120
+ end
121
+ end
104
122
end
105
123
106
124
-- Will figure out which items need to be moved to the floor, returns an item_id:item map
107
- local function process (unit , args , need_newline )
125
+ local function process (unit , args )
108
126
local silent = args .all -- Don't print details if we're iterating through all dwarves
109
127
local unit_name = dfhack .df2console (dfhack .units .getReadableName (unit ))
128
+ local printed = false
110
129
111
130
if not silent then
112
- need_newline = print_line (" Processing unit " .. unit_name , need_newline )
131
+ print (" Processing unit " .. unit_name )
132
+ printed = true
113
133
end
114
134
115
135
-- The return value
116
136
local to_drop = {} -- item id to item object
117
137
118
138
-- First get squad position for an early-out for non-military dwarves
119
- local squad_position = get_squad_position (unit , unit_name )
139
+ local squad_position = get_squad_position (unit )
120
140
if not squad_position then
121
141
if not silent then
122
- need_newline = print_line (unit_name .. " does not have a military uniform." , need_newline )
142
+ print (unit_name .. " does not have a military uniform." )
143
+ print ()
123
144
end
124
145
return
125
146
end
126
147
127
148
if unit .status .labors .MINE then
128
- need_newline = print_bad_labor (unit_name , " mining" , need_newline )
149
+ print_bad_labor (unit_name , " mining" )
150
+ printed = true
129
151
elseif unit .status .labors .CUTWOOD then
130
- need_newline = print_bad_labor (unit_name , " woodcutting" , need_newline )
152
+ print_bad_labor (unit_name , " woodcutting" )
153
+ printed = true
131
154
elseif unit .status .labors .HUNT then
132
- need_newline = print_bad_labor (unit_name , " hunting" , need_newline )
155
+ print_bad_labor (unit_name , " hunting" )
156
+ printed = true
133
157
end
134
158
135
159
-- Find all worn items which may be at issue.
@@ -148,12 +172,12 @@ local function process(unit, args, need_newline)
148
172
end
149
173
150
174
-- Now get info about which items have been assigned as part of the uniform
151
- local assigned_items = {} -- assigned item ids mapped to item objects
152
- for _ , specs in ipairs (squad_position .equipment .uniform ) do
153
- for _ , spec in ipairs (specs ) do
154
- for _ , assigned in ipairs (spec .assigned ) do
175
+ local uniform_assigned_items = {} -- assigned item ids mapped to item objects
176
+ for _ , uniform_slot_specs in ipairs (squad_position .equipment .uniform ) do
177
+ for _ , uniform_spec in ipairs (uniform_slot_specs ) do
178
+ for _ , assigned_item_id in ipairs (uniform_spec .assigned ) do
155
179
-- Include weapon and shield so we can avoid dropping them, or pull them out of container/inventory later
156
- assigned_items [ assigned ] = df .item .find (assigned )
180
+ uniform_assigned_items [ assigned_item_id ] = df .item .find (assigned_item_id )
157
181
end
158
182
end
159
183
end
@@ -163,36 +187,49 @@ local function process(unit, args, need_newline)
163
187
164
188
local present_ids = {} -- map of item ID to item object
165
189
local missing_ids = {} -- map of item ID to item object
166
- for u_id , item in pairs (assigned_items ) do
167
- if not worn_items [u_id ] then
190
+ for item_id , item in pairs (uniform_assigned_items ) do
191
+ if not worn_items [item_id ] then
168
192
if not silent then
169
- need_newline = print_line (unit_name .. " is missing an assigned item, object # " .. u_id .. " ' " ..
170
- item_description ( item ) .. " ' " , need_newline )
193
+ print (unit_name .. " is missing an assigned item, " .. item_description ( item ))
194
+ printed = true
171
195
end
172
196
if dfhack .items .getGeneralRef (item , df .general_ref_type .UNIT_HOLDER ) then
173
- need_newline = print_line (unit_name .. " cannot equip item: another unit has a claim on object #" .. u_id .. " '" .. item_description (item ) .. " '" , need_newline )
197
+ print (unit_name .. " cannot equip item: another unit has a claim on " .. item_description (item ))
198
+ printed = true
174
199
if args .free then
175
200
print (" Removing from uniform" )
176
- assigned_items [u_id ] = nil
177
- for _ , specs in ipairs (squad_position .equipment .uniform ) do
178
- for _ , spec in ipairs (specs ) do
179
- for idx , assigned in ipairs (spec .assigned ) do
180
- if assigned == u_id then
181
- spec .assigned :erase (idx )
182
- break
183
- end
184
- end
185
- end
186
- end
201
+ uniform_assigned_items [item_id ] = nil
202
+ remove_item_from_position (squad_position , item_id )
187
203
end
188
204
else
189
- missing_ids [u_id ] = item
205
+ missing_ids [item_id ] = item
190
206
if args .free then
191
- to_drop [u_id ] = item
207
+ to_drop [item_id ] = item
192
208
end
193
209
end
194
210
else
195
- present_ids [u_id ] = item
211
+ present_ids [item_id ] = item
212
+ end
213
+ end
214
+
215
+ -- Make the equipment.assigned_items list consistent with what is present in equipment.uniform
216
+ for i =# (squad_position .equipment .assigned_items )- 1 ,0 ,- 1 do
217
+ local assigned_item_id = squad_position .equipment .assigned_items [i ]
218
+ -- Quiver, backpack, and flask are assigned in their own locations rather than in equipment.uniform, and thus need their own checks
219
+ -- If more separately-assigned items are added in the future, this handling will need to be updated accordingly
220
+ if uniform_assigned_items [assigned_item_id ] == nil and
221
+ assigned_item_id ~= squad_position .equipment .quiver and
222
+ assigned_item_id ~= squad_position .equipment .backpack and
223
+ assigned_item_id ~= squad_position .equipment .flask
224
+ then
225
+ local item = df .item .find (assigned_item_id )
226
+ if item ~= nil then
227
+ print (unit_name .. " has an improperly assigned item, " .. item_description (item ) .. " ; removing it" )
228
+ else
229
+ print (unit_name .. " has a nonexistent item assigned, item # " .. assigned_item_id .. " ; removing it" )
230
+ end
231
+ printed = true
232
+ squad_position .equipment .assigned_items :erase (i )
196
233
end
197
234
end
198
235
@@ -202,10 +239,10 @@ local function process(unit, args, need_newline)
202
239
-- unless --multi is specified, in which we don't care
203
240
local covered = {} -- map of body part id to true/nil
204
241
if not args .multi then
205
- for id , item in pairs (present_ids ) do
242
+ for item_id , item in pairs (present_ids ) do
206
243
-- weapons and shields don't "cover" the bodypart they're assigned to. (Needed to figure out if we're missing gloves.)
207
244
if item ._type ~= df .item_weaponst and item ._type ~= df .item_shieldst then
208
- covered [worn_parts [id ]] = true
245
+ covered [worn_parts [item_id ]] = true
209
246
end
210
247
end
211
248
end
@@ -221,19 +258,23 @@ local function process(unit, args, need_newline)
221
258
end
222
259
223
260
-- Drop everything (except uniform pieces) from body parts which should be covered but aren't
224
- for w_id , item in pairs (worn_items ) do
225
- if assigned_items [w_id ] == nil then -- don't drop uniform pieces (including shields, weapons for hands)
226
- if uncovered [worn_parts [w_id ]] then
227
- need_newline = print_line (unit_name ..
228
- " potentially has object #" ..
229
- w_id .. " '" .. item_description (item ) .. " ' blocking a missing uniform item." , need_newline )
261
+ for worn_item_id , item in pairs (worn_items ) do
262
+ if uniform_assigned_items [worn_item_id ] == nil then -- don't drop uniform pieces (including shields, weapons for hands)
263
+ if uncovered [worn_parts [worn_item_id ]] then
264
+ print (unit_name .. " potentially has " .. item_description (item ) .. " blocking a missing uniform item." )
265
+ printed = true
230
266
if args .drop then
231
- to_drop [w_id ] = item
267
+ to_drop [worn_item_id ] = item
232
268
end
233
269
end
234
270
end
235
271
end
236
272
273
+ -- add a spacing line if there was any output
274
+ if printed then
275
+ print ()
276
+ end
277
+
237
278
return to_drop
238
279
end
239
280
@@ -242,15 +283,15 @@ local function do_drop(item_list)
242
283
return
243
284
end
244
285
245
- for id , item in pairs (item_list ) do
246
- local pos = get_item_pos (item )
286
+ for _ , item in pairs (item_list ) do
287
+ local pos = get_visible_item_pos (item )
247
288
if not pos then
248
- dfhack .printerr (" Could not find drop location for item # " .. id .. " " .. item_description (item ))
289
+ dfhack .printerr (" Could not find drop location for " .. item_description (item ))
249
290
else
250
291
if dfhack .items .moveToGround (item , pos ) then
251
- print (" Dropped item # " .. id .. " ' " .. item_description (item ) .. " ' " )
292
+ print (" Dropped " .. item_description (item ))
252
293
else
253
- dfhack .printerr (" Could not drop object # " .. id .. " " .. item_description (item ))
294
+ dfhack .printerr (" Could not drop " .. item_description (item ))
254
295
end
255
296
end
256
297
end
@@ -265,10 +306,8 @@ local function main(args)
265
306
end
266
307
267
308
if args .all then
268
- local need_newline = false
269
309
for _ , unit in ipairs (dfhack .units .getCitizens (true )) do
270
- do_drop (process (unit , args , need_newline ))
271
- need_newline = true
310
+ do_drop (process (unit , args ))
272
311
end
273
312
else
274
313
local unit = dfhack .gui .getSelectedUnit ()
0 commit comments