@@ -15,11 +15,15 @@ local validArgs = utils.invert({
1515
1616-- Functions
1717
18+ -- @param item df.item
19+ -- @return string
1820local 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 )) .. " ' "
2022end
2123
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 )
2327 local x , y , z = dfhack .items .getPosition (item )
2428 if not x or not y or not z then
2529 return
@@ -30,24 +34,30 @@ local function get_item_pos(item)
3034 end
3135end
3236
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 )
3440 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 ()
4450 return
4551 end
52+
4653 if # squad .positions > unit .military .squad_position then
4754 return squad .positions [unit .military .squad_position ]
4855 end
4956end
5057
58+ -- @param unit df.unit
59+ -- @param item df.item
60+ -- @return number[] list of body part ids
5161local function bodyparts_that_can_wear (unit , item )
5262 local bodyparts = {}
5363 local unitparts = dfhack .units .getCasteRaw (unit ).body_info .body_parts
@@ -89,47 +99,61 @@ local function bodyparts_that_can_wear(unit, item)
8999 return bodyparts
90100end
91101
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." )
99107end
100108
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
104122end
105123
106124-- 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 )
108126 local silent = args .all -- Don't print details if we're iterating through all dwarves
109127 local unit_name = dfhack .df2console (dfhack .units .getReadableName (unit ))
128+ local printed = false
110129
111130 if not silent then
112- need_newline = print_line (" Processing unit " .. unit_name , need_newline )
131+ print (" Processing unit " .. unit_name )
132+ printed = true
113133 end
114134
115135 -- The return value
116136 local to_drop = {} -- item id to item object
117137
118138 -- 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 )
120140 if not squad_position then
121141 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 ()
123144 end
124145 return
125146 end
126147
127148 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
129151 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
131154 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
133157 end
134158
135159 -- Find all worn items which may be at issue.
@@ -148,12 +172,12 @@ local function process(unit, args, need_newline)
148172 end
149173
150174 -- 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
155179 -- 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 )
157181 end
158182 end
159183 end
@@ -163,36 +187,49 @@ local function process(unit, args, need_newline)
163187
164188 local present_ids = {} -- map of item ID to item object
165189 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
168192 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
171195 end
172196 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
174199 if args .free then
175200 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 )
187203 end
188204 else
189- missing_ids [u_id ] = item
205+ missing_ids [item_id ] = item
190206 if args .free then
191- to_drop [u_id ] = item
207+ to_drop [item_id ] = item
192208 end
193209 end
194210 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 )
196233 end
197234 end
198235
@@ -202,10 +239,10 @@ local function process(unit, args, need_newline)
202239 -- unless --multi is specified, in which we don't care
203240 local covered = {} -- map of body part id to true/nil
204241 if not args .multi then
205- for id , item in pairs (present_ids ) do
242+ for item_id , item in pairs (present_ids ) do
206243 -- weapons and shields don't "cover" the bodypart they're assigned to. (Needed to figure out if we're missing gloves.)
207244 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
209246 end
210247 end
211248 end
@@ -221,19 +258,23 @@ local function process(unit, args, need_newline)
221258 end
222259
223260 -- 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
230266 if args .drop then
231- to_drop [w_id ] = item
267+ to_drop [worn_item_id ] = item
232268 end
233269 end
234270 end
235271 end
236272
273+ -- add a spacing line if there was any output
274+ if printed then
275+ print ()
276+ end
277+
237278 return to_drop
238279end
239280
@@ -242,15 +283,15 @@ local function do_drop(item_list)
242283 return
243284 end
244285
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 )
247288 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 ))
249290 else
250291 if dfhack .items .moveToGround (item , pos ) then
251- print (" Dropped item # " .. id .. " ' " .. item_description (item ) .. " ' " )
292+ print (" Dropped " .. item_description (item ))
252293 else
253- dfhack .printerr (" Could not drop object # " .. id .. " " .. item_description (item ))
294+ dfhack .printerr (" Could not drop " .. item_description (item ))
254295 end
255296 end
256297 end
@@ -265,10 +306,8 @@ local function main(args)
265306 end
266307
267308 if args .all then
268- local need_newline = false
269309 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 ))
272311 end
273312 else
274313 local unit = dfhack .gui .getSelectedUnit ()
0 commit comments