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.
-@ `autochop`: generate default names for burrows with no assigned names
- ``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
-@ `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)
-@ 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
## Documentation

@ -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 <df/historical_entity.h>
#include <df/map_block.h>
@ -26,12 +29,129 @@
#include <df/world.h>
#include <df/world_site.h>
#include <cinttypes>
#include <unordered_set>
#include <unordered_map>
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<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 {
// 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_tile_info> &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<DFCoord> &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<designation> 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,9 +747,33 @@ static void do_dig(color_ostream &out, std::vector<DFCoord> &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) {
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_tile_info> dug_tiles;
if (dig_tile(out, map, pos, td.bits.dig, dug_tiles)) {
for (auto info: dug_tiles) {
td = map.designationAt(info.pos);
@ -631,7 +793,7 @@ static void do_dig(color_ostream &out, std::vector<DFCoord> &dug_coords,
}
} else if (td.bits.smooth == 1) {
if (smooth_tile(out, map, pos)) {
to = map.occupancyAt(pos);
td = map.designationAt(pos);
td.bits.smooth = 0;
map.setDesignationAt(pos, td);
}
@ -649,8 +811,6 @@ static void do_dig(color_ostream &out, std::vector<DFCoord> &dug_coords,
}
}
}
}
}
map.WriteAll();
}