@@ -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
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,50 +187,48 @@ 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, " .. item_description (item ), need_newline )
193
+ print (unit_name .. " is missing an assigned item, " .. item_description (item ))
194
+ printed = true
170
195
end
171
196
if dfhack .items .getGeneralRef (item , df .general_ref_type .UNIT_HOLDER ) then
172
- need_newline = print_line (unit_name .. " cannot equip item: another unit has a claim on " .. item_description (item ), need_newline )
197
+ print (unit_name .. " cannot equip item: another unit has a claim on " .. item_description (item ))
198
+ printed = true
173
199
if args .free then
174
200
print (" Removing from uniform" )
175
- assigned_items [u_id ] = nil
176
- for _ , specs in ipairs (squad_position .equipment .uniform ) do
177
- for _ , spec in ipairs (specs ) do
178
- for idx , assigned in ipairs (spec .assigned ) do
179
- if assigned == u_id then
180
- spec .assigned :erase (idx )
181
- break
182
- end
183
- end
184
- end
185
- end
201
+ uniform_assigned_items [item_id ] = nil
202
+ remove_item_from_position (squad_position , item_id )
186
203
end
187
204
else
188
- missing_ids [u_id ] = item
205
+ missing_ids [item_id ] = item
189
206
if args .free then
190
- to_drop [u_id ] = item
207
+ to_drop [item_id ] = item
191
208
end
192
209
end
193
210
else
194
- present_ids [u_id ] = item
211
+ present_ids [item_id ] = item
195
212
end
196
213
end
197
214
198
215
-- Make the equipment.assigned_items list consistent with what is present in equipment.uniform
199
216
for i =# (squad_position .equipment .assigned_items )- 1 ,0 ,- 1 do
200
- local u_id = squad_position .equipment .assigned_items [i ]
217
+ local assigned_item_id = squad_position .equipment .assigned_items [i ]
201
218
-- Quiver, backpack, and flask are assigned in their own locations rather than in equipment.uniform, and thus need their own checks
202
219
-- If more separately-assigned items are added in the future, this handling will need to be updated accordingly
203
- if assigned_items [u_id ] == nil and u_id ~= squad_position .equipment .quiver and u_id ~= squad_position .equipment .backpack and u_id ~= squad_position .equipment .flask then
204
- local item = df .item .find (u_id )
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 )
205
226
if item ~= nil then
206
- need_newline = print_line (unit_name .. " has an improperly assigned item, " .. item_description (item ) .. ' ; removing it' )
227
+ print (unit_name .. " has an improperly assigned item, " .. item_description (item ) .. " ; removing it" )
207
228
else
208
- need_newline = print_line (unit_name .. " has a nonexistent item assigned, item # " .. u_id .. ' ; removing it' )
229
+ print (unit_name .. " has a nonexistent item assigned, item # " .. assigned_item_id .. " ; removing it" )
209
230
end
231
+ printed = true
210
232
squad_position .equipment .assigned_items :erase (i )
211
233
end
212
234
end
@@ -217,10 +239,10 @@ local function process(unit, args, need_newline)
217
239
-- unless --multi is specified, in which we don't care
218
240
local covered = {} -- map of body part id to true/nil
219
241
if not args .multi then
220
- for id , item in pairs (present_ids ) do
242
+ for item_id , item in pairs (present_ids ) do
221
243
-- weapons and shields don't "cover" the bodypart they're assigned to. (Needed to figure out if we're missing gloves.)
222
244
if item ._type ~= df .item_weaponst and item ._type ~= df .item_shieldst then
223
- covered [worn_parts [id ]] = true
245
+ covered [worn_parts [item_id ]] = true
224
246
end
225
247
end
226
248
end
@@ -236,17 +258,23 @@ local function process(unit, args, need_newline)
236
258
end
237
259
238
260
-- Drop everything (except uniform pieces) from body parts which should be covered but aren't
239
- for w_id , item in pairs (worn_items ) do
240
- if assigned_items [w_id ] == nil then -- don't drop uniform pieces (including shields, weapons for hands)
241
- if uncovered [worn_parts [w_id ]] then
242
- need_newline = print_line (unit_name .. " potentially has " .. 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
243
266
if args .drop then
244
- to_drop [w_id ] = item
267
+ to_drop [worn_item_id ] = item
245
268
end
246
269
end
247
270
end
248
271
end
249
272
273
+ -- add a spacing line if there was any output
274
+ if printed then
275
+ print ()
276
+ end
277
+
250
278
return to_drop
251
279
end
252
280
@@ -255,8 +283,8 @@ local function do_drop(item_list)
255
283
return
256
284
end
257
285
258
- for id , item in pairs (item_list ) do
259
- local pos = get_item_pos (item )
286
+ for _ , item in pairs (item_list ) do
287
+ local pos = get_visible_item_pos (item )
260
288
if not pos then
261
289
dfhack .printerr (" Could not find drop location for " .. item_description (item ))
262
290
else
@@ -278,10 +306,8 @@ local function main(args)
278
306
end
279
307
280
308
if args .all then
281
- local need_newline = false
282
309
for _ , unit in ipairs (dfhack .units .getCitizens (true )) do
283
- do_drop (process (unit , args , need_newline ))
284
- need_newline = true
310
+ do_drop (process (unit , args ))
285
311
end
286
312
else
287
313
local unit = dfhack .gui .getSelectedUnit ()
0 commit comments