From 8429f651766566f697f36347faba23a98d987f72 Mon Sep 17 00:00:00 2001 From: jj Date: Thu, 22 Nov 2012 16:56:22 +0100 Subject: [PATCH] add scripts/stripcaged.rb and documentation --- NEWS | 1 + Readme.rst | 45 ++++++++++ scripts/stripcaged.rb | 194 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 240 insertions(+) create mode 100644 scripts/stripcaged.rb diff --git a/NEWS b/NEWS index 463ebb47a..65c647337 100644 --- a/NEWS +++ b/NEWS @@ -21,6 +21,7 @@ DFHack future - dfusion: misc scripts with a text based menu. - embark: lets you embark anywhere. - lever: list and pull fort levers from the dfhack console. + - stripcaged: mark items inside cages for dumping, eg caged goblin weapons. New GUI scripts: - gui/guide-path: displays the cached path for minecart Guide orders. - gui/workshop-job: displays inputs of a workshop job and allows tweaking them. diff --git a/Readme.rst b/Readme.rst index e6554cac0..3434a240d 100644 --- a/Readme.rst +++ b/Readme.rst @@ -144,6 +144,16 @@ system console: The patches are expected to be encoded in text format used by IDA. + +Live patching +------------- + +As an alternative, you can use the ``binpatch`` dfhack command to apply/remove +patches live in memory during a DF session. + +In this case, updating symbols.xml is not necessary. + + ============================= Something doesn't work, help! ============================= @@ -1956,6 +1966,41 @@ embark ====== Allows to embark anywhere. Currently windows only. +lever +===== +Allow manipulation of in-game levers from the dfhack console. + +Can list levers, including state and links, with:: + + lever list + +To queue a job so that a dwarf will pull the lever 42, use ``lever pull 42``. +This is the same as 'q'uerying the building and queue a 'P'ull request. + +To magically toggle the lever immediately, use:: + + lever pull 42 --now + +stripcaged +========== +For dumping items inside cages. Will mark selected items for dumping, then +a dwarf may come and actually dump it. See also ``autodump``. + +With the ``items`` argument, only dumps items laying in the cage, excluding +stuff worn by caged creatures. ``weapons`` will dump worn weapons, ``armor`` +will dump everything worn by caged creatures (including armor and clothing), +and ``all`` will dump everything, on a creature or not. + +``stripcaged list`` will display on the dfhack console the list of all cages +and their item content. + +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 + ======================= In-game interface tools ======================= diff --git a/scripts/stripcaged.rb b/scripts/stripcaged.rb new file mode 100644 index 000000000..fa9c49552 --- /dev/null +++ b/scripts/stripcaged.rb @@ -0,0 +1,194 @@ +# mark stuff inside of cages for dumping. + +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) + 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 + + puts "#{type} ##{cage.id}: ", count.sort_by { |k, v| v }.map { |k, v| " #{v} #{k}" } + + 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}" } + 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 + puts 'Please select a cage' if list.empty? + +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 + } + puts 'Please use a valid cage id' if list.empty? + +else + list = df.world.items.other[:ANY_CAGE_OR_TRAP] +end + + +# act +case $script_args[0] +when 'items' + cage_dump_items(list) if not list.empty? +when 'armor' + cage_dump_armor(list) if not list.empty? +when 'weapons' + cage_dump_weapons(list) if not list.empty? +when 'all' + cage_dump_all(list) if not list.empty? + +when 'list' + cage_dump_list(list) if not list.empty? + +else + puts <