diff --git a/stripcaged.lua b/stripcaged.lua new file mode 100644 index 0000000000..44226e957b --- /dev/null +++ b/stripcaged.lua @@ -0,0 +1,227 @@ +local function plural(nr, name) + -- '1 cage' / '4 cages' + return string.format('%s %s%s', nr, name, nr ~= 1 and 's' or '') +end + +local function cage_dump_items(list) + local count = 0 + local count_cage = 0 + for _, cage in ipairs(list) do + local pre_count = count + for _, ref in ipairs(cage.general_refs) do + if df.general_ref_contains_itemst:is_instance(ref) then + local item = df.item.find(ref.item_id) + if not item.flags.dump then + count = count + 1 + item.flags.dump = true + end + end + end + if pre_count ~= count then count_cage = count_cage + 1 end + end + print(string.format('Dumped %s in %s', plural(count, 'item'), + plural(count_cage, 'cage'))) +end + +local function cage_dump_armor(list) + local count = 0 + local count_cage = 0 + for _, cage in ipairs(list) do + local pre_count = count + for _, ref in ipairs(cage.general_refs) do + if df.general_ref_contains_unitst:is_instance(ref) then + local inventory = df.unit.find(ref.unit_id).inventory + for _, it in ipairs(inventory) do + if not it.item.flags.dump and + it.mode == df.unit_inventory_item.T_mode.Worn then + count = count + 1 + it.item.flags.dump = true + end + end + end + end + if pre_count ~= count then count_cage = count_cage + 1 end + end + print(string.format('Dumped %s in %s', plural(count, 'armor piece'), + plural(count_cage, 'cage'))) +end + +local function cage_dump_weapons(list) + local count = 0 + local count_cage = 0 + for _, cage in ipairs(list) do + local pre_count = count + for _, ref in ipairs(cage.general_refs) do + if df.general_ref_contains_unitst:is_instance(ref) then + local inventory = df.unit.find(ref.unit_id).inventory + for _, it in ipairs(inventory) do + if not it.item.flags.dump and + it.mode == df.unit_inventory_item.T_mode.Weapon then + count = count + 1 + it.item.flags.dump = true + end + end + end + end + if pre_count ~= count then count_cage = count_cage + 1 end + end + print(string.format('Dumped %s in %s', plural(count, 'weapon'), + plural(count_cage, 'cage'))) +end + +local function cage_dump_all(list) + local count = 0 + local count_cage = 0 + + for _, cage in ipairs(list) do + local pre_count = count + for _, ref in ipairs(cage.general_refs) do + + if df.general_ref_contains_itemst:is_instance(ref) then + local item = df.item.find(ref.item_id) + if not item.flags.dump then + count = count + 1 + item.flags.dump = true + end + elseif df.general_ref_contains_unitst:is_instance(ref) then + local inventory = df.unit.find(ref.unit_id).inventory + for _, it in ipairs(inventory) do + if not it.item.flags.dump then + count = count + 1 + it.item.flags.dump = true + end + end + end + + end + if pre_count ~= count then count_cage = count_cage + 1 end + end + print(string.format('Dumped %s in %s', plural(count, 'item'), + plural(count_cage, 'cage'))) +end + +local function cage_dump_list(list) + local count_total = {} + local empty_cages = 0 + for _, cage in ipairs(list) do + local count = {} + for _, ref in ipairs(cage.general_refs) do + if df.general_ref_contains_itemst:is_instance(ref) then + local classname = df.item_type.attrs[ + df.item.find(ref.item_id):getType()].caption + count[classname] = (count[classname] or 0) + 1 + elseif df.general_ref_contains_unitst:is_instance(ref) then + local inventory = df.unit.find(ref.unit_id).inventory + for _, it in ipairs(inventory) do + local classname = df.item_type.attrs[it.item:getType()].caption + count[classname] = (count[classname] or 0) + 1 + end + -- TODO: vermin ? + + --[[ TODO: Determine how/if to handle a DEBUG flag. + + --Ruby: + else + puts "unhandled ref #{ref.inspect}" if $DEBUG + end + ]] + end + end + + local type = df.item_type.attrs[cage:getType()].caption -- Default case + if df.item_cagest:is_instance(cage) then + type = 'Cage' + elseif df.item_animaltrapst:is_instance(cage) then + type = 'Animal trap' + end + + -- If count is empty + if not next(count) then + empty_cages = empty_cages + 1 + else + local sortedlist = {} + for classname, n in pairs(count) do + table.insert(sortedlist, {classname = classname, count = n}) + end + table.sort(sortedlist, (function(i, j) return i.count < j.count end)) + print(('%s %d: '):format(type, cage.id)) + for _, t in ipairs(sortedlist) do + print(' ' .. t.count .. ' ' .. t.classname) + end + end + for k, v in pairs(count) do count_total[k] = (count_total[k] or 0) + v end + end + + if #list > 1 then + print('\nTotal:') + local sortedlist = {} + for classname, n in pairs(count_total) do + sortedlist[#sortedlist + 1] = {classname = classname, count = n} + end + table.sort(sortedlist, (function(i, j) return i.count < j.count end)) + for _, t in ipairs(sortedlist) do + print(' ' .. t.count .. ' ' .. t.classname) + end + print('with ' .. plural(empty_cages, 'empty cage')) + end +end + +-- handle magic script arguments + +local args = {...} + +local list +if args[2] == 'here' then + local it = dfhack.gui.getSelectedItem(true) + list = {it} + if not df.item_cagest:is_instance(it) and + not df.item_animaltrapst:is_instance(it) then + list = {} + for _, cage in ipairs(df.global.world.items.other.ANY_CAGE_OR_TRAP) do + if same_xyz(it.pos, cage.pos) then + table.insert(list, cage) + end + end + end + if not list[1] then + print 'Please select a cage' + return + end +elseif tonumber(args[2]) then -- Check if user provided ids + list = {} + for i = 2, #args do + local id = args[i] + local it = df.item.find(tonumber(id)) + if not it then + print('Invalid item id ' .. id) + elseif not df.item_cagest:is_instance(it) and + not df.item_animaltrapst:is_instance(it) then + print('Item ' .. id .. ' is not a cage') + else + list[#list + 1] = it + end + end + if not list[1] then + print 'Please use a valid cage id' + return + end +else + list = df.global.world.items.other.ANY_CAGE_OR_TRAP +end + +-- act +local choice = args[1] + +if choice:match '^it' then + cage_dump_items(list) +elseif choice:match '^arm' then + cage_dump_armor(list) +elseif choice:match '^wea' then + cage_dump_weapons(list) +elseif choice == 'all' then + cage_dump_all(list) +elseif choice == 'list' then + cage_dump_list(list) +else + print(dfhack.script_help()) +end diff --git a/stripcaged.rb b/stripcaged.rb deleted file mode 100644 index 8cb1a0fa40..0000000000 --- a/stripcaged.rb +++ /dev/null @@ -1,227 +0,0 @@ -# mark stuff inside of cages for dumping. -=begin - -stripcaged -========== -For dumping items inside cages. Will mark selected items for dumping, then -a dwarf may come and actually dump them (or you can use `autodump`). - -Arguments: - -:list: display the list of all cages and their item content on the console -:items: dump items in the cage, excluding stuff worn by caged creatures -:weapons: dump equipped weapons -:armor: dump everything worn by caged creatures (including armor and clothing) -:all: dump everything in the cage, on a creature or not - -Without further arguments, all commands work on all cages and animal traps on -the map. With the ``here`` argument, considers only the in-game selected cage -(or the cage under the game cursor). To target only specific cages, you can -alternatively pass cage IDs as arguments:: - - stripcaged weapons 25321 34228 - -=end - -def plural(nr, name) - # '1 cage' / '4 cages' - "#{nr} #{name}#{'s' if nr > 1}" -end - -def cage_dump_items(list) - count = 0 - count_cage = 0 - list.each { |cage| - pre_count = count - cage.general_refs.each { |ref| - next unless ref.kind_of?(DFHack::GeneralRefContainsItemst) - next if ref.item_tg.flags.dump - count += 1 - ref.item_tg.flags.dump = true - } - count_cage += 1 if pre_count != count - } - - puts "Dumped #{plural(count, 'item')} in #{plural(count_cage, 'cage')}" -end - -def cage_dump_armor(list) - count = 0 - count_cage = 0 - list.each { |cage| - pre_count = count - cage.general_refs.each { |ref| - next unless ref.kind_of?(DFHack::GeneralRefContainsUnitst) - ref.unit_tg.inventory.each { |it| - next if it.mode != :Worn - next if it.item.flags.dump - count += 1 - it.item.flags.dump = true - } - } - count_cage += 1 if pre_count != count - } - - puts "Dumped #{plural(count, 'armor piece')} in #{plural(count_cage, 'cage')}" -end - -def cage_dump_weapons(list) - count = 0 - count_cage = 0 - list.each { |cage| - pre_count = count - cage.general_refs.each { |ref| - next unless ref.kind_of?(DFHack::GeneralRefContainsUnitst) - ref.unit_tg.inventory.each { |it| - next if it.mode != :Weapon - next if it.item.flags.dump - count += 1 - it.item.flags.dump = true - } - } - count_cage += 1 if pre_count != count - } - - puts "Dumped #{plural(count, 'weapon')} in #{plural(count_cage, 'cage')}" -end - -def cage_dump_all(list) - count = 0 - count_cage = 0 - list.each { |cage| - pre_count = count - cage.general_refs.each { |ref| - case ref - when DFHack::GeneralRefContainsItemst - next if ref.item_tg.flags.dump - count += 1 - ref.item_tg.flags.dump = true - when DFHack::GeneralRefContainsUnitst - ref.unit_tg.inventory.each { |it| - next if it.item.flags.dump - count += 1 - it.item.flags.dump = true - } - end - } - count_cage += 1 if pre_count != count - } - - puts "Dumped #{plural(count, 'item')} in #{plural(count_cage, 'cage')}" -end - - -def cage_dump_list(list) - count_total = Hash.new(0) - empty_cages = 0 - list.each { |cage| - count = Hash.new(0) - - cage.general_refs.each { |ref| - case ref - when DFHack::GeneralRefContainsItemst - count[ref.item_tg._rtti_classname] += 1 - when DFHack::GeneralRefContainsUnitst - ref.unit_tg.inventory.each { |it| - count[it.item._rtti_classname] += 1 - } - # TODO vermin ? - else - puts "unhandled ref #{ref.inspect}" if $DEBUG - end - } - - type = case cage - when DFHack::ItemCagest; 'Cage' - when DFHack::ItemAnimaltrapst; 'Animal trap' - else cage._rtti_classname - end - - if count.empty? - empty_cages += 1 - else - puts "#{type} ##{cage.id}: ", count.sort_by { |k, v| v }.map { |k, v| " #{v} #{k}" } - end - - count.each { |k, v| count_total[k] += v } - } - - if list.length > 2 - puts '', "Total: ", count_total.sort_by { |k, v| v }.map { |k, v| " #{v} #{k}" } - puts "with #{plural(empty_cages, 'empty cage')}" - end -end - - -# handle magic script arguments -here_only = $script_args.delete 'here' -if here_only - it = df.item_find - list = [it] - if not it.kind_of?(DFHack::ItemCagest) and not it.kind_of?(DFHack::ItemAnimaltrapst) - list = df.world.items.other[:ANY_CAGE_OR_TRAP].find_all { |i| df.at_cursor?(i) } - end - if list.empty? - puts 'Please select a cage' - throw :script_finished - end - -elsif ids = $script_args.find_all { |arg| arg =~ /^\d+$/ } and ids.first - list = [] - ids.each { |id| - $script_args.delete id - if not it = df.item_find(id.to_i) - puts "Invalid item id #{id}" - elsif not it.kind_of?(DFHack::ItemCagest) and not it.kind_of?(DFHack::ItemAnimaltrapst) - puts "Item ##{id} is not a cage" - list << it - else - list << it - end - } - if list.empty? - puts 'Please use a valid cage id' - throw :script_finished - end - -else - list = df.world.items.other[:ANY_CAGE_OR_TRAP] -end - - -# act -case $script_args[0] -when /^it/i - cage_dump_items(list) -when /^arm/i - cage_dump_armor(list) -when /^wea/i - cage_dump_weapons(list) -when 'all' - cage_dump_all(list) -when 'list' - cage_dump_list(list) -else - puts <