diff --git a/plugins/dig-now.cpp b/plugins/dig-now.cpp index 259978f9bf..a715bbd3c8 100644 --- a/plugins/dig-now.cpp +++ b/plugins/dig-now.cpp @@ -5,6 +5,7 @@ #include "DataFuncs.h" #include "Debug.h" #include "LuaTools.h" +//#include "LuaWrapper.h" #include "PluginManager.h" #include "PluginLua.h" #include "TileTypes.h" @@ -14,12 +15,14 @@ #include "modules/Gui.h" #include "modules/Job.h" #include "modules/Maps.h" -#include "modules/MapCache.h" +#include "modules/Materials.h" #include "modules/Random.h" #include "modules/Units.h" #include "modules/World.h" -#include "df/building.h" +#include +#include +#include #include "df/builtin_mats.h" #include "df/historical_entity.h" #include "df/item.h" @@ -27,14 +30,20 @@ #include "df/material.h" #include "df/plotinfost.h" #include "df/reaction_product_itemst.h" +#include "df/region_map_entry.h" #include "df/tile_designation.h" #include "df/tile_occupancy.h" #include "df/unit.h" #include "df/vermin.h" #include "df/world.h" +#include "df/world_data.h" +#include "df/world_geo_biome.h" +#include "df/world_geo_layer.h" +#include "df/world_region_details.h" #include "df/world_site.h" #include +#include #include #include @@ -48,8 +57,336 @@ namespace DFHack { DBG_DECLARE(dignow, channels, DebugCategory::LINFO); } +using std::min; +using std::vector; using namespace DFHack; +static const df::region_map_entry* biome_at(const df::map_block &block, const df::coord2d p) +{ + auto blockptr = &static_cast(const_cast(block)); + auto biome = Maps::getRegionBiome(Maps::getBlockTileBiomeRgn(blockptr, p)); + return biome; +} + +#if false // gcc warns about unused functions, and warnings are errors. +static const df::region_map_entry* biome_at(const df::coord pos) +{ + auto block = Maps::getTileBlock(pos); + if (!block) + return nullptr; + auto blockref = static_cast(*block); + return biome_at(blockref, df::coord2d(pos) & 15); +} +#endif + +static const df::world_geo_biome* geo_biome_at(const df::map_block &block, const df::coord2d p) +{ + auto biome = biome_at(block, p); + if (!biome) + return nullptr; + if (biome->geo_index < 0) + return nullptr; + if (static_cast(biome->geo_index) >= df::world_geo_biome::get_vector().size()) + return nullptr; + auto geo_biome = df::world_geo_biome::get_vector()[biome->geo_index]; + return geo_biome; +} + +#if false // gcc warns about unused functions, and warnings are errors. +static const df::world_geo_biome* geo_biome_at(const df::coord pos) +{ + auto block = Maps::getTileBlock(pos); + if (!block) + return nullptr; + auto blockref = static_cast(*block); + return geo_biome_at(blockref, df::coord2d(pos) & 15); +} +#endif + +static const t_matpair layer_inorganic_n(const df::map_block &block, const df::coord2d p, size_t layer) +{ + using namespace df::enums::builtin_mats; + auto geo_biome = geo_biome_at(block, p); + if (!geo_biome) + return t_matpair(INORGANIC, -1); // can't happen, generic "rock" + // gcc-11 complained that 0 was of different signed-ness than size_t! trying to cast it to unsigned. + layer = clip_range(layer, static_cast(0), geo_biome->layers.size() - 1); + return t_matpair(INORGANIC, geo_biome->layers[layer]->mat_index); +} + +#if false // gcc warns about unused functions, and warnings are errors. +static const t_matpair layer_inorganic_n(const df::coord pos, size_t layer) +{ + using namespace df::enums::builtin_mats; + auto block = Maps::getTileBlock(pos); + if (!block) + return t_matpair(INORGANIC, -1); // can't happen, generic "rock" + auto blockref = static_cast(*block); + return layer_inorganic_n(blockref, df::coord2d(pos) & 15, layer); +} +#endif + +static const size_t geolayer_at(const df::map_block &block, df::coord2d p) +{ + return index_tile(block.designation, p).bits.geolayer_index; +} + +static const t_matpair layer_inorganic_at(const df::map_block &block, df::coord2d p) +{ + return layer_inorganic_n(block, p, geolayer_at(block, p)); +} + +#if false // gcc warns about unused functions, and warnings are errors. +static const t_matpair layer_inorganic_at(const df::coord pos) +{ + using namespace df::enums::builtin_mats; + auto block = Maps::getTileBlock(pos); + if (!block) + return t_matpair(INORGANIC, -1); // generic "rock" + auto blockref = const_cast(static_cast(*block)); + return layer_inorganic_at(blockref, df::coord2d(pos) & 15); +} +#endif + +static const df::enums::tiletype_material::tiletype_material getGroundType(int32_t mat_index) +{ + if (isSoilInorganic(mat_index)) + return df::enums::tiletype_material::SOIL; + if (isStoneInorganic(mat_index)) + return df::enums::tiletype_material::STONE; + return df::enums::tiletype_material::NONE; +} + +static const df::enums::tiletype_material::tiletype_material getGroundType(const t_matpair matpair) +{ + using namespace df::enums::builtin_mats; + if (matpair.mat_type != INORGANIC) + return df::enums::tiletype_material::NONE; + return getGroundType(matpair.mat_index); +} + +// copied from Maps, used by getBiomeRgnPos() +static df::coord2d biome_offsets[9] = { + df::coord2d(-1,-1), df::coord2d(0,-1), df::coord2d(1,-1), + df::coord2d(-1,0), df::coord2d(0,0), df::coord2d(1,0), + df::coord2d(-1,1), df::coord2d(0,1), df::coord2d(1,1) +}; + +// copied from Maps. arguably this should be exported from Maps; it's useful. +inline df::coord2d getBiomeRgnPos(const df::coord2d base, BiomeOffset idx) +{ + auto r = base + biome_offsets[idx]; + + int world_width = world->world_data->world_width; + int world_height = world->world_data->world_height; + + return df::coord2d(clip_range(r.x, 0, world_width-1), + clip_range(r.y, 0, world_height-1)); +} + +// copied and modified from MapCache getBaseMaterial(). +// returns the material that should be used for boulders/gems, +// or the invalid material t_matpair(-1). +static const t_matpair baseMaterialAt(const df::map_block &block, df::coord2d p) +{ + using namespace df::enums::builtin_mats; + using namespace df::enums::tiletype_material; + + p = p & 15; + t_matpair rv; + auto tt = index_tile(block.tiletype, p); + switch (tileMaterial(tt)) { + // not diggable, should not drop boulders. + case df::enums::tiletype_material::NONE: + case AIR: + case CONSTRUCTION: + case HFS: + case CAMPFIRE: + case FIRE: + case MAGMA: + case POOL: + case RIVER: + case TREE: + case ROOT: // TODO this is a bug; DF can dig roots. + case MUSHROOM: + case UNDERWORLD_GATE: + rv = t_matpair(-1); + break; + + // diggable but should not drop boulders, so return the first layer, presumably soil. + // (these won't drop anyway because they are not WALL or FORTIFICATION shaped, but safety first.) + case DRIFTWOOD: + case BROOK: + case ASHES: + case PLANT: + case GRASS_LIGHT: + case GRASS_DARK: + case GRASS_DRY: + case GRASS_DEAD: + rv = layer_inorganic_n(block, p, 0); + break; + + case STONE: + { + rv = t_matpair(INORGANIC, -1); // fallback: generic "rock" + auto geo_biome = geo_biome_at(block, p); + if (!geo_biome) + break; + + // IS THIS CODE CORRECT? + // intent here is to index 0..15 into a vector with 13 or 16 elements + // and get the element at min(index, vector length-1) . + // ranges::advance seems like the right tool. + auto geo_layer_it = begin(geo_biome->layers); + std::ranges::advance(geo_layer_it, geolayer_at(block, p), end(geo_biome->layers) - 1); + rv = t_matpair(INORGANIC, (*geo_layer_it)->mat_index); + if (getGroundType(rv) == STONE) + break; + + // fall back to the first stone layer, or the very last layer. + + // IS TNIS CODE CORRECT? + // intent here is to search a vector with 13 or 16 elements + // for the first element matching a predicate, and get that element + // OR the last element in the vector, whether or not it matches. + // all this is a lot of faffing around for something that's more + // concise in C style C++; see the SOIL code below. + auto is_stone = [&](df::world_geo_layer *geo_layer) + { return getGroundType(geo_layer->mat_index) == STONE; }; + // this vector typically has 16 entries, but I have seen 13 in oceans. + auto &geo_layers = geo_biome->layers; + geo_layer_it = std::find_if(begin(geo_layers), end(geo_layers) - 1, is_stone); + rv = t_matpair(INORGANIC, (*geo_layer_it)->mat_index); + break; + } + + case SOIL: + rv = layer_inorganic_at(block, p); + if (getGroundType(rv) != SOIL) { + // fall back to the last soil layer, or the very first layer. + for (auto i = 15; i >= 0; i--) { + rv = layer_inorganic_n(block, p, i); // this call clips to vector size. + if (getGroundType(rv) == SOIL) + break; + } + } + break; + + case FEATURE: + { + // fallback: no feature flag, or no event? no drops. + rv = t_matpair(-1); + auto des = index_tile(block.designation, p); + t_feature feature; + feature.type = df::enums::feature_type::feature_type::NONE; + if (des.bits.feature_local) + Maps::ReadFeatures( + &(static_cast(const_cast(block))), + &feature, nullptr); + else if (des.bits.feature_global) + Maps::ReadFeatures( + &(static_cast(const_cast(block))), + nullptr, &feature); + if (feature.type != df::enums::feature_type::feature_type::NONE) + rv = t_matpair(feature.main_material, feature.sub_material); + break; + } + + case LAVA_STONE: + { + auto region_details = world->world_data->midmap_data.region_details; + auto des = index_tile(block.designation, p); + auto block_region_offset_idx = static_cast(des.bits.biome); + if (block_region_offset_idx >= eBiomeCount) + // "can't happen", fall back to the local region tile's biome. + block_region_offset_idx = eHere; + auto blockptr = &static_cast(const_cast(block)); + auto region_details_coord2d = Maps::getBlockTileBiomeRgn(blockptr, p); + auto region_details_idx = linear_index(region_details, + &df::world_region_details::pos, region_details_coord2d); + if (region_details_idx == -1) + { + // "can't happen"; fall back to the first region in the region_details vector. + DEBUG(general).print("BaseMaterialAt(block {}, tile {}, case " + "LAVA_STONE, didn't find region_details for region coord {}\n", + block.map_pos, p, region_details_coord2d); + region_details_idx = 0; + } + rv = t_matpair(INORGANIC, region_details[region_details_idx]->lava_stone); + break; + } + + case MINERAL: + // fall back to the layer material, whether SOIL or STONE. + rv = layer_inorganic_at(block, p); + for (auto event : block.block_events) { + if (event->getType() == df::block_square_event_type::mineral) { + auto vein = (df::block_square_event_mineralst *)event; + if (vein->getassignment(p)) { + rv = t_matpair(INORGANIC, vein->inorganic_mat); + // do not early-out. later mineral events can override earlier ones. + } + } + } + break; + + case FROZEN_LIQUID: + rv = t_matpair(WATER, 0); + break; + + // no default so we get a compiler warning. + } + + if (rv == t_matpair(-1)) + return t_matpair(-1); + MaterialInfo mi; + mi.decode(rv); + if (mi.isValid() && mi.material->flags.is_set(df::material_flags::UNDIGGABLE)) + return t_matpair(-1); + return rv; +} + +static const t_matpair baseMaterialAt(df::coord pos) +{ + auto block = Maps::getTileBlock(pos); + if (!block) + return t_matpair(-1); + auto blockref = const_cast(static_cast(*block)); + return baseMaterialAt(blockref, df::coord2d(pos) & 15); +} + +static const df::inclusion_type veinTypeAt(df::map_block &block, df::coord2d p) +{ + using namespace df::enums::inclusion_type; + auto veintype = TOTAL; + for (auto event : block.block_events) { + if (event->getType() == df::block_square_event_type::mineral) { + auto vein = (df::block_square_event_mineralst *)event; + if (vein->getassignment(p)) { + if (vein->flags.bits.cluster_one) + veintype = CLUSTER_ONE; + else if (vein->flags.bits.cluster_small) + veintype = CLUSTER_SMALL; + else if (vein->flags.bits.vein) + veintype = VEIN; + else if (vein->flags.bits.cluster) + veintype = CLUSTER; + // do not early-out. later mineral events can override earlier ones. + } + } + } + return veintype; +} + +static const df::inclusion_type veinTypeAt(df::coord pos) +{ + auto block = Maps::getTileBlock(pos); + if (!block) + return df::enums::inclusion_type::TOTAL; + auto blockref = const_cast(static_cast(*block)); + return veinTypeAt(blockref, df::coord2d(pos) & 15); +} + struct designation{ df::coord pos; df::tile_designation type; @@ -66,6 +403,7 @@ struct designation{ } }; +// TODO maybe: consider using the version in coord.methods.inc + DataDefs.h namespace std { template <> struct hash { @@ -81,7 +419,7 @@ class DesignationJobs { std::unordered_map designations; std::unordered_map jobs; public: - void load(MapExtras::MapCache &map) { + void load() { designations.clear(); DEBUG(general).print("DesignationJobs: reading jobs list\n"); df::job_list_link* node = df::global::world->jobs.list.next; @@ -92,8 +430,8 @@ class DesignationJobs { if(!job || !Maps::isValidTilePos(job->pos)) continue; - df::tile_designation td = map.designationAt(job->pos); - df::tile_occupancy to = map.occupancyAt(job->pos); + df::tile_designation td = *Maps::getTileDesignation(job->pos); + df::tile_occupancy to = *Maps::getTileOccupancy(job->pos); const auto ctd = td.whole; const auto cto = to.whole; switch (job->job_type){ @@ -179,20 +517,20 @@ struct_identity boulder_percent_options::_identity(sizeof(boulder_percent_option struct dig_now_options { bool help; // whether to show the short help - DFCoord start; // upper-left coordinate, min z-level - DFCoord end; // lower-right coordinate, max z-level + df::coord start; // upper-left coordinate, min z-level + df::coord end; // lower-right coordinate, max z-level boulder_percent_options boulder_percents; // if set to the pos of a walkable tile (or somewhere above such a tile), // will dump generated boulders at this position instead of at their dig // locations - DFCoord dump_pos; + df::coord dump_pos; - static DFCoord getMapSize() { + static df::coord getMapSize() { uint32_t endx, endy, endz; Maps::getTileSize(endx, endy, endz); - return DFCoord(endx - 1, endy - 1, endz - 1); + return df::coord(endx - 1, endy - 1, endz - 1); } dig_now_options() : help(false), start(0, 0, 0), end(getMapSize()) { } @@ -210,11 +548,10 @@ static const struct_field_info dig_now_options_fields[] = { struct_identity dig_now_options::_identity(sizeof(dig_now_options), &df::allocator_fn, NULL, "dig_now_options", NULL, dig_now_options_fields); // propagate light, outside, and subterranean flags to open tiles below this one -static void propagate_vertical_flags(MapExtras::MapCache &map, - const DFCoord &pos) { - df::tile_designation td = map.designationAt(pos); +static void propagate_vertical_flags(const df::coord &pos) { + df::tile_designation td = *Maps::getTileDesignation(pos); - if (!map.ensureBlockAt(DFCoord(pos.x, pos.y, pos.z+1))) { + if (!Maps::ensureTileBlock(df::coord(pos.x, pos.y, pos.z+1))) { // only the sky above td.bits.light = true; td.bits.outside = true; @@ -223,12 +560,12 @@ static void propagate_vertical_flags(MapExtras::MapCache &map, int32_t zlevel = pos.z; df::tiletype_shape shape = - tileShape(map.tiletypeAt(DFCoord(pos.x, pos.y, zlevel))); + tileShape(*Maps::getTileType(df::coord(pos.x, pos.y, zlevel))); while ((shape == df::tiletype_shape::EMPTY || shape == df::tiletype_shape::RAMP_TOP) - && map.ensureBlockAt(DFCoord(pos.x, pos.y, --zlevel))) { - DFCoord pos_below(pos.x, pos.y, zlevel); - df::tile_designation td_below = map.designationAt(pos_below); + && Maps::ensureTileBlock(df::coord(pos.x, pos.y, --zlevel))) { + df::coord pos_below(pos.x, pos.y, zlevel); + df::tile_designation td_below = *Maps::getTileDesignation(pos_below); if (td_below.bits.light == td.bits.light && td_below.bits.outside == td.bits.outside && td_below.bits.subterranean == td.bits.subterranean) @@ -236,8 +573,8 @@ static void propagate_vertical_flags(MapExtras::MapCache &map, td_below.bits.light = td.bits.light; td_below.bits.outside = td.bits.outside; td_below.bits.subterranean = td.bits.subterranean; - map.setDesignationAt(pos_below, td_below); - shape = tileShape(map.tiletypeAt(pos_below)); + *Maps::getTileDesignation(pos_below) = td_below; + shape = tileShape(*Maps::getTileType(pos_below)); } } @@ -293,17 +630,67 @@ static bool can_dig_ramp(df::tiletype tt) { shape == df::tiletype_shape::FORTIFICATION; } -static void dig_type(MapExtras::MapCache &map, const DFCoord &pos, - df::tiletype tt) { - auto blk = map.BlockAtTile(pos); - if (!blk) - return; +static void dig_type(const df::coord pos, df::tiletype in_tt) { + using namespace df::enums::tiletype_material; + using namespace df::enums::tiletype_shape_basic; - map.setTiletypeAt(pos, tt); + auto block = Maps::getTileBlock(pos); + if (!block) + return; // digging a tile should revert it to the layer soil/stone material - if (!blk->setStoneAt(pos, tt, map.layerMaterialAt(pos))) - blk->setSoilAt(pos, tt, map.layerMaterialAt(pos)); + // TODO that's a bug, actually, that is not what the game does. + // this bug has been temporarily preserved to remain bug-for-bug compatible. + // this bug is NOT the same bug as the preserved bug 25 lines below. + auto tt = in_tt; + if (tt == df::tiletype::Void || tileMaterial(tt) == NONE) { + TRACE(general).print("dig_type: pre-dig: {}. {} in:{} out:{}\n", + (tt == df::tiletype::Void ? "tiletype is Void" : "tiletype_material is NONE"), + pos, static_cast(in_tt), static_cast(tt)); + } + if (tileShapeBasic(tileShape(tt)) == None || tileShapeBasic(tileShape(tt)) == Open) { + TRACE(general).print("dig_type: pre-dig: tiletype_shape_basic is {}. {} in:{} out:{}\n", + (tileShapeBasic(tileShape(tt)) == None ? "None" : "Open"), + pos, static_cast(in_tt), static_cast(tt)); + } + if (tileShapeBasic(tileShape(tt)) != Open) { + auto matpair = baseMaterialAt(pos); + auto ground_type = getGroundType(matpair); + if (ground_type == SOIL || ground_type == STONE) + tt = matchTileMaterial(tt, ground_type); + else { + TRACE(general).print("dig_type: getGroundType did not return SOIL or STONE." + " not updating tiletype material. {} in:{} out:{} ({},{})\n", + pos, static_cast(in_tt), static_cast(tt), + matpair.mat_type, matpair.mat_index); + } + } + if (tt == df::tiletype::Void || tileMaterial(tt) == NONE) { + TRACE(general).print("dig_type: derived tiletype: {} {} in:{} out:{}\n", + (tt == df::tiletype::Void ? "tiletype is Void" : "tiletype_material is NONE"), + pos, static_cast(in_tt), static_cast(tt)); + } + // this segment temporarily reimplements buggy behavior to remain bug-for-bug compatible. + // specifically, STONE tiles (i.e. layer stone) which are of the material type + // of the current region's .lava_stone (e.g. obsidian) are changed to LAVA_STONE. + // (later: unless they are in a local feature, apparently.) + // TODO when bugfixing starts, remove this block. + if (true && tileShapeBasic(tileShape(tt)) != Open) { + auto matpair = baseMaterialAt(pos); + auto des = index_tile(block->designation, pos); + auto block_region_offset_idx = des.bits.biome; + auto region_details_idx = block->region_offset[block_region_offset_idx]; + if (matpair.mat_index == world->world_data->midmap_data.region_details[region_details_idx]->lava_stone + && ! des.bits.feature_local) + tt = matchTileMaterial(tt, LAVA_STONE); + } + + if (tt == df::tiletype::Void || tileMaterial(tt) == NONE) { + TRACE(general).print("dig_type: setting tile: {}. {} in:{} out:{}\n", + (tt == df::tiletype::Void ? "tiletype is Void" : "tiletype_material is NONE"), + pos, static_cast(in_tt), static_cast(tt)); + } + index_tile(block->tiletype, pos) = tt; } static df::tiletype get_target_type(df::tiletype tt, df::tiletype_shape shape) { @@ -316,63 +703,63 @@ static df::tiletype get_target_type(df::tiletype tt, df::tiletype_shape shape) { return findRandomVariant(tt); } -static void dig_shape(MapExtras::MapCache &map, const DFCoord &pos, - df::tiletype tt, df::tiletype_shape shape) { - dig_type(map, pos, get_target_type(tt, shape)); +static void dig_shape(const df::coord pos, df::tiletype tt, df::tiletype_shape shape) { + dig_type(pos, get_target_type(tt, shape)); } -static void remove_ramp_top(MapExtras::MapCache &map, const DFCoord &pos) { - if (!map.ensureBlockAt(pos)) +static void remove_ramp_top(const df::coord pos) { + if (!Maps::ensureTileBlock(pos)) return; - if (tileShape(map.tiletypeAt(pos)) == df::tiletype_shape::RAMP_TOP) - dig_type(map, pos, df::tiletype::OpenSpace); + if (tileShape(*Maps::getTileType(pos)) == df::tiletype_shape::RAMP_TOP) + dig_type(pos, df::tiletype::OpenSpace); } -static bool is_wall(MapExtras::MapCache &map, const DFCoord &pos) { - if (!map.ensureBlockAt(pos)) +static bool is_wall(const df::coord pos) { + if (!Maps::ensureTileBlock(pos)) return false; - return tileShape(map.tiletypeAt(pos)) == df::tiletype_shape::WALL; + return tileShape(*Maps::getTileType(pos)) == df::tiletype_shape::WALL; } -static void clean_ramp(MapExtras::MapCache &map, const DFCoord &pos) { - if (!map.ensureBlockAt(pos)) +static void clean_ramp(const df::coord pos) { + if (!Maps::ensureTileBlock(pos)) return; - df::tiletype tt = map.tiletypeAt(pos); + df::tiletype tt = *Maps::getTileType(pos); if (tileShape(tt) != df::tiletype_shape::RAMP) return; - if (is_wall(map, DFCoord(pos.x-1, pos.y, pos.z)) || - is_wall(map, DFCoord(pos.x+1, pos.y, pos.z)) || - is_wall(map, DFCoord(pos.x, pos.y-1, pos.z)) || - is_wall(map, DFCoord(pos.x, pos.y+1, pos.z)) || - is_wall(map, DFCoord(pos.x-1, pos.y-1, pos.z)) || - is_wall(map, DFCoord(pos.x-1, pos.y+1, pos.z)) || - is_wall(map, DFCoord(pos.x+1, pos.y-1, pos.z)) || - is_wall(map, DFCoord(pos.x+1, pos.y+1, pos.z))) + if ( is_wall(df::coord(pos.x-1, pos.y, pos.z)) || + is_wall(df::coord(pos.x+1, pos.y, pos.z)) || + is_wall(df::coord(pos.x, pos.y-1, pos.z)) || + is_wall(df::coord(pos.x, pos.y+1, pos.z)) || + is_wall(df::coord(pos.x-1, pos.y-1, pos.z)) || + is_wall(df::coord(pos.x-1, pos.y+1, pos.z)) || + is_wall(df::coord(pos.x+1, pos.y-1, pos.z)) || + is_wall(df::coord(pos.x+1, pos.y+1, pos.z)) ) return; - remove_ramp_top(map, DFCoord(pos.x, pos.y, pos.z+1)); - dig_shape(map,pos, tt, df::tiletype_shape::FLOOR); + remove_ramp_top(df::coord(pos.x, pos.y, pos.z+1)); + dig_shape(pos, tt, df::tiletype_shape::FLOOR); } // removes self and/or orthogonally adjacent ramps that are no longer adjacent // to a wall -static void clean_ramps(MapExtras::MapCache &map, const DFCoord &pos) { - clean_ramp(map, pos); - clean_ramp(map, DFCoord(pos.x-1, pos.y, pos.z)); - clean_ramp(map, DFCoord(pos.x+1, pos.y, pos.z)); - clean_ramp(map, DFCoord(pos.x, pos.y-1, pos.z)); - clean_ramp(map, DFCoord(pos.x, pos.y+1, pos.z)); - clean_ramp(map, DFCoord(pos.x-1, pos.y-1, pos.z)); - clean_ramp(map, DFCoord(pos.x-1, pos.y+1, pos.z)); - clean_ramp(map, DFCoord(pos.x+1, pos.y-1, pos.z)); - clean_ramp(map, DFCoord(pos.x+1, pos.y+1, pos.z)); +static void clean_ramps(const df::coord pos) { + // TODO possible bug; should clean_ramp this tile *last*. + clean_ramp(pos); + clean_ramp(df::coord(pos.x-1, pos.y, pos.z)); + clean_ramp(df::coord(pos.x+1, pos.y, pos.z)); + clean_ramp(df::coord(pos.x, pos.y-1, pos.z)); + clean_ramp(df::coord(pos.x, pos.y+1, pos.z)); + clean_ramp(df::coord(pos.x-1, pos.y-1, pos.z)); + clean_ramp(df::coord(pos.x-1, pos.y+1, pos.z)); + clean_ramp(df::coord(pos.x+1, pos.y-1, pos.z)); + clean_ramp(df::coord(pos.x+1, pos.y+1, pos.z)); } // destroys any colonies located at pos -static void destroy_colony(const DFCoord &pos) { +static void destroy_colony(const df::coord pos) { auto same_pos = [&](df::vermin *colony){ return colony->pos == pos; }; auto &colonies = world->event.vermin_colonies; @@ -387,80 +774,74 @@ static void destroy_colony(const DFCoord &pos) { } struct dug_tile_info { - DFCoord pos; + df::coord pos; df::tiletype_material tmat; df::item_type itype; t_matpair imat; // matpair of boulder/gem potentially generated at this pos - dug_tile_info(MapExtras::MapCache &map, const DFCoord &pos) { + dug_tile_info(const df::coord pos) { this->pos = pos; - df::tiletype tt = map.tiletypeAt(pos); + auto tt = *Maps::getTileType(pos); tmat = tileMaterial(tt); itype = df::item_type::BOULDER; - imat = -1; + imat = t_matpair(-1); df::tiletype_shape shape = tileShape(tt); if (shape != df::tiletype_shape::WALL && shape != df::tiletype_shape::FORTIFICATION) return; - switch (tmat) { - case df::tiletype_material::STONE: - case df::tiletype_material::MINERAL: - case df::tiletype_material::FEATURE: - case df::tiletype_material::LAVA_STONE: - imat = map.baseMaterialAt(pos); - break; - case df::tiletype_material::FROZEN_LIQUID: - // assume frozen water - // we can't use baseMaterialAt here because it will return the underlying river bed material - imat = t_matpair(df::builtin_mats::WATER, -1); - break; - default: - return; - } + imat = baseMaterialAt(pos); + if (imat == t_matpair(-1)) + return; MaterialInfo mi; mi.decode(imat); if (mi.type == -1 || !mi.material) return; - if (mi.material->isGem()) { + if (mi.material->isGem()) itype = df::item_type::ROUGH; - } } }; -static bool is_diggable(MapExtras::MapCache &map, const DFCoord &pos, - df::tiletype tt) { +static bool is_diggable(const df::coord pos, df::tiletype tt) { + using namespace df::enums::tiletype_material; df::tiletype_material mat = tileMaterial(tt); switch (mat) { - case df::tiletype_material::CONSTRUCTION: - case df::tiletype_material::TREE: - case df::tiletype_material::ROOT: - case df::tiletype_material::MAGMA: - case df::tiletype_material::HFS: - case df::tiletype_material::UNDERWORLD_GATE: - return false; - default: - break; + case CONSTRUCTION: + case HFS: + case CAMPFIRE: + case FIRE: + case MAGMA: + case POOL: + case RIVER: + case ROOT: // TODO this is a bug; DF can dig roots. + case TREE: + case MUSHROOM: + case UNDERWORLD_GATE: + return false; + case AIR: + return false; + default: + break; } MaterialInfo mi; - mi.decode(map.baseMaterialAt(pos)); + mi.decode(baseMaterialAt(pos)); if (mi.material != nullptr && mi.material->flags.is_set(df::material_flags::UNDIGGABLE)) return false; return true; } -static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, - const DFCoord &pos, df::tile_dig_designation designation, +static bool dig_tile(color_ostream &out, + const df::coord pos, df::tile_dig_designation designation, std::vector &dug_tiles) { - df::tiletype tt = map.tiletypeAt(pos); + df::tiletype tt = *Maps::getTileType(pos); - if (!is_diggable(map, pos, tt)) { + if (!is_diggable(pos, tt)) { DEBUG(general).print("dig_tile: not diggable\n"); return false; } @@ -474,33 +855,47 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, if (shape == df::tiletype_shape::STAIR_UPDOWN) target_shape = df::tiletype_shape::STAIR_DOWN; else if (shape == df::tiletype_shape::RAMP) - remove_ramp_top(map, DFCoord(pos.x, pos.y, pos.z+1)); + remove_ramp_top(df::coord(pos.x, pos.y, pos.z+1)); target_type = get_target_type(tt, target_shape); } break; case df::tile_dig_designation::Channel: { - 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 ({}) [can_dig_channel: true]\n", pos_below); + df::coord pos_below(pos.x, pos.y, pos.z-1); + if (can_dig_channel(tt) && Maps::ensureTileBlock(pos_below) + /*&& is_diggable(pos_below, *Maps::getTileType(pos_below))*/) { + DEBUG(channels).print("dig_tile: channeling at {} [can_dig_channel: true]\n", pos_below); target_type = df::tiletype::OpenSpace; - DFCoord pos_above(pos.x, pos.y, pos.z+1); - 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)) { - clean_ramps(map, pos_below); + df::coord pos_above(pos.x, pos.y, pos.z+1); + if (Maps::ensureTileBlock(pos_above)) + remove_ramp_top(pos_above); + // TODO processing of the tile below probably should be pulled out of this case. + df::tile_dig_designation td_below = (*Maps::getTileDesignation(pos_below)).bits.dig; + if (is_diggable(pos_below, *Maps::getTileType(pos_below)) + && dig_tile(out, pos_below, df::tile_dig_designation::Ramp, dug_tiles)) { + clean_ramps(pos_below); if (td_below == df::tile_dig_designation::Default) { - dig_tile(out, map, pos_below, td_below, dug_tiles); + dig_tile(out, pos_below, td_below, dug_tiles); + } + clean_ramps(pos); + propagate_vertical_flags(pos); + if (*Maps::getTileType(pos_below) == df::tiletype::Void + || tileMaterial(*Maps::getTileType(pos_below)) == df::tiletype_material::NONE) { + TRACE(general).print("dig_tile: Channel: post-dig-pos_below: {}. pos_below {} tiletype {}\n", + *Maps::getTileType(pos_below) == df::tiletype::Void + ? "tiletype is Void" : "tiletype_material is NONE", + pos_below, static_cast(*Maps::getTileType(pos_below))); } - clean_ramps(map, pos); - propagate_vertical_flags(map, pos); return true; } + else { + // TODO create a floor on the upper level if the lower level is WALL-basic-shaped. + // TODO also if the lower level is a construction, create a floor construction on + // the upper level with .flags.no_build_item and .flags.top_of_wall set, and + // the original_tile set to OpenSpace. + } } else { - DEBUG(channels).print("dig_tile: failed to channel at ({}) [can_dig_channel: false]\n", pos_below); + DEBUG(channels).print("dig_tile: failed to channel at {} [can_dig_channel: false]\n", pos_below); } break; } @@ -509,37 +904,28 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, target_type = get_target_type(tt, df::tiletype_shape::STAIR_UP); break; case df::tile_dig_designation::DownStair: - if (can_dig_down_stair(tt)) { - target_type = - get_target_type(tt, df::tiletype_shape::STAIR_DOWN); - - } + if (can_dig_down_stair(tt)) + target_type = get_target_type(tt, df::tiletype_shape::STAIR_DOWN); break; case df::tile_dig_designation::UpDownStair: - if (can_dig_up_down_stair(tt)) { - target_type = - get_target_type(tt, df::tiletype_shape::STAIR_UPDOWN); - } + if (can_dig_up_down_stair(tt)) + target_type = get_target_type(tt, df::tiletype_shape::STAIR_UPDOWN); break; case df::tile_dig_designation::Ramp: { if (can_dig_ramp(tt)) { target_type = get_target_type(tt, df::tiletype_shape::RAMP); - DFCoord pos_above(pos.x, pos.y, pos.z+1); - if (target_type != tt && map.ensureBlockAt(pos_above) - && is_diggable(map, pos, map.tiletypeAt(pos_above))) { + df::coord pos_above(pos.x, pos.y, pos.z+1); + if (target_type != tt && Maps::ensureTileBlock(pos_above) + && is_diggable(pos, *Maps::getTileType(pos_above))) { // only capture the tile info of pos_above if we didn't get // here via the Channel case above if (dug_tiles.size() == 0) - dug_tiles.push_back(dug_tile_info(map, pos_above)); + dug_tiles.push_back(dug_tile_info(pos_above)); destroy_colony(pos_above); - // set tile type directly instead of calling dig_shape - // because we need to use *this* tile's material, not the - // material of the tile above - map.setTiletypeAt(pos_above, - get_target_type(tt, df::tiletype_shape::RAMP_TOP)); - remove_ramp_top(map, DFCoord(pos.x, pos.y, pos.z+2)); - propagate_vertical_flags(map, DFCoord(pos.x, pos.y, pos.z + 1)); + *Maps::getTileType(pos_above) = df::tiletype::RampTop; + remove_ramp_top(df::coord(pos.x, pos.y, pos.z+2)); + propagate_vertical_flags(df::coord(pos.x, pos.y, pos.z + 1)); } } break; @@ -551,27 +937,32 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, pos.x, pos.y, pos.z, ENUM_AS_STR(designation)); } + if (target_type == df::tiletype::Void || tileMaterial(target_type) == df::tiletype_material::NONE) { + TRACE(general).print("dig_tile: target_type: {}. {} {}\n", + target_type == df::tiletype::Void ? "tiletype is Void" : "tiletype_material is NONE", + pos, static_cast(tt)); + } // fail if unhandled or no change to tile if (target_type == df::tiletype::Void || target_type == tt) return false; - dug_tiles.emplace_back(map, pos); - TRACE(general).print("dig_tile: digging the designation tile at ({})\n",pos); - dig_type(map, pos, target_type); + dug_tiles.emplace_back(pos); + TRACE(general).print("dig_tile: digging the designation tile at {}\n",pos); + dig_type(pos, target_type); - clean_ramps(map, pos); + clean_ramps(pos); return true; } -static bool is_smooth_wall(MapExtras::MapCache &map, const DFCoord &pos) { - if (!map.ensureBlockAt(pos)) +static bool is_smooth_wall(const df::coord pos) { + if (!Maps::ensureTileBlock(pos)) return false; - df::tiletype tt = map.tiletypeAt(pos); + df::tiletype tt = *Maps::getTileType(pos); return tileSpecial(tt) == df::tiletype_special::SMOOTH && tileShape(tt) == df::tiletype_shape::WALL; } -static bool is_connector(MapExtras::MapCache &map, const DFCoord &pos) { +static bool is_connector(const df::coord pos) { df::building *bld = Buildings::findAtTile(pos); return bld && @@ -579,22 +970,20 @@ static bool is_connector(MapExtras::MapCache &map, const DFCoord &pos) { bld->getType() == df::building_type::Floodgate); } -static bool is_smooth_wall_or_connector(MapExtras::MapCache &map, - const DFCoord &pos) { - return is_smooth_wall(map, pos) || is_connector(map, pos); +static bool is_smooth_wall_or_connector(const df::coord pos) { + return is_smooth_wall(pos) || is_connector(pos); } // adds adjacent smooth walls and doors to the given tdir -static TileDirection get_adjacent_smooth_walls(MapExtras::MapCache &map, - const DFCoord &pos, +static TileDirection get_adjacent_smooth_walls(const df::coord pos, TileDirection tdir) { - if (is_smooth_wall_or_connector(map, DFCoord(pos.x, pos.y-1, pos.z))) + if (is_smooth_wall_or_connector(df::coord(pos.x, pos.y-1, pos.z))) tdir.north = 1; - if (is_smooth_wall_or_connector(map, DFCoord(pos.x, pos.y+1, pos.z))) + if (is_smooth_wall_or_connector(df::coord(pos.x, pos.y+1, pos.z))) tdir.south = 1; - if (is_smooth_wall_or_connector(map, DFCoord(pos.x-1, pos.y, pos.z))) + if (is_smooth_wall_or_connector(df::coord(pos.x-1, pos.y, pos.z))) tdir.west = 1; - if (is_smooth_wall_or_connector(map, DFCoord(pos.x+1, pos.y, pos.z))) + if (is_smooth_wall_or_connector(df::coord(pos.x+1, pos.y, pos.z))) tdir.east = 1; return tdir; } @@ -616,44 +1005,42 @@ static TileDirection ensure_valid_tdir(TileDirection tdir) { // connects adjacent smooth walls to our new smooth wall static TileDirection BLANK_TILE_DIRECTION; -static bool adjust_smooth_wall_dir(MapExtras::MapCache &map, - const DFCoord &pos, +static bool adjust_smooth_wall_dir(const df::coord pos, TileDirection tdir = BLANK_TILE_DIRECTION) { - if (!is_smooth_wall(map, pos)) - return is_connector(map, pos); + if (!is_smooth_wall(pos)) + return is_connector(pos); - tdir = ensure_valid_tdir(get_adjacent_smooth_walls(map, pos, tdir)); + tdir = ensure_valid_tdir(get_adjacent_smooth_walls(pos, tdir)); - df::tiletype tt = map.tiletypeAt(pos); + df::tiletype tt = *Maps::getTileType(pos); tt = findTileType(tileShape(tt), tileMaterial(tt), tileVariant(tt), tileSpecial(tt), tdir); if (tt == df::tiletype::Void) return false; - map.setTiletypeAt(pos, tt); + *Maps::getTileType(pos) = tt; return true; } -static void refresh_adjacent_smooth_walls(MapExtras::MapCache &map, - const DFCoord &pos) { - adjust_smooth_wall_dir(map, DFCoord(pos.x, pos.y-1, pos.z)); - adjust_smooth_wall_dir(map, DFCoord(pos.x, pos.y+1, pos.z)); - adjust_smooth_wall_dir(map, DFCoord(pos.x-1, pos.y, pos.z)); - adjust_smooth_wall_dir(map, DFCoord(pos.x+1, pos.y, pos.z)); +static void refresh_adjacent_smooth_walls(const df::coord pos) { + adjust_smooth_wall_dir(df::coord(pos.x, pos.y-1, pos.z)); + adjust_smooth_wall_dir(df::coord(pos.x, pos.y+1, pos.z)); + adjust_smooth_wall_dir(df::coord(pos.x-1, pos.y, pos.z)); + adjust_smooth_wall_dir(df::coord(pos.x+1, pos.y, pos.z)); } // assumes that if the game let you designate a tile for smoothing, it must be // valid to do so. -static bool smooth_tile(color_ostream &out, MapExtras::MapCache &map, - const DFCoord &pos) { - df::tiletype tt = map.tiletypeAt(pos); +static bool smooth_tile(color_ostream &out, + const df::coord pos) { + df::tiletype tt = *Maps::getTileType(pos); df::tiletype_shape shape = tileShape(tt); df::tiletype_variant variant = tileVariant(tt); df::tiletype_special special = df::tiletype_special::SMOOTH; TileDirection tdir; - if (is_smooth_wall(map, pos)) { + if (is_smooth_wall(pos)) { // engraving is filtered out at a higher level, so this is a // fortification designation shape = tiletype_shape::FORTIFICATION; @@ -661,16 +1048,16 @@ static bool smooth_tile(color_ostream &out, MapExtras::MapCache &map, special = df::tiletype_special::NONE; } else if (shape == df::tiletype_shape::WALL) { - if (adjust_smooth_wall_dir(map, DFCoord(pos.x, pos.y-1, pos.z), + if (adjust_smooth_wall_dir(df::coord(pos.x, pos.y-1, pos.z), TileDirection(0, 1, 0, 0))) tdir.north = 1; - if (adjust_smooth_wall_dir(map, DFCoord(pos.x, pos.y+1, pos.z), + if (adjust_smooth_wall_dir(df::coord(pos.x, pos.y+1, pos.z), TileDirection(1, 0, 0, 0))) tdir.south = 1; - if (adjust_smooth_wall_dir(map, DFCoord(pos.x-1, pos.y, pos.z), + if (adjust_smooth_wall_dir(df::coord(pos.x-1, pos.y, pos.z), TileDirection(0, 0, 0, 1))) tdir.west = 1; - if (adjust_smooth_wall_dir(map, DFCoord(pos.x+1, pos.y, pos.z), + if (adjust_smooth_wall_dir(df::coord(pos.x+1, pos.y, pos.z), TileDirection(0, 0, 1, 0))) tdir.east = 1; tdir = ensure_valid_tdir(tdir); @@ -680,15 +1067,14 @@ static bool smooth_tile(color_ostream &out, MapExtras::MapCache &map, if (tt == df::tiletype::Void) return false; - map.setTiletypeAt(pos, tt); + *Maps::getTileType(pos) = tt; return true; } // assumes that if the game let you designate a tile for track carving, it must // be valid to do so. -static bool carve_tile(MapExtras::MapCache &map, - const DFCoord &pos, df::tile_occupancy &to) { - df::tiletype tt = map.tiletypeAt(pos); +static bool carve_tile(const df::coord pos, df::tile_occupancy &to) { + df::tiletype tt = *Maps::getTileType(pos); TileDirection tdir = tileDirection(tt); if (to.bits.carve_track_north) @@ -705,18 +1091,18 @@ static bool carve_tile(MapExtras::MapCache &map, if (tt == df::tiletype::Void) return false; - map.setTiletypeAt(pos, tt); + *Maps::getTileType(pos) = tt; return true; } static bool produces_item(const boulder_percent_options &options, - MapExtras::MapCache &map, Random::MersenneRNG &rng, + Random::MersenneRNG &rng, const dug_tile_info &info) { uint32_t probability; if (info.tmat == df::tiletype_material::FEATURE) probability = options.deep; else { - switch (map.BlockAtTile(info.pos)->veinTypeAt(info.pos)) { + switch (veinTypeAt(info.pos)) { case df::inclusion_type::CLUSTER: case df::inclusion_type::VEIN: probability = options.vein; @@ -734,17 +1120,16 @@ static bool produces_item(const boulder_percent_options &options, return rng.random(100) < probability; } -typedef std::map, std::vector> +typedef std::map, std::vector > item_coords_t; -static void do_dig(color_ostream &out, std::vector &dug_coords, +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; DEBUG(general).print("do_dig(): starting..\n"); - jobs.load(map); + jobs.load(); rng.init(); DEBUG(general).print("do_dig(): reading map..\n"); @@ -758,9 +1143,9 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, if (!Maps::getTileBlock(x, y, z)) continue; - DFCoord pos(x, y, z); - df::tile_designation td = map.designationAt(pos); - df::tile_occupancy to = map.occupancyAt(pos); + df::coord pos(x, y, z); + df::tile_designation td = *Maps::getTileDesignation(pos); + df::tile_occupancy to = *Maps::getTileOccupancy(pos); if (jobs.count(pos)) { buffer.emplace(jobs.get(pos)); jobs.remove(pos); @@ -789,53 +1174,49 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, 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)) { + if (dig_tile(out, pos, td.bits.dig, dug_tiles)) { for (auto info: dug_tiles) { - td = map.designationAt(info.pos); + td = *Maps::getTileDesignation(info.pos); td.bits.dig = df::tile_dig_designation::No; - map.setDesignationAt(info.pos, td); + *Maps::getTileDesignation(info.pos) = td; dug_coords.push_back(info.pos); - refresh_adjacent_smooth_walls(map, info.pos); + refresh_adjacent_smooth_walls(info.pos); if (info.imat < 0) continue; - if (produces_item(options.boulder_percents, - map, rng, info)) { + if (produces_item(options.boulder_percents, 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); + if (smooth_tile(out, pos)) { + td = *Maps::getTileDesignation(pos); td.bits.smooth = 0; - map.setDesignationAt(pos, td); + *Maps::getTileDesignation(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); + if (carve_tile(pos, to)) { + to = *Maps::getTileOccupancy(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); + *Maps::getTileOccupancy(pos) = to; } } } - - DEBUG(general).print("do_dig(): write changes to map..\n"); - map.WriteAll(); } // if pos is empty space, teleport to a floor somewhere below // if we fall out of the world (e.g. empty space or walls all the way down), // returned position will be invalid -static DFCoord simulate_fall(const DFCoord &pos) { - DFCoord resting_pos(pos); +static df::coord simulate_fall(const df::coord pos) { + df::coord resting_pos(pos); while (Maps::ensureTileBlock(resting_pos)) { df::tiletype tt = *Maps::getTileType(resting_pos); @@ -858,7 +1239,7 @@ static void create_boulders(color_ostream &out, std::vector in_reag; std::vector in_items; - DFCoord dump_pos; + df::coord dump_pos; if (Maps::isValidTilePos(options.dump_pos)) { dump_pos = simulate_fall(options.dump_pos); if (!Maps::ensureTileBlock(dump_pos)) @@ -869,7 +1250,7 @@ static void create_boulders(color_ostream &out, for (auto entry : item_coords) { df::reaction_product_itemst *prod = df::allocate(); - const std::vector &coords = entry.second; + const std::vector &coords = entry.second; prod->item_type = entry.first.first; prod->item_subtype = -1; @@ -902,7 +1283,7 @@ static void create_boulders(color_ostream &out, } for (size_t i = 0; i < num_items; ++i) { - DFCoord pos = Maps::isValidTilePos(dump_pos) ? + df::coord pos = Maps::isValidTilePos(dump_pos) ? dump_pos : simulate_fall(coords[i]); if (!Maps::ensureTileBlock(pos)) { out.printerr( @@ -917,26 +1298,26 @@ static void create_boulders(color_ostream &out, } } -static bool needs_unhide(const DFCoord &pos) { +static bool needs_unhide(const df::coord pos) { return !Maps::ensureTileBlock(pos) || Maps::getTileDesignation(pos)->bits.hidden; } -static bool needs_flood_unhide(const DFCoord &pos) { +static bool needs_flood_unhide(const df::coord pos) { return needs_unhide(pos) - || needs_unhide(DFCoord(pos.x-1, pos.y-1, pos.z)) - || needs_unhide(DFCoord(pos.x, pos.y-1, pos.z)) - || needs_unhide(DFCoord(pos.x+1, pos.y-1, pos.z)) - || needs_unhide(DFCoord(pos.x-1, pos.y, pos.z)) - || needs_unhide(DFCoord(pos.x+1, pos.y, pos.z)) - || needs_unhide(DFCoord(pos.x-1, pos.y+1, pos.z)) - || needs_unhide(DFCoord(pos.x, pos.y+1, pos.z)) - || needs_unhide(DFCoord(pos.x+1, pos.y+1, pos.z)); + || needs_unhide(df::coord(pos.x-1, pos.y-1, pos.z)) + || needs_unhide(df::coord(pos.x, pos.y-1, pos.z)) + || needs_unhide(df::coord(pos.x+1, pos.y-1, pos.z)) + || needs_unhide(df::coord(pos.x-1, pos.y, pos.z)) + || needs_unhide(df::coord(pos.x+1, pos.y, pos.z)) + || needs_unhide(df::coord(pos.x-1, pos.y+1, pos.z)) + || needs_unhide(df::coord(pos.x, pos.y+1, pos.z)) + || needs_unhide(df::coord(pos.x+1, pos.y+1, pos.z)); } static void post_process_dug_tiles(color_ostream &out, - const std::vector &dug_coords) { - for (DFCoord pos : dug_coords) { + const std::vector &dug_coords) { + for (df::coord pos : dug_coords) { if (needs_flood_unhide(pos)) { // set current tile to hidden to allow flood_unhide to work on tiles // that were already visible but that reveal hidden tiles when dug. @@ -946,7 +1327,7 @@ static void post_process_dug_tiles(color_ostream &out, df::tile_occupancy &to = *Maps::getTileOccupancy(pos); if (to.bits.unit || to.bits.item) { - DFCoord resting_pos = simulate_fall(pos); + df::coord resting_pos = simulate_fall(pos); if (resting_pos == pos) continue; @@ -1023,7 +1404,7 @@ bool dig_now_impl(color_ostream &out, const dig_now_options &options) { } // track which positions were modified and where to produce items - std::vector dug_coords; + std::vector dug_coords; item_coords_t item_coords; do_dig(out, dug_coords, item_coords, options); @@ -1060,14 +1441,13 @@ DFhackCExport command_result plugin_shutdown(color_ostream &) { // Lua API -// runs dig-now for the specified tile coordinate. default options apply. static int dig_now_tile(lua_State *L) { - DFCoord pos; + df::coord pos; if (lua_gettop(L) <= 1) Lua::CheckDFAssign(L, &pos, 1); else - pos = DFCoord(luaL_checkint(L, 1), luaL_checkint(L, 2), + pos = df::coord(luaL_checkint(L, 1), luaL_checkint(L, 2), luaL_checkint(L, 3)); color_ostream *out = Lua::GetOutput(L); @@ -1084,20 +1464,50 @@ static int dig_now_tile(lua_State *L) static int link_adjacent_smooth_walls(lua_State *L) { - DFCoord pos; + df::coord pos; if (lua_gettop(L) <= 1) Lua::CheckDFAssign(L, &pos, 1); else - pos = DFCoord(luaL_checkint(L, 1), luaL_checkint(L, 2), + pos = df::coord(luaL_checkint(L, 1), luaL_checkint(L, 2), luaL_checkint(L, 3)); - MapExtras::MapCache map; - adjust_smooth_wall_dir(map, pos); - refresh_adjacent_smooth_walls(map, pos); - map.WriteAll(); + adjust_smooth_wall_dir(pos); + refresh_adjacent_smooth_walls(pos); return 0; } +/* LuaLS annotations. + +--- Runs dig-now for the specified tile coordinate. default options apply. +--- The tile must have a dig designation, a smooth designation, or the +--- occupancy must be set for track carving; otherwise nothing is done. +--- The designations and track-carving occupancy settings will be cleared. +--- +--- Returns false if no map is loaded or no unit is alive. +--- Otherwise, returns true whether or not any work was done. +--- +--- Note: to process large areas, it is better to invoke the `dig-now` plugin. +--- e.g. `dfhack.run_command("dig-now", "param1", "param2", ...)`. +--- +---@param x integer +---@param y integer +---@param z integer +---@return boolean +---@overload fun(pos: df.coord):boolean +function dig_now_tile(x, y, z) end + +--- re-connects smooth walls for the given tile and the orthogonally +--- adjacent tiles. +--- +---@param x integer +---@param y integer +---@param z integer +---@return nil +---@overload fun(pos: df.coord):boolean +function link_adjacent_smooth_walls(x, y, z) + +*/ + DFHACK_PLUGIN_LUA_COMMANDS { DFHACK_LUA_COMMAND(dig_now_tile), DFHACK_LUA_COMMAND(link_adjacent_smooth_walls), diff --git a/plugins/lua/dig-now.lua b/plugins/lua/dig-now.lua index b3ffeb0bcc..348c995c34 100644 --- a/plugins/lua/dig-now.lua +++ b/plugins/lua/dig-now.lua @@ -4,11 +4,21 @@ local argparse = require('argparse') local guidm = require('gui.dwarfmode') local utils = require('utils') +---@diagnostic disable-next-line: duplicate-doc-alias +---@alias coord df.coord | { x:integer, y: integer, z:integer } +---@alias boulder_percents { layer:integer, vein:integer, small_cluster:integer, deep:integer } -- dig-now.cpp boulder_percent_options_fields +---@alias opts { help:boolean, start:coord, end:coord, dump_pos:coord, boulder_percents:boulder_percents } -- dig-now.cpp dig_now_options_fields + +---@param opts opts +---@param configname 'start' | 'end' | 'dump_pos' +---@param arg 'here' | string -- "x,y,z", integers, map coordinate local function parse_coords(opts, configname, arg) - local cursor = argparse.coords(arg, configname) + local cursor = argparse.coords(arg, configname) ---@type coord utils.assign(opts[configname], cursor) end +---@param opts opts +---@param arg string -- "int,int,int,int" ranges 0..100 local function parse_percentages(opts, arg) local nums = argparse.numberList(arg, 'percentages', 4) for _,percentage in ipairs(nums) do @@ -24,12 +34,16 @@ local function parse_percentages(opts, arg) nums[1], nums[2], nums[3], nums[4] end +---@generic T -- any, non-nil +---@param ... T local function min_to_max(...) local args = {...} table.sort(args, function(a, b) return a < b end) return table.unpack(args) end +---@param opts opts +---@param ... string function parse_commandline(opts, ...) local use_zlevel = false local positionals = argparse.processArgsGetopt({...}, {