diff --git a/docs/changelog.txt b/docs/changelog.txt index 4a3428031..7cec154f3 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -66,6 +66,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `autodump`: changed behaviour to only change ``dump`` and ``forbid`` flags if an item is successfully dumped. -@ `autochop`: generate default names for burrows with no assigned names - ``Buildings::StockpileIterator``: fix check for stockpile items on block boundary. +- `dig-now`: fixed multi-layer channel designations only channeling every second layer - `tailor`: block making clothing sized for toads; make replacement clothing orders use the size of the wearer, not the size of the garment -@ `confirm`: fix fps drop when enabled - `channel-safely`: fix an out of bounds error regarding the REPORT event listener receiving (presumably) stale id's @@ -76,6 +77,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `showmood`: now shows the number of items needed for cloth and bars in addition to the technically correct but always confusing "total dimension" (150 per bar or 10,000 per cloth) -@ Stopped mouse clicks from affecting the map when a click on a DFHack screen dismisses the window - `confirm`: configuration data is now persisted globally. +- `dig-now`: added handling of dig designations that have been converted into active jobs - `tailor`: add support for adamantine cloth (off by default); improve logging ## API diff --git a/plugins/dig-now.cpp b/plugins/dig-now.cpp index 6227f44f5..e94cb41da 100644 --- a/plugins/dig-now.cpp +++ b/plugins/dig-now.cpp @@ -6,6 +6,7 @@ #include "PluginManager.h" #include "TileTypes.h" #include "LuaTools.h" +#include "Debug.h" #include "modules/Buildings.h" #include "modules/Gui.h" @@ -14,6 +15,8 @@ #include "modules/Random.h" #include "modules/Units.h" #include "modules/World.h" +#include "modules/EventManager.h" +#include "modules/Job.h" #include #include @@ -26,12 +29,129 @@ #include #include +#include +#include +#include + DFHACK_PLUGIN("dig-now"); REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(world); +// Debugging +namespace DFHack { + DBG_DECLARE(dignow, general, DebugCategory::LINFO); + DBG_DECLARE(dignow, channels, DebugCategory::LINFO); +} + +#define COORD "%" PRIi16 " %" PRIi16 " %" PRIi16 +#define COORDARGS(id) id.x, id.y, id.z + using namespace DFHack; +struct designation{ + df::coord pos; + df::tile_designation type; + df::tile_occupancy occupancy; + designation() = default; + designation(const df::coord &c, const df::tile_designation &td, const df::tile_occupancy &to) : pos(c), type(td), occupancy(to) {} + + bool operator==(const designation &rhs) const { + return pos == rhs.pos; + } + + bool operator!=(const designation &rhs) const { + return !(rhs == *this); + } +}; + +namespace std { + template<> + struct hash { + std::size_t operator()(const designation &c) const { + std::hash hash_coord; + return hash_coord(c.pos); + } + }; +} + +class DesignationJobs { +private: + std::unordered_map designations; + std::unordered_map jobs; +public: + void load(MapExtras::MapCache &map) { + designations.clear(); + df::job_list_link* node = df::global::world->jobs.list.next; + while (node) { + df::job* job = node->item; + if(!job || !Maps::isValidTilePos(job->pos)) + continue; + + node = node->next; + df::tile_designation td = map.designationAt(job->pos); + df::tile_occupancy to = map.occupancyAt(job->pos); + const auto ctd = td.whole; + const auto cto = to.whole; + switch (job->job_type){ + case job_type::Dig: + td.bits.dig = tile_dig_designation::Default; + break; + case job_type::DigChannel: + td.bits.dig = tile_dig_designation::Channel; + break; + case job_type::CarveRamp: + td.bits.dig = tile_dig_designation::Ramp; + break; + case job_type::CarveUpwardStaircase: + td.bits.dig = tile_dig_designation::UpStair; + break; + case job_type::CarveDownwardStaircase: + td.bits.dig = tile_dig_designation::DownStair; + break; + case job_type::CarveUpDownStaircase: + td.bits.dig = tile_dig_designation::UpDownStair; + break; + case job_type::DetailWall: + case job_type::DetailFloor: { + df::tiletype tt = map.tiletypeAt(job->pos); + if (tileSpecial(tt) != df::tiletype_special::SMOOTH) { + td.bits.smooth = 1; + } + break; + } + case job_type::CarveTrack: + to.bits.carve_track_north = (job->item_category.whole >> 18) & 1; + to.bits.carve_track_south = (job->item_category.whole >> 19) & 1; + to.bits.carve_track_west = (job->item_category.whole >> 20) & 1; + to.bits.carve_track_east = (job->item_category.whole >> 21) & 1; + break; + default: + break; + } + if (ctd != td.whole || cto != to.whole) { + // we found a designation job + designations.emplace(job->pos, designation(job->pos, td, to)); + jobs.emplace(job->pos, job); + } + } + } + void remove(const df::coord &pos) { + if(jobs.count(pos)) { + Job::removeJob(jobs[pos]); + jobs.erase(pos); + } + } + designation get(const df::coord &pos) { + if (designations.count(pos)) { + return designations[pos]; + } + return {}; + } + bool count(const df::coord &pos) { + return jobs.count(pos); + } +}; + struct boulder_percent_options { // percent chance ([0..100]) for creating a boulder for the given rock type uint32_t layer; @@ -320,8 +440,19 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, std::vector &dug_tiles) { df::tiletype tt = map.tiletypeAt(pos); - if (!is_diggable(map, pos, tt)) + if (!is_diggable(map, pos, tt)) { + DEBUG(general).print("dig_tile: not diggable\n"); return false; + } + + /** The algorithm process seems to be: + * for each tile + * check for a designation + * if a designation exists send it to dig_tile + * + * dig_tile (below) then digs the layer below the channel designated tile + * thereby changing it and causing its designation to be lost + * */ df::tiletype target_type = df::tiletype::Void; switch(designation) { @@ -341,19 +472,22 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, DFCoord pos_below(pos.x, pos.y, pos.z-1); if (can_dig_channel(tt) && map.ensureBlockAt(pos_below) && is_diggable(map, pos_below, map.tiletypeAt(pos_below))) { + TRACE(channels).print("dig_tile: channeling at (" COORD ") [can_dig_channel: true]\n",COORDARGS(pos_below)); target_type = df::tiletype::OpenSpace; DFCoord pos_above(pos.x, pos.y, pos.z+1); - if (map.ensureBlockAt(pos_above)) + if (map.ensureBlockAt(pos_above)) { remove_ramp_top(map, pos_above); - df::tile_dig_designation td_below = - map.designationAt(pos_below).bits.dig; - if (dig_tile(out, map, pos_below, - df::tile_dig_designation::Ramp, dug_tiles)) { + } + df::tile_dig_designation td_below = map.designationAt(pos_below).bits.dig; + if (dig_tile(out, map, pos_below, df::tile_dig_designation::Ramp, dug_tiles)) { clean_ramps(map, pos_below); - if (td_below == df::tile_dig_designation::Default) + if (td_below == df::tile_dig_designation::Default) { dig_tile(out, map, pos_below, td_below, dug_tiles); + } return true; } + } else { + DEBUG(channels).print("dig_tile: failed to channel at (" COORD ") [can_dig_channel: false]\n", COORDARGS(pos_below)); } break; } @@ -407,7 +541,8 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, if (target_type == df::tiletype::Void || target_type == tt) return false; - dug_tiles.push_back(dug_tile_info(map, pos)); + dug_tiles.emplace_back(map, pos); + TRACE(general).print("dig_tile: digging the designation tile at (" COORD ")\n",COORDARGS(pos)); dig_type(map, pos, target_type); // let light filter down to newly exposed tiles @@ -594,9 +729,12 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, item_coords_t &item_coords, const dig_now_options &options) { MapExtras::MapCache map; Random::MersenneRNG rng; + DesignationJobs jobs; + jobs.load(map); rng.init(); + std::unordered_set buffer; // go down levels instead of up so stacked ramps behave as expected for (int16_t z = options.end.z; z >= options.start.z; --z) { for (int16_t y = options.start.y; y <= options.end.y; ++y) { @@ -609,46 +747,68 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, DFCoord pos(x, y, z); df::tile_designation td = map.designationAt(pos); df::tile_occupancy to = map.occupancyAt(pos); - if (td.bits.dig != df::tile_dig_designation::No && - !to.bits.dig_marked) { - std::vector dug_tiles; - if (dig_tile(out, map, pos, td.bits.dig, dug_tiles)) { - for (auto info : dug_tiles) { - td = map.designationAt(info.pos); - td.bits.dig = df::tile_dig_designation::No; - map.setDesignationAt(info.pos, td); - - dug_coords.push_back(info.pos); - refresh_adjacent_smooth_walls(map, info.pos); - if (info.imat < 0) - continue; - if (produces_item(options.boulder_percents, - map, rng, info)) { - auto k = std::make_pair(info.itype, info.imat); - item_coords[k].push_back(info.pos); - } - } - } - } else if (td.bits.smooth == 1) { - if (smooth_tile(out, map, pos)) { - td = map.designationAt(pos); - td.bits.smooth = 0; - map.setDesignationAt(pos, td); - } - } else if (to.bits.carve_track_north == 1 - || to.bits.carve_track_east == 1 - || to.bits.carve_track_south == 1 - || to.bits.carve_track_west == 1) { - if (carve_tile(map, pos, to)) { - to = map.occupancyAt(pos); - to.bits.carve_track_north = 0; - to.bits.carve_track_east = 0; - to.bits.carve_track_south = 0; - to.bits.carve_track_west = 0; - map.setOccupancyAt(pos, to); + if (jobs.count(pos)) { + buffer.emplace(jobs.get(pos)); + jobs.remove(pos); + // if it does get removed, then we're gonna buffer the jobs info then remove the job + } else if ((td.bits.dig != df::tile_dig_designation::No && !to.bits.dig_marked) + || td.bits.smooth == 1 + || to.bits.carve_track_north == 1 + || to.bits.carve_track_east == 1 + || to.bits.carve_track_south == 1 + || to.bits.carve_track_west == 1) { + + // we're only buffering designations, so that processing doesn't affect what we're buffering + buffer.emplace(pos, td, to); + } + } + } + } + + // process designations + for(auto &d : buffer) { + auto pos = d.pos; + auto td = d.type; + auto to = d.occupancy; + + if (td.bits.dig != df::tile_dig_designation::No && !to.bits.dig_marked) { + std::vector dug_tiles; + + if (dig_tile(out, map, pos, td.bits.dig, dug_tiles)) { + for (auto info: dug_tiles) { + td = map.designationAt(info.pos); + td.bits.dig = df::tile_dig_designation::No; + map.setDesignationAt(info.pos, td); + + dug_coords.push_back(info.pos); + refresh_adjacent_smooth_walls(map, info.pos); + if (info.imat < 0) + continue; + if (produces_item(options.boulder_percents, + map, rng, info)) { + auto k = std::make_pair(info.itype, info.imat); + item_coords[k].push_back(info.pos); } } } + } else if (td.bits.smooth == 1) { + if (smooth_tile(out, map, pos)) { + td = map.designationAt(pos); + td.bits.smooth = 0; + map.setDesignationAt(pos, td); + } + } else if (to.bits.carve_track_north == 1 + || to.bits.carve_track_east == 1 + || to.bits.carve_track_south == 1 + || to.bits.carve_track_west == 1) { + if (carve_tile(map, pos, to)) { + to = map.occupancyAt(pos); + to.bits.carve_track_north = 0; + to.bits.carve_track_east = 0; + to.bits.carve_track_south = 0; + to.bits.carve_track_west = 0; + map.setOccupancyAt(pos, to); + } } }