diff --git a/NEWS b/NEWS index 5072cebc3..40e9315c5 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,7 @@ DFHack future Misc improvements: - fastdwarf: new mode using debug flags, and some internal consistency fixes. - added a small stand-alone utility for applying and removing binary patches. + - removebadthoughts: add --dry-run option DFHack v0.34.11-r2 diff --git a/Readme.rst b/Readme.rst index bed837f0b..39d44d975 100644 --- a/Readme.rst +++ b/Readme.rst @@ -1662,16 +1662,18 @@ removebadthoughts This script remove negative thoughts from your dwarves. Very useful against tantrum spirals. -With a selected unit in 'v' mode, will clear this unit's mind, otherwise -clear all your fort's units minds. +The script can target a single creature, when used with the ``him`` argument, +or the whole fort population, with ``all``. + +To show every bad thought present without actually removing them, run the +script with the ``-n`` or ``--dry-run`` argument. This can give a quick +hint on what bothers your dwarves the most. Individual dwarf happiness may not increase right after this command is run, but in the short term your dwarves will get much more joyful. -The thoughts are set to be very old, and the game will remove them soon when -you unpause. -With the optional ``-v`` parameter, the script will dump the negative thoughts -it removed. +Internals: the thoughts are set to be very old, so that the game remove them +quickly after you unpause. slayrace @@ -1680,7 +1682,7 @@ Kills any unit of a given race. With no argument, lists the available races. -With the special argument 'him', targets only the selected creature. +With the special argument ``him``, targets only the selected creature. Any non-dead non-caged unit of the specified race gets its ``blood_count`` set to 0, which means immediate death at the next game tick. For creatures diff --git a/plugins/ruby/building.rb b/plugins/ruby/building.rb index 59f715515..08c12a841 100644 --- a/plugins/ruby/building.rb +++ b/plugins/ruby/building.rb @@ -352,23 +352,13 @@ module DFHack job end - # check item flags to see if it is suitable for use as a building material - def building_isitemfree(i) - !i.flags.in_job and - !i.flags.in_inventory and - !i.flags.removed and - !i.flags.in_building and - !i.flags.owned and - !i.flags.forbid - end - # exemple usage def buildbed(pos=cursor) raise 'where to ?' if pos.x < 0 item = world.items.all.find { |i| i.kind_of?(ItemBedst) and - building_isitemfree(i) + item_isfree(i) } raise 'no free bed, build more !' if not item diff --git a/plugins/ruby/item.rb b/plugins/ruby/item.rb index fc840f7ad..469ec7449 100644 --- a/plugins/ruby/item.rb +++ b/plugins/ruby/item.rb @@ -6,8 +6,12 @@ module DFHack if what == :selected case curview._rtti_classname when :viewscreen_itemst - ref = curview.entry_ref[curview.cursor_pos] - ref.item_tg if ref.kind_of?(GeneralRefItem) + if ref = curview.entry_ref[curview.cursor_pos] + ref.item_tg if ref.kind_of?(GeneralRefItem) + else + # not a container + curview.item + end when :viewscreen_storesst # z/stocks if curview.in_group_mode == 0 and curview.in_right_list == 1 curview.items[curview.item_cursor] @@ -47,5 +51,17 @@ module DFHack raise "what what?" end end + + # check item flags to see if it is suitable for use as a job input material + def item_isfree(i) + !i.flags.trader and + !i.flags.in_job and + !i.flags.in_inventory and + !i.flags.removed and + !i.flags.in_building and + !i.flags.owned and + !i.flags.forbid + end + end end diff --git a/plugins/ruby/map.rb b/plugins/ruby/map.rb index 371614704..7c616d722 100644 --- a/plugins/ruby/map.rb +++ b/plugins/ruby/map.rb @@ -240,5 +240,35 @@ module DFHack def spawn_magma(quantity=7) spawn_liquid(quantity, true) end + + # yield a serie of tiles until the block returns true, returns the matching tile + # the yielded tiles form a (squared) spiral centered here in the current zlevel + # eg for radius 4, yields (-4, -4), (-4, -3), .., (-4, 3), + # (-4, 4), (-3, 4), .., (4, 4), .., (4, -4), .., (-3, -4) + # then move on to radius 5 + def spiral_search(maxradius=100, minradius=0, step=1) + if minradius == 0 + return self if yield self + minradius += step + end + + sides = [[0, 1], [1, 0], [0, -1], [-1, 0]] + (minradius..maxradius).step(step) { |r| + sides.length.times { |s| + dxr, dyr = sides[(s-1) % sides.length] + dx, dy = sides[s] + (-r...r).step(step) { |v| + t = offset(dxr*r + dx*v, dyr*r + dy*v) + return t if t and yield t + } + } + } + nil + end + + # returns dx^2+dy^2+dz^2 + def distance_to(ot) + (x-ot.x)**2 + (y-ot.y)**2 + (z-ot.z)**2 + end end end diff --git a/plugins/ruby/material.rb b/plugins/ruby/material.rb index 4a92118d6..ca0a64779 100644 --- a/plugins/ruby/material.rb +++ b/plugins/ruby/material.rb @@ -189,6 +189,10 @@ module DFHack end def to_s ; token ; end + + def ===(other) + other.mat_index == mat_index and other.mat_type == mat_type + end end class << self diff --git a/plugins/ruby/ruby-autogen-defs.rb b/plugins/ruby/ruby-autogen-defs.rb index 64c08facb..4470c8022 100644 --- a/plugins/ruby/ruby-autogen-defs.rb +++ b/plugins/ruby/ruby-autogen-defs.rb @@ -785,6 +785,7 @@ module DFHack def isset(key) raise unless @_memaddr key = @_enum.int(key) if _enum + raise "unknown key #{key.inspect}" if key.kind_of?(::Symbol) DFHack.memory_stlset_isset(@_memaddr, key) end alias is_set? isset @@ -792,12 +793,14 @@ module DFHack def set(key) raise unless @_memaddr key = @_enum.int(key) if _enum + raise "unknown key #{key.inspect}" if key.kind_of?(::Symbol) DFHack.memory_stlset_set(@_memaddr, key) end def delete(key) raise unless @_memaddr key = @_enum.int(key) if _enum + raise "unknown key #{key.inspect}" if key.kind_of?(::Symbol) DFHack.memory_stlset_deletekey(@_memaddr, key) end diff --git a/plugins/zone.cpp b/plugins/zone.cpp index 542ce8a02..2c63b6af7 100644 --- a/plugins/zone.cpp +++ b/plugins/zone.cpp @@ -3498,11 +3498,11 @@ command_result start_autonestbox(color_ostream &out) { enable_autonestbox = true; - if (!config_autobutcher.isValid()) + if (!config_autonestbox.isValid()) { config_autonestbox = World::AddPersistentData("autonestbox/config"); - if (!config_autobutcher.isValid()) + if (!config_autonestbox.isValid()) { out << "Cannot enable autonestbox without a world!" << endl; return CR_OK; diff --git a/scripts/deathcause.rb b/scripts/deathcause.rb index 178ebbc87..0ed54d81a 100644 --- a/scripts/deathcause.rb +++ b/scripts/deathcause.rb @@ -1,15 +1,13 @@ # show death cause of a creature -def display_event(e) - p e if $DEBUG - - str = "#{e.victim_tg.name} died in year #{e.year}" - str << " of #{e.death_cause}" if false - str << " killed by the #{e.slayer_race_tg.name[0]} #{e.slayer_tg.name}" if e.slayer != -1 +def display_death_event(e) + str = "The #{e.victim_hf_tg.race_tg.name[0]} #{e.victim_hf_tg.name} died in year #{e.year}" + str << " (cause: #{e.death_cause.to_s.downcase})," + str << " killed by the #{e.slayer_race_tg.name[0]} #{e.slayer_hf_tg.name}" if e.slayer_hf != -1 str << " using a #{df.world.raws.itemdefs.weapons[e.weapon.item_subtype].name}" if e.weapon.item_type == :WEAPON str << ", shot by a #{df.world.raws.itemdefs.weapons[e.weapon.bow_item_subtype].name}" if e.weapon.bow_item_type == :WEAPON - puts str + '.' + puts str.chomp(',') + '.' end item = df.item_find(:selected) @@ -21,14 +19,15 @@ end if !item or !item.kind_of?(DFHack::ItemBodyComponent) puts "Please select a corpse in the loo'k' menu" else - hfig = item.hist_figure_id - if hfig == -1 - puts "Not a historical figure, cannot find info" + hf = item.hist_figure_id + if hf == -1 + # TODO try to retrieve info from the unit (u = item.unit_tg) + puts "Not a historical figure, cannot death find info" else events = df.world.history.events (0...events.length).reverse_each { |i| - if events[i].kind_of?(DFHack::HistoryEventHistFigureDiedst) and events[i].victim == hfig - display_event(events[i]) + if events[i].kind_of?(DFHack::HistoryEventHistFigureDiedst) and events[i].victim_hf == hf + display_death_event(events[i]) break end } diff --git a/scripts/removebadthoughts.rb b/scripts/removebadthoughts.rb index 99b742643..ff98f8f65 100644 --- a/scripts/removebadthoughts.rb +++ b/scripts/removebadthoughts.rb @@ -1,27 +1,43 @@ # remove bad thoughts for the selected unit or the whole fort -# with removebadthoughts -v, dump the bad thoughts types we removed -verbose = $script_args.delete('-v') +dry_run = $script_args.delete('--dry-run') || $script_args.delete('-n') -if u = df.unit_find(:selected) - targets = [u] -else - targets = df.unit_citizens -end +$script_args << 'all' if dry_run and $script_args.empty? seenbad = Hash.new(0) -targets.each { |u| +clear_mind = lambda { |u| u.status.recent_events.each { |e| next if DFHack::UnitThoughtType::Value[e.type].to_s[0, 1] != '-' seenbad[e.type] += 1 - e.age = 0x1000_0000 + e.age = 0x1000_0000 unless dry_run } } -if verbose - seenbad.sort_by { |k, v| v }.each { |k, v| puts " #{v} #{k}" } -end +summary = lambda { + seenbad.sort_by { |thought, cnt| cnt }.each { |thought, cnt| + puts " #{thought} #{cnt}" + } + count = seenbad.values.inject(0) { |sum, cnt| sum+cnt } + puts "Removed #{count} bad thought#{'s' if count != 1}." if count > 0 and not dry_run +} -count = seenbad.values.inject(0) { |s, v| s+v } -puts "removed #{count} bad thought#{'s' if count != 1}" +case $script_args[0] +when 'him' + if u = df.unit_find + clear_mind[u] + summary[] + else + puts 'Please select a dwarf ingame' + end + +when 'all' + df.unit_citizens.each { |uu| + clear_mind[uu] + } + summary[] + +else + puts "Usage: removebadthoughts [--dry-run] " + +end diff --git a/scripts/slayrace.rb b/scripts/slayrace.rb index b1fc24e35..749d0189b 100644 --- a/scripts/slayrace.rb +++ b/scripts/slayrace.rb @@ -33,12 +33,14 @@ slayit = lambda { |u| end } -all_races = df.world.units.active.map { |u| - u.race_tg.creature_id if checkunit[u] -}.compact.uniq.sort +all_races = Hash.new(0) + +df.world.units.active.map { |u| + all_races[u.race_tg.creature_id] += 1 if checkunit[u] +} if !race - puts all_races + all_races.sort_by { |race, cnt| [cnt, race] }.each{ |race, cnt| puts " #{race} #{cnt}" } elsif race == 'him' if him = df.unit_find slayit[him] @@ -46,7 +48,7 @@ elsif race == 'him' puts "Choose target" end else - raw_race = df.match_rawname(race, all_races) + raw_race = df.match_rawname(race, all_races.keys) raise 'invalid race' if not raw_race race_nr = df.world.raws.creatures.all.index { |cr| cr.creature_id == raw_race }