Merge pull request #2908 from cppcooper/dig-now

dig-now bug fixing
develop
Myk 2023-02-22 11:22:59 -08:00 committed by GitHub
commit c54a2346f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 207 additions and 45 deletions

@ -42,6 +42,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. - `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 -@ `autochop`: generate default names for burrows with no assigned names
- ``Buildings::StockpileIterator``: check for stockpile items on block boundary. - ``Buildings::StockpileIterator``: 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; add support for adamantine cloth (off by default); improve logging - `tailor`: block making clothing sized for toads; make replacement clothing orders use the size of the wearer, not the size of the garment; add support for adamantine cloth (off by default); improve logging
-@ `confirm`: fix fps drop when enabled -@ `confirm`: fix fps drop when enabled
@ -50,6 +51,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) - `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 -@ Stopped mouse clicks from affecting the map when a click on a DFHack screen dismisses the window
- `confirm`: configuration data is now persisted globally. - `confirm`: configuration data is now persisted globally.
- `dig-now`: added handling of dig designations that have been converted into active jobs
## Documentation ## Documentation

@ -6,6 +6,7 @@
#include "PluginManager.h" #include "PluginManager.h"
#include "TileTypes.h" #include "TileTypes.h"
#include "LuaTools.h" #include "LuaTools.h"
#include "Debug.h"
#include "modules/Buildings.h" #include "modules/Buildings.h"
#include "modules/Gui.h" #include "modules/Gui.h"
@ -14,6 +15,8 @@
#include "modules/Random.h" #include "modules/Random.h"
#include "modules/Units.h" #include "modules/Units.h"
#include "modules/World.h" #include "modules/World.h"
#include "modules/EventManager.h"
#include "modules/Job.h"
#include <df/historical_entity.h> #include <df/historical_entity.h>
#include <df/map_block.h> #include <df/map_block.h>
@ -26,12 +29,129 @@
#include <df/world.h> #include <df/world.h>
#include <df/world_site.h> #include <df/world_site.h>
#include <cinttypes>
#include <unordered_set>
#include <unordered_map>
DFHACK_PLUGIN("dig-now"); DFHACK_PLUGIN("dig-now");
REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(plotinfo);
REQUIRE_GLOBAL(world); 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; 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<designation> {
std::size_t operator()(const designation &c) const {
std::hash<df::coord> hash_coord;
return hash_coord(c.pos);
}
};
}
class DesignationJobs {
private:
std::unordered_map<df::coord, designation> designations;
std::unordered_map<df::coord, df::job*> 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 { struct boulder_percent_options {
// percent chance ([0..100]) for creating a boulder for the given rock type // percent chance ([0..100]) for creating a boulder for the given rock type
uint32_t layer; uint32_t layer;
@ -320,8 +440,19 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map,
std::vector<dug_tile_info> &dug_tiles) { std::vector<dug_tile_info> &dug_tiles) {
df::tiletype tt = map.tiletypeAt(pos); 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; 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; df::tiletype target_type = df::tiletype::Void;
switch(designation) { 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); DFCoord pos_below(pos.x, pos.y, pos.z-1);
if (can_dig_channel(tt) && map.ensureBlockAt(pos_below) if (can_dig_channel(tt) && map.ensureBlockAt(pos_below)
&& is_diggable(map, pos_below, map.tiletypeAt(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; target_type = df::tiletype::OpenSpace;
DFCoord pos_above(pos.x, pos.y, pos.z+1); 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); remove_ramp_top(map, pos_above);
df::tile_dig_designation td_below = }
map.designationAt(pos_below).bits.dig; df::tile_dig_designation td_below = map.designationAt(pos_below).bits.dig;
if (dig_tile(out, map, pos_below, if (dig_tile(out, map, pos_below, df::tile_dig_designation::Ramp, dug_tiles)) {
df::tile_dig_designation::Ramp, dug_tiles)) {
clean_ramps(map, pos_below); 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); dig_tile(out, map, pos_below, td_below, dug_tiles);
}
return true; return true;
} }
} else {
DEBUG(channels).print("dig_tile: failed to channel at (" COORD ") [can_dig_channel: false]\n", COORDARGS(pos_below));
} }
break; break;
} }
@ -407,7 +541,8 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map,
if (target_type == df::tiletype::Void || target_type == tt) if (target_type == df::tiletype::Void || target_type == tt)
return false; 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); dig_type(map, pos, target_type);
// let light filter down to newly exposed tiles // let light filter down to newly exposed tiles
@ -594,9 +729,12 @@ static void do_dig(color_ostream &out, std::vector<DFCoord> &dug_coords,
item_coords_t &item_coords, const dig_now_options &options) { item_coords_t &item_coords, const dig_now_options &options) {
MapExtras::MapCache map; MapExtras::MapCache map;
Random::MersenneRNG rng; Random::MersenneRNG rng;
DesignationJobs jobs;
jobs.load(map);
rng.init(); rng.init();
std::unordered_set<designation> buffer;
// go down levels instead of up so stacked ramps behave as expected // 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 z = options.end.z; z >= options.start.z; --z) {
for (int16_t y = options.start.y; y <= options.end.y; ++y) { 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<DFCoord> &dug_coords,
DFCoord pos(x, y, z); DFCoord pos(x, y, z);
df::tile_designation td = map.designationAt(pos); df::tile_designation td = map.designationAt(pos);
df::tile_occupancy to = map.occupancyAt(pos); df::tile_occupancy to = map.occupancyAt(pos);
if (td.bits.dig != df::tile_dig_designation::No && if (jobs.count(pos)) {
!to.bits.dig_marked) { buffer.emplace(jobs.get(pos));
std::vector<dug_tile_info> dug_tiles; jobs.remove(pos);
if (dig_tile(out, map, pos, td.bits.dig, dug_tiles)) { // if it does get removed, then we're gonna buffer the jobs info then remove the job
for (auto info : dug_tiles) { } else if ((td.bits.dig != df::tile_dig_designation::No && !to.bits.dig_marked)
td = map.designationAt(info.pos); || td.bits.smooth == 1
td.bits.dig = df::tile_dig_designation::No; || to.bits.carve_track_north == 1
map.setDesignationAt(info.pos, td); || to.bits.carve_track_east == 1
|| to.bits.carve_track_south == 1
dug_coords.push_back(info.pos); || to.bits.carve_track_west == 1) {
refresh_adjacent_smooth_walls(map, info.pos);
if (info.imat < 0) // we're only buffering designations, so that processing doesn't affect what we're buffering
continue; buffer.emplace(pos, td, to);
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); }
}
} // process designations
} for(auto &d : buffer) {
} else if (td.bits.smooth == 1) { auto pos = d.pos;
if (smooth_tile(out, map, pos)) { auto td = d.type;
to = map.occupancyAt(pos); auto to = d.occupancy;
td.bits.smooth = 0;
map.setDesignationAt(pos, td); if (td.bits.dig != df::tile_dig_designation::No && !to.bits.dig_marked) {
} std::vector<dug_tile_info> dug_tiles;
} else if (to.bits.carve_track_north == 1
|| to.bits.carve_track_east == 1 if (dig_tile(out, map, pos, td.bits.dig, dug_tiles)) {
|| to.bits.carve_track_south == 1 for (auto info: dug_tiles) {
|| to.bits.carve_track_west == 1) { td = map.designationAt(info.pos);
if (carve_tile(map, pos, to)) { td.bits.dig = df::tile_dig_designation::No;
to = map.occupancyAt(pos); map.setDesignationAt(info.pos, td);
to.bits.carve_track_north = 0;
to.bits.carve_track_east = 0; dug_coords.push_back(info.pos);
to.bits.carve_track_south = 0; refresh_adjacent_smooth_walls(map, info.pos);
to.bits.carve_track_west = 0; if (info.imat < 0)
map.setOccupancyAt(pos, to); 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);
}
} }
} }