diff --git a/NEWS b/NEWS index 297591e1e..8b6cf82dd 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,10 @@ DFHack future New commands: - restrictliquid - Restrict traffic on every visible square with liquid. - restrictice - Restrict traffic on squares above visible ice. + New scripts: + - masspit: designate caged creatures in a zone for pitting + Misc improvements: + - exterminate: renamed from slayrace, add help message DFHack v0.34.11-r3 diff --git a/Readme.rst b/Readme.rst index 415de29dd..53ae24389 100644 --- a/Readme.rst +++ b/Readme.rst @@ -1882,8 +1882,8 @@ Internals: the thoughts are set to be very old, so that the game remove them quickly after you unpause. -slayrace -======== +exterminate +=========== Kills any unit of a given race. With no argument, lists the available races and count eligible targets. @@ -1906,15 +1906,15 @@ Will target any unit on a revealed tile of the map, including ambushers. Ex:: - slayrace gob + exterminate gob To kill a single creature, select the unit with the 'v' cursor and:: - slayrace him + exterminate him To purify all elves on the map with fire (may have side-effects):: - slayrace elve magma + exterminate elve magma magmasource @@ -1936,6 +1936,13 @@ To remove all placed sources, call ``magmasource stop``. With no argument, this command shows an help message and list existing sources. +masspit +======= +Designate all creatures in cages on top of a pit/pond activity zone for pitting. +Works best with an animal stockpile on top of the zone. + +Works with a zone number as argument (eg ``Activity Zone #6`` -> ``masspit 6``) +or with the game cursor on top of the area. digfort ======= diff --git a/plugins/createitem.cpp b/plugins/createitem.cpp index 8764f6ae2..9697a3947 100644 --- a/plugins/createitem.cpp +++ b/plugins/createitem.cpp @@ -46,18 +46,23 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out ) return CR_OK; } -bool makeItem (df::reaction_product_itemst *prod, df::unit *unit, bool glove2 = false) +bool makeItem (df::reaction_product_itemst *prod, df::unit *unit, bool second_item = false) { vector out_items; vector in_reag; vector in_items; bool is_gloves = (prod->item_type == df::item_type::GLOVES); + bool is_shoes = (prod->item_type == df::item_type::SHOES); prod->produce(unit, &out_items, &in_reag, &in_items, 1, df::job_skill::NONE, df::historical_entity::find(unit->civ_id), ((*gametype == df::game_type::DWARF_MAIN) || (*gametype == df::game_type::DWARF_RECLAIM)) ? df::world_site::find(ui->site_id) : NULL); if (!out_items.size()) return false; + // if we asked to make shoes and we got twice as many as we asked, then we're okay + // otherwise, make a second set because shoes are normally made in pairs + if (is_shoes && out_items.size() == prod->count * 2) + is_shoes = false; for (size_t i = 0; i < out_items.size(); i++) { out_items[i]->moveToGround(unit->pos.x, unit->pos.y, unit->pos.z); @@ -67,10 +72,10 @@ bool makeItem (df::reaction_product_itemst *prod, df::unit *unit, bool glove2 = if (out_items[i]->getGloveHandedness() > 0) is_gloves = false; else - out_items[i]->setGloveHandedness(glove2 ? 2 : 1); + out_items[i]->setGloveHandedness(second_item ? 2 : 1); } } - if (is_gloves && !glove2) + if ((is_gloves || is_shoes) && !second_item) return makeItem(prod, unit, true); return true; @@ -248,7 +253,9 @@ command_result df_createitem (color_ostream &out, vector & parameters) break; } - if (!makeItem(prod, unit)) + bool result = makeItem(prod, unit); + delete prod; + if (!result) { out.printerr("Failed to create item!\n"); return CR_FAILURE; diff --git a/plugins/search.cpp b/plugins/search.cpp index 4e502b838..541fc9506 100644 --- a/plugins/search.cpp +++ b/plugins/search.cpp @@ -422,12 +422,17 @@ protected: virtual bool can_init(S *screen) { auto list = getLayerList(screen); - if (!list->active) + if (!is_list_valid(screen) || !list->active) return false; return true; } + virtual bool is_list_valid(S*) + { + return true; + } + virtual void do_search() { search_generic::do_search(); @@ -444,8 +449,12 @@ protected: virtual void clear_search() { search_generic::clear_search(); - auto list = getLayerList(this->viewscreen); - list->num_entries = this->get_primary_list()->size(); + + if (is_list_valid(this->viewscreen)) + { + auto list = getLayerList(this->viewscreen); + list->num_entries = this->get_primary_list()->size(); + } } private: @@ -1208,12 +1217,14 @@ public: return 'q'; } - bool can_init(df::viewscreen_layer_militaryst *screen) + // When not on the positions page, this list is used for something + // else entirely, so screwing with it seriously breaks stuff. + bool is_list_valid(df::viewscreen_layer_militaryst *screen) { if (screen->page != df::viewscreen_layer_militaryst::Positions) return false; - return military_search_base::can_init(screen); + return true; } vector *get_primary_list() diff --git a/scripts/autofarm.rb b/scripts/autofarm.rb index e1dc62ae3..b8a6871e9 100644 --- a/scripts/autofarm.rb +++ b/scripts/autofarm.rb @@ -6,10 +6,12 @@ class AutoFarm end def setthreshold(id, v) - if df.world.raws.plants.all.find { |r| r.id == id } - @thresholds[id] = v.to_i + list = df.world.raws.plants.all.find_all { |plt| plt.flags[:SEED] }.map { |plt| plt.id } + if tok = df.match_rawname(id, list) + @thresholds[tok] = v.to_i else - puts "No plant with id #{id}" + puts "No plant with id #{id}, try one of " + + list.map { |w| w =~ /[^\w]/ ? w.inspect : w }.sort.join(' ') end end @@ -17,11 +19,11 @@ class AutoFarm @thresholds.default = v.to_i end - def is_plantable (plant) + def is_plantable(plant) has_seed = plant.flags[:SEED] season = df.cur_season harvest = df.cur_season_tick + plant.growdur * 10 - will_finish = harvest < 10080 + will_finish = harvest < 10080 can_plant = has_seed && plant.flags[season] can_plant = can_plant && (will_finish || plant.flags[(season+1)%4]) can_plant @@ -36,7 +38,7 @@ class AutoFarm !i.flags.hostile && !i.flags.on_fire && !i.flags.rotten && !i.flags.trader && !i.flags.in_building && !i.flags.construction && !i.flags.artifact) - counts[i.mat_index] = counts[i.mat_index] + i.stack_size + counts[i.mat_index] += i.stack_size end } @@ -53,7 +55,7 @@ class AutoFarm return plantable end - def set_farms( plants, farms) + def set_farms(plants, farms) return if farms.length == 0 if plants.length == 0 plants = [-1] @@ -61,41 +63,36 @@ class AutoFarm season = df.cur_season - idx = 0 - - farms.each { |f| - f.plant_id[season] = plants[idx] - idx = (idx + 1) % plants.length + farms.each_with_index { |f, idx| + f.plant_id[season] = plants[idx % plants.length] } end def process - return false unless @running - plantable = find_plantable_plants - counts = Hash.new(0) + @lastcounts = Hash.new(0) df.world.items.other[:PLANT].each { |i| if (!i.flags.dump && !i.flags.forbid && !i.flags.garbage_collect && !i.flags.hostile && !i.flags.on_fire && !i.flags.rotten && !i.flags.trader && !i.flags.in_building && !i.flags.construction && !i.flags.artifact && plantable.has_key?(i.mat_index)) - counts[i.mat_index] = counts[i.mat_index] + i.stack_size + id = df.world.raws.plants.all[i.mat_index].id + @lastcounts[id] += i.stack_size end } + return unless @running + plants_s = [] plants_u = [] - @lastcounts.clear - plantable.each_key { |k| plant = df.world.raws.plants.all[k] - if (counts[k] < @thresholds[plant.id]) + if (@lastcounts[plant.id] < @thresholds[plant.id]) plants_s.push(k) if plantable[k] == :Surface plants_u.push(k) if plantable[k] == :Underground end - @lastcounts[plant.id] = counts[k] } farms_s = [] @@ -110,11 +107,11 @@ class AutoFarm set_farms(plants_s, farms_s) set_farms(plants_u, farms_u) - end def start - @onupdate = df.onupdate_register('autofarm', 100) { process } + return if @running + @onupdate = df.onupdate_register('autofarm', 1200) { process } @running = true end @@ -125,23 +122,28 @@ class AutoFarm def status stat = @running ? "Running." : "Stopped." + @lastcounts.each { |k,v| + stat << "\n#{k} limit #{@thresholds.fetch(k, 'default')} current #{v}" + } @thresholds.each { |k,v| - stat += "\n#{k} limit #{v} current #{@lastcounts[k]}" + stat << "\n#{k} limit #{v} current 0" unless @lastcounts.has_key?(k) } - stat += "\nDefault: #{@thresholds.default}" + stat << "\nDefault: #{@thresholds.default}" stat end end -$AutoFarm = AutoFarm.new unless $AutoFarm +$AutoFarm ||= AutoFarm.new case $script_args[0] when 'start' - $AutoFarm.start + $AutoFarm.start + puts $AutoFarm.status -when 'end', 'stop' - $AutoFarm.stop +when 'end', 'stop', 'disable' + $AutoFarm.stop + puts 'Stopped.' when 'default' $AutoFarm.setdefault($script_args[1]) @@ -156,10 +158,19 @@ when 'delete' $AutoFarm.stop $AutoFarm = nil +when 'help', '?' + puts < 5 + df.curview.feed_keys(:CURSOR_DOWN_Z) + df.curview.feed_keys(:CURSOR_UP_Z) +else + df.curview.feed_keys(:CURSOR_UP_Z) + df.curview.feed_keys(:CURSOR_DOWN_Z) +end diff --git a/scripts/slayrace.rb b/scripts/exterminate.rb similarity index 73% rename from scripts/slayrace.rb rename to scripts/exterminate.rb index 4844538d4..72d1a4b4a 100644 --- a/scripts/slayrace.rb +++ b/scripts/exterminate.rb @@ -1,4 +1,4 @@ -# slay all creatures of a given race +# exterminate creatures # race = name of the race to eradicate, use 'him' to target only the selected creature # use 'undead' to target all undeads @@ -24,7 +24,7 @@ slayit = lambda { |u| else # it's getting hot around here # !!WARNING!! do not call on a magma-safe creature - ouh = df.onupdate_register("slayrace ensure #{u.id}", 1) { + ouh = df.onupdate_register("exterminate ensure #{u.id}", 1) { if u.flags1.dead df.onupdate_unregister(ouh) else @@ -55,7 +55,23 @@ case race when nil all_races.sort_by { |race, cnt| [cnt, race] }.each{ |race, cnt| puts " #{race} #{cnt}" } -when 'him' +when 'help', '?' + puts <= df.cursor.x and zone.y1 <= df.cursor.y and zone.y2 >= df.cursor.y + } + +end + +if not bld + puts "Please select a pit/pond zone" + throw :script_finished +end + +found = 0 +df.world.items.other[:CAGE].each { |cg| + next if not cg.flags.on_ground + next if cg.pos.z != bld.z or cg.pos.x < bld.x1 or cg.pos.x > bld.x2 or cg.pos.y < bld.y1 or cg.pos.y > bld.y2 + next if not uref = cg.general_refs.grep(DFHack::GeneralRefContainsUnitst).first + found += 1 + u = uref.unit_tg + puts "Pitting #{u.race_tg.name[0]} #{u.id} #{u.name}" + u.general_refs << DFHack::GeneralRefBuildingCivzoneAssignedst.cpp_new(:building_id => bld.id) + bld.assigned_creature << u.id +} +puts "No creature available for pitting" if found == 0 diff --git a/scripts/soundsense-season.lua b/scripts/soundsense-season.lua index 6b7d43cfa..c2b57e35e 100644 --- a/scripts/soundsense-season.lua +++ b/scripts/soundsense-season.lua @@ -1,6 +1,7 @@ -- On map load writes the current season to gamelog.txt local seasons = { + [-1] = 'Nothing', -- worldgen [0] = 'Spring', [1] = 'Summer', [2] = 'Autumn',