From 79364de95c61b52b51b6f978a851f4af6df9ed90 Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Fri, 20 Sep 2024 03:42:13 -0700 Subject: [PATCH 01/29] Update Lua API.rst - maps.isPlantInBox --- docs/dev/Lua API.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 232daea9eb..94ba7636b6 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -2298,6 +2298,11 @@ Maps module Returns the plant struct that owns the tile at the specified position. +* ``dfhack.maps.isPlantInBox(plant,x1,y1,z1,x2,y2,z2)`` + + Returns true if the plant is within a box defined by the specified + coordinates, accounting for trees. + * ``dfhack.maps.getWalkableGroup(pos)`` Returns the walkability group for the given tile position. A return value From 8081985cccf15c2b2c37a8a884c1833d1c56fc44 Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Fri, 20 Sep 2024 03:44:17 -0700 Subject: [PATCH 02/29] Update cleaners.rst --- docs/plugins/cleaners.rst | 141 ++++++++++++++++++++++++++++++-------- 1 file changed, 113 insertions(+), 28 deletions(-) diff --git a/docs/plugins/cleaners.rst b/docs/plugins/cleaners.rst index 8b2e6f71f4..cdf548f5ac 100644 --- a/docs/plugins/cleaners.rst +++ b/docs/plugins/cleaners.rst @@ -5,57 +5,142 @@ cleaners ======== .. dfhack-tool:: - :summary: Provides commands for cleaning spatter from the map. - :tags: adventure fort armok fps items map units + :summary: Provides commands for cleaning spatter (and grass) from the map. + :tags: adventure fort armok fps items map plants units :no-command: .. dfhack-command:: clean :summary: Removes contaminants. .. dfhack-command:: spotclean - :summary: Remove all contaminants from the tile under the cursor. + :summary: Remove contaminants from the tile under the cursor. -This plugin provides commands that clean the splatter that get scattered all +This plugin provides commands that clean the spatter that gets scattered all over the map and that clings to your items and units. In an old fortress, -cleaning with this tool can significantly reduce FPS lag! It can also spoil your -!!FUN!!, so think before you use it. +cleaning with this tool can significantly reduce FPS lag! It can also spoil +your !!FUN!!, so think before you use it. + +This plugin can also be used to remove grass in an area you don't want it +(including those hard to reach areas like stairs and under buildings). +Use it if you messed up with the `regrass` tool, or grass grew in your +dining hall after a flooding mishap. Grass may eventually regrow if the tile +remains soil, so `gui/tiletypes` may come in handy to change it to an +appropriate stone type. Usage ----- :: - clean all|map|items|units|plants [] + clean [ []] [] spotclean -By default, cleaning the map leaves mud and snow alone. Note that cleaning units -includes hostiles, and that cleaning items removes poisons from weapons. +By default, cleaning the map leaves mud, snow, and item spatter (e.g., tree +droppings) alone. Note that cleaning units includes hostiles, and that +cleaning items removes poisons from weapons. + +Mud will not be cleaned out from under farm plots unless the tile is furrowed +soil, since that would render the plot inoperable. + +Operates on the entire map unless otherwise specified. Supplying a ``pos`` +argument can limit operation to a single tile. Supplying both can operate on +a cuboid region. ``pos`` should normally be in the form ``0,0,0``, without +spaces. The string ``here`` can be used in place of numeric coordinates to use +the position of the keyboard cursor, if active. The ``--zlevel`` option uses +the ``pos`` values differently. -``spotclean`` works like ``clean map snow mud``, removing all contaminants from -the tile under the keyboard cursor. This is ideal if you just want to clean a -specific tile but don't want the `clean ` command to remove all the -glorious blood from your entranceway. +``spotclean`` works like ``clean here --map --mud --snow``, removing all +contaminants from a specific tile quickly. Intended as a hotkey command. -Mud will not be cleaned out from under farm plots, since that would render the -plot inoperable. Examples -------- -``clean all`` - Clean everything that can be cleaned (except mud and snow). -``clean map mud item snow`` - Removes all spatter, including mud, leaves, and snow from map tiles. Farm - plots will retain their mud. +``clean --all`` + Clean most spatter from all map tiles (excluding mud, snow, and item + spatter), as well as all contaminants from units and items. + +``clean --map --mud --snow --item`` + Removes all spatter, including mud, snow, and tree droppings from map + tiles. Farm plots will retain their mud as needed. + +``clean --map --item --only --items`` + Remove only tree droppings from the map (leaving blood and other + contaminants untouched). Clean all items of their contaminants. + +``clean here -mdstu`` + Clean any sort of contaminant from the map tile under the keyboard cursor, + as well as any contaminant found on a unit there, but don't touch contaminants + on any item (e.g., the unit's poisoned weapon). + +``clean 0,0,90 0,0,120 -uiz`` + Clean all contaminants from units and items on z-levels 90 through 120. + Don't touch map spatter at all. + +``clean -mgxoz`` + Reduce all grass on the current z-level to barren soil. Don't touch + any contaminants. + +``clean 0,0,100 19,19,119 -adstg`` + Remove all contaminants of any type from the 20 x 20 x 20 cube defined + by the coords, including on any units, items, and ground spatter + (excluding mud for farms). Also remove any unused grass type events from + the affected map blocks, but don't remove any grass present. Options ------- -When cleaning the map, you can specify extra options for extra cleaning: - -``mud`` - Also remove mud. -``item`` - Also remove item spatter, like fallen leaves and flowers. -``snow`` - Also remove snow coverings. +``-a``, ``--all`` + Equivalent to ``--map --units --items --plants``. +``-m``, ``--map`` + Clean selected map tiles. Cleans most spatter by default, but not mud, + snow, or item spatter. +``-d``, ``--mud`` + Also remove mud from map tiles. +``-s``, ``--snow`` + Also remove snow coverings from map tiles. +``-t``, ``--item`` + Also remove item spatter (e.g., fallen leaves, flowers, and gatherable + tree fruit) from map tiles. Not to be confused with ``--items``, which + cleans contaminants *off* of items. +``-g``, ``--grass`` + Remove unused (entirely depleted) grass events from map blocks. DF will + create a grass events for each type of grass that grows in a block, but + doesn't remove them if you were to pave over everything, or they got + depleted and entirely replaced with a different type. Could possibly + improve FPS if you had a ton of unused grass events everywhere (a likely + outcome of using ``regrass --new``). Requires ``--map`` option to be + specified. + +``-x``, ``--desolate`` + Use with caution! Remove grass from the selected area. You probably don't + want to use this on the entire map, so make sure to use ``pos`` arguments. + Requires ``--map`` and ``--grass`` options to be specified. +``-o``, ``--only`` + Ignore most spatter (e.g., blood, vomit, and ooze) and focus only on the + other specified options. Requires ``--map`` and at least one of: ``--mud``, + ``--snow``, ``--item``, or ``--grass``. +``-u``, ``--units`` + Clean all contaminants off of units in the selected area. Not affected by + map options that specify spatter types (e.g., snow). They will always be + completely cleaned. +``-i``, ``--items`` + Clean all contaminants off of items in the selected area (including those + held by units). Not affected by map options that specify spatter types. + Not to be confused with ``--item``, which removes tree droppings found on + the ground. +``-z``, ``--zlevel`` + Select entire z-levels. Will do all z-levels between ``pos`` arguments if + both are given, z-level of first ``pos`` if one is given, else z-level of + current view if no ``pos`` is given. + +Troubleshooting +--------------- + +Use ``debugfilter set Debug cleaners log`` (or +``debugfilter set Trace cleaners log`` for more detail) to help diagnose +issues. (Avoid cleaning large parts of the map using many options with +Trace enabled, as it could make the game unresponsive and flood the console +for a good minute.) + +Disable with ``debugfilter set Info cleaners log``. From 174aa2eaed8089fbaee2ad07cb18d5ef34757f4e Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Fri, 20 Sep 2024 03:46:46 -0700 Subject: [PATCH 03/29] Update regrass.rst - Talk about cleaners remove grass --- docs/plugins/regrass.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/plugins/regrass.rst b/docs/plugins/regrass.rst index 8d42fe5a77..16c1fe43f1 100644 --- a/docs/plugins/regrass.rst +++ b/docs/plugins/regrass.rst @@ -9,6 +9,10 @@ This command can refresh the grass (and subterranean moss) growing on your map. Operates on floors, stairs, and ramps. Also works underneath shrubs, saplings, and tree trunks. Ignores furrowed soil and wet sand (beaches). +The `cleaners` tool can help remove grass if you messed up and suddenly there's +staring eyeballs all over your fort. `gui/tiletypes` can then be used to +change the soil back to stone. + Usage ----- From 96ab31d7734b429162017ba66fed9124a5d7cf6e Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Fri, 20 Sep 2024 03:49:57 -0700 Subject: [PATCH 04/29] Update LuaApi.cpp - Add maps.isPlantInBox --- library/LuaApi.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index e193bb8cc6..79bb63cd87 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -2531,6 +2531,20 @@ static int maps_getPlantAtTile(lua_State *L) return 1; } +static int maps_isPlantInBox(lua_State *state) +{ + auto plant = Lua::CheckDFObject(state, 1); + int x1 = luaL_checkint(state, 2); + int y1 = luaL_checkint(state, 3); + int z1 = luaL_checkint(state, 4); + int x2 = luaL_checkint(state, 5); + int y2 = luaL_checkint(state, 6); + int z2 = luaL_checkint(state, 7); + + lua_pushboolean(state, Maps::isPlantInBox(plant, x1, y1, z1, x2, y2, z2)); + return 1; +} + static int maps_getBiomeType(lua_State *L) { auto pos = CheckCoordXY(L, 1, true); @@ -2596,6 +2610,7 @@ static const luaL_Reg dfhack_maps_funcs[] = { { "getRegionBiome", maps_getRegionBiome }, { "getTileBiomeRgn", maps_getTileBiomeRgn }, { "getPlantAtTile", maps_getPlantAtTile }, + { "isPlantInBox", maps_isPlantInBox }, { "getBiomeType", maps_getBiomeType }, { "isTileAquifer", maps_isTileAquifer }, { "isTileHeavyAquifer", maps_isTileHeavyAquifer }, From da108eed5ef7f9d7b70f9a0d89522a8cbed02dbf Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Fri, 20 Sep 2024 03:59:43 -0700 Subject: [PATCH 05/29] Update Maps.h - isPlantInBox --- library/include/modules/Maps.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/include/modules/Maps.h b/library/include/modules/Maps.h index f5a3be142c..93c4841dd0 100644 --- a/library/include/modules/Maps.h +++ b/library/include/modules/Maps.h @@ -382,6 +382,10 @@ DFHACK_EXPORT bool canStepBetween(df::coord pos1, df::coord pos2); // Get the plant that owns the tile at the specified position. extern DFHACK_EXPORT df::plant *getPlantAtTile(int32_t x, int32_t y, int32_t z); inline df::plant *getPlantAtTile(df::coord pos) { return getPlantAtTile(pos.x, pos.y, pos.z); } +// Returns true if the plant is within a box defined by the specified coordinates, accounting for trees. +DFHACK_EXPORT bool isPlantInBox(df::plant *plant, const cuboid &bounds); +inline bool isPlantInBox(df::plant *plant, int16_t x1, int16_t y1, int16_t z1, + int16_t x2, int16_t y2, int16_t z2) { return isPlantInBox(plant, cuboid(x1, y1, z1, x2, y2, z2)); } // Get the biome type at the given region coordinates. DFHACK_EXPORT df::enums::biome_type::biome_type getBiomeTypeWithRef(int16_t region_x, int16_t region_y, int16_t region_ref_y); From b5dbea2356d0203bb81e5ec6dca4732ed99bd48a Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Fri, 20 Sep 2024 04:03:04 -0700 Subject: [PATCH 06/29] Update tile-material.lua - Use getPlantAtTile; handle roots --- library/lua/tile-material.lua | 30 ++++-------------------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/library/lua/tile-material.lua b/library/lua/tile-material.lua index c0fe2e7cb6..dadbb38b43 100644 --- a/library/lua/tile-material.lua +++ b/library/lua/tile-material.lua @@ -172,33 +172,10 @@ end -- GetTreeMat returns the material of the tree at the given tile or nil if the tile does not have a -- tree or giant mushroom. --- Currently roots are ignored. function GetTreeMat(x, y, z) local pos = prepPos(x, y, z) - - local function coordInTree(pos, tree) - local x1 = tree.pos.x - math.floor(tree.tree_info.dim_x / 2) - local x2 = tree.pos.x + math.floor(tree.tree_info.dim_x / 2) - local y1 = tree.pos.y - math.floor(tree.tree_info.dim_y / 2) - local y2 = tree.pos.y + math.floor(tree.tree_info.dim_y / 2) - local z1 = tree.pos.z - local z2 = tree.pos.z + tree.tree_info.body_height - - if not ((pos.x >= x1 and pos.x <= x2) and (pos.y >= y1 and pos.y <= y2) and (pos.z >= z1 and pos.z <= z2)) then - return false - end - - return not tree.tree_info.body[pos.z - tree.pos.z]:_displace((pos.y - y1) * tree.tree_info.dim_x + (pos.x - x1)).blocked - end - - for _, tree in ipairs(df.global.world.plants.all) do - if tree.tree_info ~= nil then - if coordInTree(pos, tree) then - return dfhack.matinfo.decode(419, tree.material) - end - end - end - return nil + local plant = dfhack.maps.getPlantAtTile(pos) + return plant and plant.tree_info and dfhack.matinfo.decode(419, plant.material) or nil end -- GetShrubMat returns the material of the shrub at the given tile or nil if the tile does not @@ -289,7 +266,7 @@ BasicMats = { [df.tiletype_material.DRIFTWOOD] = GetLayerMat, [df.tiletype_material.POOL] = GetLayerMat, [df.tiletype_material.BROOK] = GetLayerMat, - [df.tiletype_material.ROOT] = GetLayerMat, + [df.tiletype_material.ROOT] = GetTreeMat, [df.tiletype_material.TREE] = GetTreeMat, [df.tiletype_material.MUSHROOM] = GetTreeMat, [df.tiletype_material.UNDERWORLD_GATE] = nil, -- I guess this is for the gates found in vaults? @@ -327,6 +304,7 @@ OnlyPlantMats = { [df.tiletype_material.GRASS_DRY] = GetGrassMat, [df.tiletype_material.GRASS_DEAD] = GetGrassMat, [df.tiletype_material.PLANT] = GetShrubMat, + [df.tiletype_material.ROOT] = GetTreeMat, [df.tiletype_material.TREE] = GetTreeMat, [df.tiletype_material.MUSHROOM] = GetTreeMat, } From 8f10e8ee25ecc68cb251333f9c5bffb1ab43d09d Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Fri, 20 Sep 2024 04:03:59 -0700 Subject: [PATCH 07/29] Update Maps.cpp - isPlantInBox --- library/modules/Maps.cpp | 41 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index c207360ac4..069b135168 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -938,6 +938,47 @@ df::plant *Maps::getPlantAtTile(int32_t x, int32_t y, int32_t z) return NULL; } +bool Maps::isPlantInBox(df::plant *plant, const cuboid &bounds) +{ + if (!bounds.isValid()) + return false; + else if (bounds.containsPos(plant->pos)) + return true; + else if (!plant->tree_info) + return false; + + auto &pos = plant->pos; + auto &t = *(plant->tree_info); + // Northwest x/y pos of tree bounds + int x_NW = pos.x - (t.dim_x >> 1); + int y_NW = pos.y - (t.dim_y >> 1); + + if (!cuboid(max(0, x_NW), max(0, y_NW), max(0, pos.z - t.roots_depth), + x_NW + t.dim_x, y_NW + t.dim_y, pos.z + t.body_height - 1).clamp(bounds).isValid()) + { // No intersection of tree bounds with cuboid + return false; + } + + int xy_size = t.dim_x * t.dim_y; + // Iterate tree body + for (int z_idx = 0; z_idx < t.body_height; z_idx++) + for (int xy_idx = 0; xy_idx < xy_size; xy_idx++) + if ((t.body[z_idx][xy_idx].whole & 0x7F) != 0 && // Any non-blocked + bounds.containsPos(x_NW + xy_idx % t.dim_x, y_NW + xy_idx / t.dim_x, pos.z + z_idx)) + { + return true; + } + // Iterate tree roots + for (int z_idx = 0; z_idx < t.roots_depth; z_idx++) + for (int xy_idx = 0; xy_idx < xy_size; xy_idx++) + if ((t.roots[z_idx][xy_idx].whole & 0x7F) != 0 && // Any non-blocked + bounds.containsPos(x_NW + xy_idx % t.dim_x, y_NW + xy_idx / t.dim_x, pos.z - z_idx - 1)) + { + return true; + } + return false; +} + /* * Biomes */ From ad11f48641bfcc3dfc5a439fe680c20ea4812008 Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Fri, 20 Sep 2024 04:07:25 -0700 Subject: [PATCH 08/29] Update CMakeLists.txt - cleaners link lua --- plugins/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index f0b35ea9ef..8919dfa02e 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -99,7 +99,7 @@ if(BUILD_SUPPORTED) dfhack_plugin(changevein changevein.cpp) #add_subdirectory(channel-safely) dfhack_plugin(cleanconst cleanconst.cpp) - dfhack_plugin(cleaners cleaners.cpp) + dfhack_plugin(cleaners cleaners.cpp LINK_LIBRARIES lua) dfhack_plugin(cleanowned cleanowned.cpp) dfhack_plugin(createitem createitem.cpp) dfhack_plugin(cursecheck cursecheck.cpp) From e3a529a55bef6ca2ec51cf2dba83f37bd443afa2 Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Fri, 20 Sep 2024 04:11:33 -0700 Subject: [PATCH 09/29] Update burrow.cpp - Use Maps::forCoord; use using better --- plugins/burrow.cpp | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/plugins/burrow.cpp b/plugins/burrow.cpp index 8de149979d..f236fae8ef 100644 --- a/plugins/burrow.cpp +++ b/plugins/burrow.cpp @@ -1,3 +1,5 @@ +// Quickly adjust burrow tiles and units. + #include "Core.h" #include "Debug.h" #include "LuaTools.h" @@ -18,8 +20,8 @@ #include "df/unit.h" #include "df/world.h" -using std::vector; using std::string; +using std::vector; using namespace DFHack; DFHACK_PLUGIN("burrow"); @@ -44,7 +46,7 @@ static void init_diggers(color_ostream& out); static void jobStartedHandler(color_ostream& out, void* ptr); static void jobCompletedHandler(color_ostream& out, void* ptr); -DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { +DFhackCExport command_result plugin_init(color_ostream &out, vector &commands) { DEBUG(status, out).print("initializing %s\n", plugin_name); commands.push_back( PluginCommand("burrow", @@ -117,7 +119,7 @@ static void init_diggers(color_ostream& out) { return; } - std::vector pvec; + vector pvec; int start_id = 0; if (Job::listNewlyCreated(&pvec, &start_id)) { for (auto job : pvec) { @@ -146,16 +148,12 @@ static void jobStartedHandler(color_ostream& out, void* ptr) { static void add_walls_to_burrow(color_ostream &out, df::burrow* b, const df::coord & pos1, const df::coord & pos2) { - for (int z = pos1.z; z <= pos2.z; z++) { - for (int y = pos1.y; y <= pos2.y; y++) { - for (int x = pos1.x; x <= pos2.x; x++) { - df::coord pos(x,y,z); - df::tiletype *tt = Maps::getTileType(pos); - if (tt && isWallTerrain(*tt)) - Burrows::setAssignedTile(b, pos, true); - } - } - } + Maps::forCoord([&b](df::coord pos) { + auto tt = Maps::getTileType(pos); + if (tt && isWallTerrain(*tt)) + Burrows::setAssignedTile(b, pos, true); + return true; // next pos + }, pos1, pos2); } static void expand_burrows(color_ostream &out, const df::coord & pos, df::tiletype prev_tt, df::tiletype tt) { @@ -327,7 +325,7 @@ static void setTilesByDesignation(df::burrow *target, df::tile_designation d_mas } } -static bool setTilesByKeyword(df::burrow *target, std::string name, bool enable) { +static bool setTilesByKeyword(df::burrow *target, string name, bool enable) { CHECK_NULL_POINTER(target); df::tile_designation mask; From d165e27bca70a5e8afc08fc8028598f81e178702 Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Fri, 20 Sep 2024 04:14:37 -0700 Subject: [PATCH 10/29] Update plant.cpp - Move utility fn to Maps module --- plugins/plant.cpp | 51 +++-------------------------------------------- 1 file changed, 3 insertions(+), 48 deletions(-) diff --git a/plugins/plant.cpp b/plugins/plant.cpp index 4f7ca00f73..035b911d6a 100644 --- a/plugins/plant.cpp +++ b/plugins/plant.cpp @@ -15,9 +15,6 @@ #include "df/map_block_column.h" #include "df/plant.h" #include "df/plant_raw.h" -#include "df/plant_root_tile.h" -#include "df/plant_tree_info.h" -#include "df/plant_tree_tile.h" #include "df/world.h" using std::string; @@ -249,48 +246,6 @@ command_result df_createplant(color_ostream &out, const df::coord &pos, const pl return CR_OK; } -static bool plant_in_cuboid(const df::plant *plant, const cuboid &bounds) -{ // Will detect tree tiles - if (bounds.containsPos(plant->pos)) - return true; - else if (!plant->tree_info) - return false; - - auto &pos = plant->pos; - auto &t = *(plant->tree_info); - - // Northwest x/y pos of tree bounds - int x_NW = pos.x - (t.dim_x >> 1); - int y_NW = pos.y - (t.dim_y >> 1); - - if (!cuboid(std::max(0, x_NW), std::max(0, y_NW), std::max(0, pos.z - t.roots_depth), - x_NW + t.dim_x, y_NW + t.dim_y, pos.z + t.body_height - 1).clamp(bounds).isValid()) - { // No intersection of tree bounds with cuboid - return false; - } - - int xy_size = t.dim_x * t.dim_y; - // Iterate tree body - for (int z_idx = 0; z_idx < t.body_height; z_idx++) - for (int xy_idx = 0; xy_idx < xy_size; xy_idx++) - if ((t.body[z_idx][xy_idx].whole & 0x7F) != 0 && // Any non-blocked - bounds.containsPos(x_NW + xy_idx % t.dim_x, y_NW + xy_idx / t.dim_x, pos.z + z_idx)) - { - return true; - } - - // Iterate tree roots - for (int z_idx = 0; z_idx < t.roots_depth; z_idx++) - for (int xy_idx = 0; xy_idx < xy_size; xy_idx++) - if ((t.roots[z_idx][xy_idx].whole & 0x7F) != 0 && // Any non-blocked - bounds.containsPos(x_NW + xy_idx % t.dim_x, y_NW + xy_idx / t.dim_x, pos.z - z_idx - 1)) - { - return true; - } - - return false; -} - command_result df_grow(color_ostream &out, const cuboid &bounds, const plant_options &options, vector *filter = nullptr) { if (!bounds.isValid()) @@ -312,7 +267,7 @@ command_result df_grow(color_ostream &out, const cuboid &bounds, const plant_opt { if (ENUM_ATTR(plant_type, is_shrub, plant->type)) continue; // Shrub - else if (!plant_in_cuboid(plant, bounds)) + else if (!Maps::isPlantInBox(plant, bounds)) continue; // Outside cuboid else if (do_filter && (vector_contains(*filter, (int32_t)plant->material) == options.filter_ex)) continue; // Filtered out @@ -459,7 +414,7 @@ command_result df_removeplant(color_ostream &out, const cuboid &bounds, const pl continue; // Not removing saplings } - if (!plant_in_cuboid(&plant, bounds)) + if (!Maps::isPlantInBox(&plant, bounds)) continue; // Outside cuboid else if (do_filter && (vector_contains(*filter, (int32_t)plant.material) == options.filter_ex)) continue; // Filtered out @@ -577,7 +532,7 @@ command_result df_plant(color_ostream &out, vector ¶meters) DEBUG(log, out).print("pos_1 = (%d, %d, %d)\npos_2 = (%d, %d, %d)\n", pos_1.x, pos_1.y, pos_1.z, pos_2.x, pos_2.y, pos_2.z); - if (!Core::getInstance().isMapLoaded()) + if (!Maps::IsValid()) { out.printerr("Map not loaded!\n"); return CR_FAILURE; From 86f98b0501b02c4fa94ee0d4709f80a7d24ee08d Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Fri, 20 Sep 2024 04:16:26 -0700 Subject: [PATCH 11/29] Update regrass.cpp - Maps::isValid is cleaner --- plugins/regrass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/regrass.cpp b/plugins/regrass.cpp index dd4ec9788c..d06e63f5eb 100644 --- a/plugins/regrass.cpp +++ b/plugins/regrass.cpp @@ -458,7 +458,7 @@ command_result df_regrass(color_ostream &out, vector ¶meters) out.printerr("Invalid pos for --block (or used more than one!)\n"); return CR_WRONG_USAGE; } - else if (!Core::getInstance().isMapLoaded()) { + else if (!Maps::IsValid()) { out.printerr("Map not loaded!\n"); return CR_FAILURE; } From 4bd5becf131505cbde44f57d700d9de80d5fde92 Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Fri, 20 Sep 2024 04:33:49 -0700 Subject: [PATCH 12/29] Create cleaners.lua --- plugins/lua/cleaners.lua | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 plugins/lua/cleaners.lua diff --git a/plugins/lua/cleaners.lua b/plugins/lua/cleaners.lua new file mode 100644 index 0000000000..30f6c9cacb --- /dev/null +++ b/plugins/lua/cleaners.lua @@ -0,0 +1,39 @@ +local _ENV = mkmodule('plugins.cleaners') + +local argparse = require('argparse') +local utils = require('utils') + +function parse_commandline(opts, pos_1, pos_2, args) + local positionals = argparse.processArgsGetopt(args, + { + {'a', 'all', handler=function() + opts.map = true + opts.units = true + opts.items = true + opts.plants = true end}, + {'m', 'map', handler=function() opts.map = true end}, + {'d', 'mud', handler=function() opts.mud = true end}, + {'s', 'snow', handler=function() opts.snow = true end}, + {'t', 'item', handler=function() opts.item_spat = true end}, + {'g', 'grass', handler=function() opts.grass = true end}, + {'x', 'desolate', handler=function() opts.desolate = true end}, + {'o', 'only', handler=function() opts.only = true end}, + {'u', 'units', handler=function() opts.units = true end}, + {'i', 'items', handler=function() opts.items = true end}, + {'z', 'zlevel', handler=function() opts.zlevel = true end}, + }) + + if #positionals > 2 then + qerror('Too many positionals!') + end + + if positionals[1] then + utils.assign(pos_1, argparse.coords(positionals[1], 'pos_1', true)) + end + + if positionals[2] then + utils.assign(pos_2, argparse.coords(positionals[2], 'pos_2', true)) + end +end + +return _ENV From a4ccb445ec497ccdfa50c4801adcf3d0fbc00794 Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Fri, 20 Sep 2024 04:52:47 -0700 Subject: [PATCH 13/29] Update cleaners.cpp * Cuboid selections * Lua argparse * Allow remove grass, skip blood removal * Better track affected blocks (arrow debris) * Debug logging --- plugins/cleaners.cpp | 515 +++++++++++++++++++++++++++++++------------ 1 file changed, 376 insertions(+), 139 deletions(-) diff --git a/plugins/cleaners.cpp b/plugins/cleaners.cpp index c1afdb5537..19a132ef9f 100644 --- a/plugins/cleaners.cpp +++ b/plugins/cleaners.cpp @@ -1,9 +1,19 @@ +// Provides commands for cleaning spatter (and grass) from the map. + +#include "Debug.h" +#include "LuaTools.h" #include "PluginManager.h" +#include "TileTypes.h" #include "modules/Buildings.h" +#include "modules/Items.h" +#include "modules/Gui.h" #include "modules/Maps.h" +#include "modules/Units.h" #include "df/block_square_event.h" +#include "df/block_square_event_grassst.h" +#include "df/block_square_event_item_spatterst.h" #include "df/block_square_event_material_spatterst.h" #include "df/building.h" #include "df/builtin_mats.h" @@ -16,99 +26,268 @@ #include "df/unit_spatter.h" #include "df/world.h" -using std::vector; using std::string; +using std::vector; using namespace DFHack; using namespace df::enums; DFHACK_PLUGIN("cleaners"); REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(cursor); -static void clean_mud_safely(df::block_square_event_material_spatterst *spatter, - const df::coord &block_pos, const df::coord &offset) +namespace DFHack { - df::coord pos = block_pos + offset; - auto bld = Buildings::findAtTile(pos); - if (!bld || bld->getType() != building_type::FarmPlot) - spatter->amount[offset.x][offset.y] = 0; + DBG_DECLARE(cleaners, log, DebugCategory::LINFO); } -command_result cleanmap (color_ostream &out, bool snow, bool mud, bool item_spatter) +struct clean_options { - // Invoked from clean(), already suspended - int num_blocks = 0; - for (auto block : world->map.map_blocks) + bool map = false; // Clean spatter from the ground + bool mud = false; // Clean mud when doing map + bool snow = false; // Clean snow when doing map + bool item_spat = false; // Clean item spatter when doing map + bool grass = false; // Delete surplus grass events when doing map + bool desolate = false; // Remove all grass when doing map. Requires --grass. Careful! + bool only = false; // Ignore blood/other when doing map, only do specified options + bool units = false; // Clean spatter from units + bool items = false; // Clean spatter from items + bool plants = false; // Clean spatter from plants + bool zlevel = false; // Operate on entire z-levels + + static struct_identity _identity; +}; +static const struct_field_info clean_options_fields[] = +{ + { struct_field_info::PRIMITIVE, "map", offsetof(clean_options, map), &df::identity_traits::identity, 0, 0 }, + { struct_field_info::PRIMITIVE, "mud", offsetof(clean_options, mud), &df::identity_traits::identity, 0, 0 }, + { struct_field_info::PRIMITIVE, "snow", offsetof(clean_options, snow), &df::identity_traits::identity, 0, 0 }, + { struct_field_info::PRIMITIVE, "item_spat", offsetof(clean_options, item_spat), &df::identity_traits::identity, 0, 0 }, + { struct_field_info::PRIMITIVE, "grass", offsetof(clean_options, grass), &df::identity_traits::identity, 0, 0 }, + { struct_field_info::PRIMITIVE, "desolate", offsetof(clean_options, desolate), &df::identity_traits::identity, 0, 0 }, + { struct_field_info::PRIMITIVE, "only", offsetof(clean_options, only), &df::identity_traits::identity, 0, 0 }, + { struct_field_info::PRIMITIVE, "units", offsetof(clean_options, units), &df::identity_traits::identity, 0, 0 }, + { struct_field_info::PRIMITIVE, "items", offsetof(clean_options, items), &df::identity_traits::identity, 0, 0 }, + { struct_field_info::PRIMITIVE, "plants", offsetof(clean_options, plants), &df::identity_traits::identity, 0, 0 }, + { struct_field_info::PRIMITIVE, "zlevel", offsetof(clean_options, zlevel), &df::identity_traits::identity, 0, 0 }, + { struct_field_info::END } +}; +struct_identity clean_options::_identity(sizeof(clean_options), &df::allocator_fn, NULL, "clean_options", NULL, clean_options_fields); + +static bool clean_mud_safely(color_ostream &out, df::block_square_event_material_spatterst *spatter, const df::coord &pos) +{ // Avoid cleaning mud on farm tiles that need it, return true on success + auto &amt = spatter->amount[pos.x&15][pos.y&15]; + if (amt == 0) + return false; // Nothing cleaned + auto tt = Maps::getTileType(pos); + + if (tt && *tt != tiletype::FurrowedSoil) + { // Not furrowed soil, mud might be required + auto bld = Buildings::findAtTile(pos); + if (bld && bld->getType() == building_type::FarmPlot) + { // A farm needs the mud + DEBUG(log, out).print("Protecting mud at (%d,%d,%d)\n", pos.x, pos.y, pos.z); + return false; // Won't clean + } + } + amt = 0; + TRACE(log, out).print("Cleaned mud at (%d,%d,%d)\n", pos.x, pos.y, pos.z); + return true; +} + +static void degrass_tt(color_ostream &out, df::map_block *block, int tx, int ty) +{ // Convert grass to soil + auto &tt = block->tiletype[tx][ty]; + auto mat = tileMaterial(tt); + + if (mat < tiletype_material::GRASS_LIGHT || + mat > tiletype_material::GRASS_DEAD) + return; // Grass under a sapling, etc. + + auto shape = tileShape(tt); + df::tiletype new_tt = tiletype::Void; + + if (shape == tiletype_shape::FLOOR) + new_tt = findRandomVariant(tiletype::SoilFloor1); + else // Ramp or stairs + new_tt = findTileType(shape, tiletype_material::SOIL, tiletype_variant::NONE, tiletype_special::NONE, nullptr); + + TRACE(log, out).print("Degrass %s to %s\n", ENUM_KEY_STR(tiletype, tt).c_str(), ENUM_KEY_STR(tiletype, new_tt).c_str()); + if (new_tt != tiletype::Void) + tt = new_tt; // Assign new tiletype +} + +#define DEL_BLEV block->block_events.erase(block->block_events.begin() + i); delete blev; cleaned = true; + +static bool clean_block(color_ostream &out, df::map_block *block, const cuboid &inter, const clean_options &options) +{ // Perform cleaning on intersection of map block, return true if any tile cleaned + if (!block || !inter.isValid()) { - bool cleaned = false; - for(int x = 0; x < 16; x++) - { - for(int y = 0; y < 16; y++) - { - block->occupancy[x][y].bits.arrow_color = 0; - block->occupancy[x][y].bits.arrow_variant = 0; + DEBUG(log, out).print("Failed cleaning block <%p>\n", block); + return false; + } + DEBUG(log, out).print("Cleaning block at (%d,%d,%d)\n", + block->map_pos.x, block->map_pos.y, block->map_pos.z); + bool cleaned = false; + // Clean arrow debris + inter.forCoord([&](df::coord pos) { + auto &occ = block->occupancy[pos.x&15][pos.y&15]; + cleaned |= (bool)occ.bits.arrow_color; + + occ.bits.arrow_color = 0; + occ.bits.arrow_variant = 0; + return true; // Next pos + }); + // Knowing if block is fully inside cuboid helps optimize + bool full_block = (inter.x_max - inter.x_min) == 15 && (inter.y_max - inter.y_min) == 15; + TRACE(log, out).print("full_block = %d\n", full_block); + // Clean relevant spatter + for (size_t i = block->block_events.size(); i-- > 0;) + { // Iterate block events (blev) backwards + auto blev = block->block_events[i]; + if (auto ms_ev = virtual_cast(blev)) + { // Material spatter + if (ms_ev->mat_type == builtin_mats::MUD && + ms_ev->mat_state == matter_state::Solid) + { // Mud + if (!options.mud) + continue; // Not doing mud, skip + // Handle mud without messing up farms + inter.forCoord([&](df::coord pos) { + cleaned |= clean_mud_safely(out, ms_ev, pos); + return true; // Next pos + }); + // Only delete blev if empty + if (blev->isEmpty()) + { // Block was already empty of mud, or we just made it so + DEBUG(log, out).print("Deleting mud blev at index %d\n", i); + DEL_BLEV + } + continue; // Next blev } + else if (ms_ev->mat_type == builtin_mats::WATER && + ms_ev->mat_state == matter_state::Powder) + { // Snow + if (options.snow) + continue; // Not doing snow, skip + } + else if (options.only) + continue; // Not doing blood/other, skip + + if (!full_block) + { // Non-mud ms_ev, partial clean + inter.forCoord([&](df::coord pos) { + auto &amt = ms_ev->amount[pos.x&15][pos.y&15]; + cleaned |= (bool)amt; + amt = 0; + return true; // Next pos + }); + // Will now check blev->isEmpty() + } + // else full_block, delete blev } - for (size_t j = 0; j < block->block_events.size(); j++) - { - df::block_square_event *evt = block->block_events[j]; - if (evt->getType() == block_square_event_type::material_spatter) - { - // type verified - recast to subclass - df::block_square_event_material_spatterst *spatter = (df::block_square_event_material_spatterst *)evt; - - // filter snow - if(!snow - && spatter->mat_type == builtin_mats::WATER - && spatter->mat_state == (short)matter_state::Powder) - continue; - // filter mud - if(!mud - && spatter->mat_type == builtin_mats::MUD - && spatter->mat_state == (short)matter_state::Solid) - continue; - // save the farm plots - if(mud - && spatter->mat_type == builtin_mats::MUD - && spatter->mat_state == (short)matter_state::Solid) - { - for (size_t x = 0; x < 16; ++x) - for (size_t y = 0; y < 16; ++y) - clean_mud_safely(spatter, block->map_pos, df::coord(x, y, 0)); - continue; - } + else if (auto is_ev = virtual_cast(blev)) + { // Item spatter + if (!options.item_spat) + continue; // Not doing item spatter, skip + else if (!full_block) + { // Partial clean + inter.forCoord([&](df::coord pos) { + auto &amt = is_ev->amount[pos.x&15][pos.y&15]; + cleaned |= (bool)amt; + amt = 0; + return true; // Next pos + }); + // Will now check blev->isEmpty() } - else if (evt->getType() == block_square_event_type::item_spatter) - { - if (!item_spatter) - continue; + // else full_block, delete blev + } + else if (auto gr_ev = virtual_cast(blev)) + { // Grass + if (!options.grass) + continue; // Not doing grass, skip + else if (options.desolate) + { // We're actively removing grass tiles + inter.forCoord([&](df::coord pos) { + auto &amt = gr_ev->amount[pos.x&15][pos.y&15]; + cleaned |= (bool)amt; + amt = 0; + degrass_tt(out, block, pos.x&15, pos.y&15); + return true; // Next pos + }); } - else - continue; + // Only delete blev if empty + if (blev->isEmpty()) + { // Block was already empty of grass type, or we just made it so + DEBUG(log, out).print("Deleting grass blev at index %d\n", i); + DEL_BLEV + } + continue; // Next blev + } + else // Unhandled blev type + continue; // Skip - delete evt; - block->block_events.erase(block->block_events.begin() + j); - j--; - cleaned = true; + if (full_block || blev->isEmpty()) + { // Always delete a full block, else ensure blev empty + DEBUG(log, out).print("Deleting blev at index %d\n", i); + DEL_BLEV } - num_blocks += cleaned; + // Next blev } + DEBUG(log, out).print("cleaned = %d\n", cleaned); + return cleaned; +} +#undef DEL_BLEV - if(num_blocks) - out.print("Cleaned %d of %zd map blocks.\n", num_blocks, world->map.map_blocks.size()); +command_result cleanmap(color_ostream &out, const cuboid &bounds, const clean_options &options) +{ // Invoked from clean(), already suspended + DEBUG(log, out).print("Cleaning map...\n"); + cuboid my_bounds; // Local copy + if (bounds.isValid()) + my_bounds = bounds; + else + { // Do full map + my_bounds.addPos(0, 0, 0); + my_bounds.addPos(world->map.x_count-1, + world->map.y_count-1, world->map.z_count-1); + DEBUG(log, out).print("Invalid cuboid, selecting full map.\n"); + } + int num_blocks = 0, max_blocks = 0; + + my_bounds.forBlock([&](df::map_block *block, cuboid inter) { + num_blocks += clean_block(out, block, inter, options); + max_blocks++; + return true; // Next block + }); + + if(num_blocks > 0) + out.print("Cleaned %d of %d selected map blocks.\n", num_blocks, max_blocks); return CR_OK; } -command_result cleanitems (color_ostream &out) -{ - // Invoked from clean(), already suspended +command_result cleanitems(color_ostream &out, const cuboid &bounds) +{ // Invoked from clean(), already suspended + DEBUG(log, out).print("Cleaning items...\n"); + bool valid_cuboid = bounds.isValid(); // Allow for items outside map if false int cleaned_items = 0, cleaned_total = 0; - for (auto i : world->items.other.IN_PLAY) { - // currently, all item classes extend item_actual, so this should be safe - df::item_actual *item = virtual_cast(i); - if (item && item->contaminants && item->contaminants->size()) + for (auto i : world->items.other.IN_PLAY) + { + TRACE(log, out).print("Considering item #%d\n", i->id); + auto item = virtual_cast(i); + if (!item) + { + out.printerr("Item #%d isn't item_actual!\n", i->id); + continue; + } + else if (valid_cuboid) + { // Check if item is inside cuboid + auto pos = Items::getPosition(item); + if (!pos.isValid() || !bounds.containsPos(pos)) + continue; + } + TRACE(log, out).print("Selected\n"); + + if (item->contaminants && !item->contaminants->empty()) { - std::vector saved; + vector saved; for (size_t j = 0; j < item->contaminants->size(); j++) { auto obj = (*item->contaminants)[j]; @@ -120,136 +299,194 @@ command_result cleanitems (color_ostream &out) cleaned_items++; cleaned_total += item->contaminants->size() - saved.size(); item->contaminants->swap(saved); + DEBUG(log, out).print("Cleaned item #%d\n", item->id); } } - if (cleaned_total) + if (cleaned_total > 0) out.print("Removed %d contaminants from %d items.\n", cleaned_total, cleaned_items); return CR_OK; } -command_result cleanunits (color_ostream &out) -{ - // Invoked from clean(), already suspended +command_result cleanunits(color_ostream &out, const cuboid &bounds) +{ // Invoked from clean(), already suspended + DEBUG(log, out).print("Cleaning units...\n"); + bool valid_cuboid = bounds.isValid(); // Allow for dead/inactive units if false int cleaned_units = 0, cleaned_total = 0; for (auto unit : world->units.active) { - if (unit->body.spatters.size()) + TRACE(log, out).print("Considering unit #%d\n", unit->id); + if (valid_cuboid) + { // Check if unit is inside cuboid + if (!Units::isActive(unit)) + continue; // Dead or off map + auto pos = Units::getPosition(unit); + if (!pos.isValid() || !bounds.containsPos(pos)) + continue; + } + TRACE(log, out).print("Selected\n"); + + if (!unit->body.spatters.empty()) { for (size_t j = 0; j < unit->body.spatters.size(); j++) delete unit->body.spatters[j]; cleaned_units++; cleaned_total += unit->body.spatters.size(); unit->body.spatters.clear(); + DEBUG(log, out).print("Cleaned unit #%d\n", unit->id); } } - if (cleaned_total) + if (cleaned_total > 0) out.print("Removed %d contaminants from %d creatures.\n", cleaned_total, cleaned_units); return CR_OK; } -command_result cleanplants (color_ostream &out) -{ - // Invoked from clean(), already suspended +command_result cleanplants(color_ostream &out, const cuboid &bounds) +{ // Invoked from clean(), already suspended + DEBUG(log, out).print("Cleaning plants...\n"); + bool valid_cuboid = bounds.isValid(); // Skip pos check if false int cleaned_plants = 0, cleaned_total = 0; for (auto plant : world->plants.all) { - if (plant->contaminants.size()) + TRACE(log, out).print("Considering plant <%p>\n", plant); + if (valid_cuboid && !Maps::isPlantInBox(plant, bounds)) + continue; + TRACE(log, out).print("Selected\n"); + + if (!plant->contaminants.empty()) { for (size_t j = 0; j < plant->contaminants.size(); j++) delete plant->contaminants[j]; cleaned_plants++; cleaned_total += plant->contaminants.size(); plant->contaminants.clear(); + DEBUG(log, out).print("Cleaned plant <%p>\n", plant); } } - if (cleaned_total) + if (cleaned_total > 0) out.print("Removed %d contaminants from %d plants.\n", cleaned_total, cleaned_plants); return CR_OK; } -command_result spotclean (color_ostream &out, vector & parameters) -{ - // HOTKEY COMMAND: CORE ALREADY SUSPENDED - if (cursor->x < 0) - { - out.printerr("The cursor is not active.\n"); - return CR_WRONG_USAGE; - } +command_result spotclean(color_ostream &out, vector ¶meters) +{ // Hotkey command, already suspended + DEBUG(log, out).print("Doing spotclean.\n"); if (!Maps::IsValid()) { out.printerr("Map is not available.\n"); return CR_FAILURE; } - df::map_block *block = Maps::getTileBlock(cursor->x, cursor->y, cursor->z); - if (block == NULL) + auto pos = Gui::getCursorPos(); + if (!pos.isValid()) + { + out.printerr("The keyboard cursor is not active.\n"); + return CR_WRONG_USAGE; + } + auto block = Maps::getTileBlock(pos); + if (!block) { out.printerr("Invalid map block selected!\n"); return CR_FAILURE; } + clean_options options; + options.mud = true; + options.snow = true; - for (auto evt : block->block_events) - { - if (evt->getType() != block_square_event_type::material_spatter) - continue; - // type verified - recast to subclass - df::block_square_event_material_spatterst *spatter = (df::block_square_event_material_spatterst *)evt; - clean_mud_safely(spatter, block->map_pos, df::coord(cursor->x % 16, cursor->y % 16, 0)); - } + clean_block(out, block, cuboid(pos), options); return CR_OK; } -command_result clean (color_ostream &out, vector & parameters) +command_result clean(color_ostream &out, vector ¶meters) { - bool map = false; - bool snow = false; - bool mud = false; - bool item_spatter = false; - bool units = false; - bool items = false; - bool plants = false; - for(size_t i = 0; i < parameters.size();i++) + clean_options options; + df::coord pos_1, pos_2; + cuboid bounds; + + CoreSuspender suspend; + + if (!Lua::CallLuaModuleFunction(out, "plugins.cleaners", "parse_commandline", + std::make_tuple(&options, &pos_1, &pos_2, parameters))) + { + return CR_WRONG_USAGE; + } + + DEBUG(log, out).print("pos_1 = (%d, %d, %d)\npos_2 = (%d, %d, %d)\n", + pos_1.x, pos_1.y, pos_1.z, pos_2.x, pos_2.y, pos_2.z); + + bool map_target = options.mud || options.snow || options.item_spat || options.grass; + if (!options.map) { - if(parameters[i] == "map") - map = true; - else if(parameters[i] == "units") - units = true; - else if(parameters[i] == "items") - items = true; - else if(parameters[i] == "plants") - plants = true; - else if(parameters[i] == "all") + if (!options.units && !options.items && !options.plants) + { + out.printerr("Choose at least: --map, --units, --items, or --plants. Use --all for all.\n"); + return CR_WRONG_USAGE; + } + else if (options.item_spat && !options.items) { - map = true; - items = true; - units = true; - plants = true; + out.printerr("Must use --map (or --all) with --item. Did you mean --items?\n"); + return CR_WRONG_USAGE; } - else if(parameters[i] == "snow") - snow = true; - else if(parameters[i] == "mud") - mud = true; - else if(parameters[i] == "item") - item_spatter = true; - else + else if (map_target || options.only) + { + out.printerr("Must use --map (or --all) with --mud, --snow, --item, --grass, or --only.\n"); return CR_WRONG_USAGE; + } } - if(!map && !units && !items && !plants) + + if (options.desolate && !options.grass) + { + out.printerr("Must use --grass with --desolate. This kills grass!\n"); return CR_WRONG_USAGE; + } + else if (options.only && !map_target) + { + out.printerr("Specified --only for map, but there's nothing else to do.\n"); + return CR_WRONG_USAGE; + } + else if (!Maps::IsValid()) + { + out.printerr("Map not loaded!\n"); + return CR_FAILURE; + } - CoreSuspender suspend; + if (options.zlevel) + { // Specified z-levels or viewport z + auto z1 = pos_1.isValid() ? pos_1.z : Gui::getViewportPos().z; + auto z2 = pos_2.isValid() ? pos_2.z : z1; + DEBUG(log, out).print("Selecting z-levels %d to %d\n", z1, z2); + bounds.addPos(0, 0, z1); + bounds.addPos(world->map.x_count-1, world->map.y_count-1, z2); + } + else if (pos_1.isValid()) + { // Point or cuboid + DEBUG(log, out).print("Selecting %s.\n", pos_2.isValid() ? "cuboid" : "point"); + bounds.addPos(pos_1); + bounds.addPos(pos_2); // Ignored if invalid + + if (!bounds.clampMap().isValid()) // Clamp to map, check selection + { // No intersection. Don't don't entire map, just fail + out.printerr("Invalid position!\n"); + return CR_FAILURE; + } + } + else + { // Entire map (plus units and items outside map edge) + DEBUG(log, out).print("Selecting entire map.\n"); + } + DEBUG(log, out).print("bounds = (%d:%d, %d:%d, %d:%d)\n", + bounds.x_min, bounds.x_max, bounds.y_min, bounds.y_max, bounds.z_min, bounds.z_max); - if(map) - cleanmap(out,snow,mud,item_spatter); - if(units) - cleanunits(out); - if(items) - cleanitems(out); - if(plants) - cleanplants(out); + if(options.map) + cleanmap(out, bounds, options); + if(options.units) + cleanunits(out, bounds); + if(options.items) + cleanitems(out, bounds); + if(options.plants) + cleanplants(out, bounds); return CR_OK; } -DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +DFhackCExport command_result plugin_init(color_ostream &out, vector &commands) { commands.push_back(PluginCommand( "clean", @@ -258,11 +495,11 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector Date: Fri, 20 Sep 2024 05:04:56 -0700 Subject: [PATCH 14/29] Update cleaners.cpp - Remove plant cleaning (just rainwater) --- plugins/cleaners.cpp | 37 ++----------------------------------- 1 file changed, 2 insertions(+), 35 deletions(-) diff --git a/plugins/cleaners.cpp b/plugins/cleaners.cpp index 19a132ef9f..d0be981358 100644 --- a/plugins/cleaners.cpp +++ b/plugins/cleaners.cpp @@ -19,8 +19,6 @@ #include "df/builtin_mats.h" #include "df/item_actual.h" #include "df/map_block.h" -#include "df/plant.h" -#include "df/plant_spatter.h" #include "df/spatter.h" #include "df/unit.h" #include "df/unit_spatter.h" @@ -50,7 +48,6 @@ struct clean_options bool only = false; // Ignore blood/other when doing map, only do specified options bool units = false; // Clean spatter from units bool items = false; // Clean spatter from items - bool plants = false; // Clean spatter from plants bool zlevel = false; // Operate on entire z-levels static struct_identity _identity; @@ -66,7 +63,6 @@ static const struct_field_info clean_options_fields[] = { struct_field_info::PRIMITIVE, "only", offsetof(clean_options, only), &df::identity_traits::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "units", offsetof(clean_options, units), &df::identity_traits::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "items", offsetof(clean_options, items), &df::identity_traits::identity, 0, 0 }, - { struct_field_info::PRIMITIVE, "plants", offsetof(clean_options, plants), &df::identity_traits::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "zlevel", offsetof(clean_options, zlevel), &df::identity_traits::identity, 0, 0 }, { struct_field_info::END } }; @@ -340,33 +336,6 @@ command_result cleanunits(color_ostream &out, const cuboid &bounds) return CR_OK; } -command_result cleanplants(color_ostream &out, const cuboid &bounds) -{ // Invoked from clean(), already suspended - DEBUG(log, out).print("Cleaning plants...\n"); - bool valid_cuboid = bounds.isValid(); // Skip pos check if false - int cleaned_plants = 0, cleaned_total = 0; - for (auto plant : world->plants.all) - { - TRACE(log, out).print("Considering plant <%p>\n", plant); - if (valid_cuboid && !Maps::isPlantInBox(plant, bounds)) - continue; - TRACE(log, out).print("Selected\n"); - - if (!plant->contaminants.empty()) - { - for (size_t j = 0; j < plant->contaminants.size(); j++) - delete plant->contaminants[j]; - cleaned_plants++; - cleaned_total += plant->contaminants.size(); - plant->contaminants.clear(); - DEBUG(log, out).print("Cleaned plant <%p>\n", plant); - } - } - if (cleaned_total > 0) - out.print("Removed %d contaminants from %d plants.\n", cleaned_total, cleaned_plants); - return CR_OK; -} - command_result spotclean(color_ostream &out, vector ¶meters) { // Hotkey command, already suspended DEBUG(log, out).print("Doing spotclean.\n"); @@ -415,9 +384,9 @@ command_result clean(color_ostream &out, vector ¶meters) bool map_target = options.mud || options.snow || options.item_spat || options.grass; if (!options.map) { - if (!options.units && !options.items && !options.plants) + if (!options.units && !options.items) { - out.printerr("Choose at least: --map, --units, --items, or --plants. Use --all for all.\n"); + out.printerr("Choose at least: --map, --units, or --items. Use --all for all.\n"); return CR_WRONG_USAGE; } else if (options.item_spat && !options.items) @@ -481,8 +450,6 @@ command_result clean(color_ostream &out, vector ¶meters) cleanunits(out, bounds); if(options.items) cleanitems(out, bounds); - if(options.plants) - cleanplants(out, bounds); return CR_OK; } From f910c0cc5054e11f282b33f2771fea2c012f88e9 Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Fri, 20 Sep 2024 06:10:55 -0700 Subject: [PATCH 15/29] Update changelog.txt --- docs/changelog.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 7fbf560635..9803b9fe04 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -54,14 +54,17 @@ Template for new versions: ## New Tools ## New Features +- `cleaners`: positional options to limit cleaning to a cuboid area. Options to remove grass or skip blood spatter removal. ## Fixes +``tile-material.lua``: made root tiles return tree material when not using GetLayerMat. ## Misc Improvements ## Documentation ## API +``Maps::isPlantInBox``: function to determine if a tree's tile is present in a cuboid. ## Lua From 6375c92575465e3e7fb6514a007617c0889b83c8 Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Fri, 20 Sep 2024 06:27:39 -0700 Subject: [PATCH 16/29] Update changelog.txt --- docs/changelog.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 9803b9fe04..5b5b6dcb7d 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -57,14 +57,14 @@ Template for new versions: - `cleaners`: positional options to limit cleaning to a cuboid area. Options to remove grass or skip blood spatter removal. ## Fixes -``tile-material.lua``: made root tiles return tree material when not using GetLayerMat. +- ``tile-material.lua``: made root tiles return tree material when not using GetLayerMat. ## Misc Improvements ## Documentation ## API -``Maps::isPlantInBox``: function to determine if a tree's tile is present in a cuboid. +- ``Maps::isPlantInBox``: function to determine if a tree's tile is present in a cuboid. ## Lua From f89fc3cf2b6cf5c1cc7a85b9b41d473d2805e8e6 Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Fri, 20 Sep 2024 06:33:32 -0700 Subject: [PATCH 17/29] Update cleaners.cpp - Fix print size_t --- plugins/cleaners.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/cleaners.cpp b/plugins/cleaners.cpp index d0be981358..00033c4fed 100644 --- a/plugins/cleaners.cpp +++ b/plugins/cleaners.cpp @@ -154,7 +154,7 @@ static bool clean_block(color_ostream &out, df::map_block *block, const cuboid & // Only delete blev if empty if (blev->isEmpty()) { // Block was already empty of mud, or we just made it so - DEBUG(log, out).print("Deleting mud blev at index %d\n", i); + DEBUG(log, out).print("Deleting mud blev at index %u\n", i); DEL_BLEV } continue; // Next blev @@ -213,7 +213,7 @@ static bool clean_block(color_ostream &out, df::map_block *block, const cuboid & // Only delete blev if empty if (blev->isEmpty()) { // Block was already empty of grass type, or we just made it so - DEBUG(log, out).print("Deleting grass blev at index %d\n", i); + DEBUG(log, out).print("Deleting grass blev at index %u\n", i); DEL_BLEV } continue; // Next blev @@ -223,7 +223,7 @@ static bool clean_block(color_ostream &out, df::map_block *block, const cuboid & if (full_block || blev->isEmpty()) { // Always delete a full block, else ensure blev empty - DEBUG(log, out).print("Deleting blev at index %d\n", i); + DEBUG(log, out).print("Deleting blev at index %u\n", i); DEL_BLEV } // Next blev From 961ceb8f795b54a768fabde43b1b4e521f3cc8c9 Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Fri, 20 Sep 2024 06:38:04 -0700 Subject: [PATCH 18/29] Update cleaners.cpp - Really fix print size_t --- plugins/cleaners.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/cleaners.cpp b/plugins/cleaners.cpp index 00033c4fed..906c637b56 100644 --- a/plugins/cleaners.cpp +++ b/plugins/cleaners.cpp @@ -154,7 +154,7 @@ static bool clean_block(color_ostream &out, df::map_block *block, const cuboid & // Only delete blev if empty if (blev->isEmpty()) { // Block was already empty of mud, or we just made it so - DEBUG(log, out).print("Deleting mud blev at index %u\n", i); + DEBUG(log, out).print("Deleting mud blev at index %lu\n", i); DEL_BLEV } continue; // Next blev @@ -213,7 +213,7 @@ static bool clean_block(color_ostream &out, df::map_block *block, const cuboid & // Only delete blev if empty if (blev->isEmpty()) { // Block was already empty of grass type, or we just made it so - DEBUG(log, out).print("Deleting grass blev at index %u\n", i); + DEBUG(log, out).print("Deleting grass blev at index %lu\n", i); DEL_BLEV } continue; // Next blev @@ -223,7 +223,7 @@ static bool clean_block(color_ostream &out, df::map_block *block, const cuboid & if (full_block || blev->isEmpty()) { // Always delete a full block, else ensure blev empty - DEBUG(log, out).print("Deleting blev at index %u\n", i); + DEBUG(log, out).print("Deleting blev at index %lu\n", i); DEL_BLEV } // Next blev From 244e84870e89261be3a128ba5472379429188db1 Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Fri, 27 Sep 2024 13:48:18 -0700 Subject: [PATCH 19/29] Tidy up docs * Update cleaners.rst * Update regrass.rst --- docs/plugins/cleaners.rst | 8 ++++---- docs/plugins/regrass.rst | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/plugins/cleaners.rst b/docs/plugins/cleaners.rst index cdf548f5ac..0b7de58c79 100644 --- a/docs/plugins/cleaners.rst +++ b/docs/plugins/cleaners.rst @@ -91,12 +91,12 @@ Options ------- ``-a``, ``--all`` - Equivalent to ``--map --units --items --plants``. + Equivalent to ``--map --units --items``. ``-m``, ``--map`` Clean selected map tiles. Cleans most spatter by default, but not mud, snow, or item spatter. ``-d``, ``--mud`` - Also remove mud from map tiles. + Also remove mud from map tiles. Excludes mud required for farm plots. ``-s``, ``--snow`` Also remove snow coverings from map tiles. ``-t``, ``--item`` @@ -106,7 +106,7 @@ Options ``-g``, ``--grass`` Remove unused (entirely depleted) grass events from map blocks. DF will create a grass events for each type of grass that grows in a block, but - doesn't remove them if you were to pave over everything, or they got + doesn't remove them if you pave over everything, or the grass got depleted and entirely replaced with a different type. Could possibly improve FPS if you had a ton of unused grass events everywhere (a likely outcome of using ``regrass --new``). Requires ``--map`` option to be @@ -122,7 +122,7 @@ Options ``--snow``, ``--item``, or ``--grass``. ``-u``, ``--units`` Clean all contaminants off of units in the selected area. Not affected by - map options that specify spatter types (e.g., snow). They will always be + map options that specify spatter types (e.g., snow). Units will always be completely cleaned. ``-i``, ``--items`` Clean all contaminants off of items in the selected area (including those diff --git a/docs/plugins/regrass.rst b/docs/plugins/regrass.rst index 16c1fe43f1..40d3f05cf7 100644 --- a/docs/plugins/regrass.rst +++ b/docs/plugins/regrass.rst @@ -9,9 +9,9 @@ This command can refresh the grass (and subterranean moss) growing on your map. Operates on floors, stairs, and ramps. Also works underneath shrubs, saplings, and tree trunks. Ignores furrowed soil and wet sand (beaches). -The `cleaners` tool can help remove grass if you messed up and suddenly there's -staring eyeballs all over your fort. `gui/tiletypes` can then be used to -change the soil back to stone. +The `cleaners` tool can help remove grass if you messed up and suddenly there +are staring eyeballs growing all over your fort. `gui/tiletypes` can then be used +to change the soil back to stone. Usage ----- From 9b1b88068a64066a6f5b73175ecad30899184b0e Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Fri, 27 Sep 2024 14:03:35 -0700 Subject: [PATCH 20/29] More doc tidying * Update regrass.rst * Update cleaners.rst --- docs/plugins/cleaners.rst | 4 ++-- docs/plugins/regrass.rst | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/plugins/cleaners.rst b/docs/plugins/cleaners.rst index 0b7de58c79..a6eb6fe384 100644 --- a/docs/plugins/cleaners.rst +++ b/docs/plugins/cleaners.rst @@ -105,8 +105,8 @@ Options cleans contaminants *off* of items. ``-g``, ``--grass`` Remove unused (entirely depleted) grass events from map blocks. DF will - create a grass events for each type of grass that grows in a block, but - doesn't remove them if you pave over everything, or the grass got + create a grass event for each type of grass that grows in a block, but + doesn't remove them if you pave over the block or if the grass got depleted and entirely replaced with a different type. Could possibly improve FPS if you had a ton of unused grass events everywhere (a likely outcome of using ``regrass --new``). Requires ``--map`` option to be diff --git a/docs/plugins/regrass.rst b/docs/plugins/regrass.rst index 40d3f05cf7..21d1baffd4 100644 --- a/docs/plugins/regrass.rst +++ b/docs/plugins/regrass.rst @@ -78,7 +78,10 @@ Options ``-n``, ``--new`` Adds all biome-compatible grass types that were not originally present in the map block. Allows regrass to work in blocks that never had any grass to - begin with. Will still fail in incompatible biomes. + begin with. Will still fail in incompatible biomes. This can add an excessive + amount of grass events to your map, so it may be desirable to run + ``clean --map --grass --only`` (see: `cleaners`) to clean up any unused events + afterwards. ``-f``, ``--force`` Force a grass type on tiles with no compatible grass types. Unsets the ``no_grow`` flag on all tiles. The ``--new`` option takes precedence for From ed4e3e022469d25faec0c5d7a31e44864244cff0 Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Fri, 27 Sep 2024 14:16:59 -0700 Subject: [PATCH 21/29] Update regrass.rst - Minor wording change --- docs/plugins/regrass.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/plugins/regrass.rst b/docs/plugins/regrass.rst index 21d1baffd4..8aedc70170 100644 --- a/docs/plugins/regrass.rst +++ b/docs/plugins/regrass.rst @@ -79,9 +79,9 @@ Options Adds all biome-compatible grass types that were not originally present in the map block. Allows regrass to work in blocks that never had any grass to begin with. Will still fail in incompatible biomes. This can add an excessive - amount of grass events to your map, so it may be desirable to run - ``clean --map --grass --only`` (see: `cleaners`) to clean up any unused events - afterwards. + number of grass events to your map, so it may be desirable to run + ``clean --map --grass --only`` (see: `cleaners`) afterwards to clean up any + unused events. ``-f``, ``--force`` Force a grass type on tiles with no compatible grass types. Unsets the ``no_grow`` flag on all tiles. The ``--new`` option takes precedence for From 6d26265ffe7a992f50c825e16330f50fe88ec6a0 Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Fri, 27 Sep 2024 14:17:40 -0700 Subject: [PATCH 22/29] Update regrass.rst --- docs/plugins/regrass.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plugins/regrass.rst b/docs/plugins/regrass.rst index 8aedc70170..f4134c13ee 100644 --- a/docs/plugins/regrass.rst +++ b/docs/plugins/regrass.rst @@ -78,8 +78,8 @@ Options ``-n``, ``--new`` Adds all biome-compatible grass types that were not originally present in the map block. Allows regrass to work in blocks that never had any grass to - begin with. Will still fail in incompatible biomes. This can add an excessive - number of grass events to your map, so it may be desirable to run + begin with. Will still fail in incompatible biomes. Note: This can add an + excessive number of grass events to your map, so it may be desirable to run ``clean --map --grass --only`` (see: `cleaners`) afterwards to clean up any unused events. ``-f``, ``--force`` From e121d8f4201d904fc45437bdd5696a6cf97f2ebd Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Sun, 8 Dec 2024 00:58:35 -0800 Subject: [PATCH 23/29] Fix changelog.txt --- docs/changelog.txt | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 0454d688b3..7bf6b8b9f0 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -52,26 +52,51 @@ Template for new versions: # Future ## New Tools +- `infinite-sky`: (reinstated, renamed from ``infiniteSky``) automatically create new z-levels of sky to build in +- `forceequip`: (reinstated) forcibly move items into a unit's inventory ## New Features +- `tweak`: ``realistic-melting``: change melting return for inorganic armor parts, shields, weapons, trap components and tools to stop smelters from creating metal, bring melt return for adamantine in line with other metals to ~95% of forging cost. wear reduces melt return by 10% per level - `cleaners`: positional options to limit cleaning to a cuboid area. Options to remove grass or skip blood spatter removal. ## Fixes +- Fix mouse clicks bleeding through resizable DFHack windows when clicking in the space between the frame and the window content +- `autobutcher`: don't run a scanning and marking cycle on the first tick of a fortress to allow for all custom configuration to be set first +- `nestboxes`: don't consider eggs to be infertile just because the mother has left the nest; eggs can still hatch in this situation +- `timestream`: adjust the incubation counter on fertile eggs so they hatch at the expected time +- `timestream`: adjust the timeout on traps so they can be re-triggered at normal rates +- `logistics`: don't ignore rotten items when applying stockpile logistics operations (e.g. autodump, autoclaim, etc.) - ``tile-material.lua``: made root tiles return tree material when not using GetLayerMat. ## Misc Improvements +- DFHack now verifies that critical DF data structures have known sizes and refuses to start if there is a mismatch - DFHack text edit fields now delete the character at the cursor when you hit the Delete key - DFHack text edit fields now move the cursor by one word left or right with Ctrl-Left and Ctrl-Right - DFHack text edit fields now move the cursor to the beginning or end of the line with Home and End +- Quickfort blueprint library: ``aquifer_tap`` blueprint walkthough rewritten for clarity +- Quickfort blueprint library: ``aquifer_tap`` blueprint now designated at priority 3 and marks the stairway tile below the tap in "blueprint" mode to prevent drips while the drainage pipe is being prepared +- `preserve-rooms`: automatically release room reservations for captured squad members. we were kidding ourselves with our optimistic kept reservations. they're unlikely to come back : (( +- `buildingplan`: add value info to item selection dialog (effectively ungrouping items with different values) and add sorting by value +- `timestream`: improve FPS by a further 10% +- `fix/occupancy`: additionally handle the case where tile building occupancy needs to be set instead of cleared +- `orders`: ``orders sort`` now moves orders that are tied to a specific workshop to the top of the list in the overall manager orders screen ## Documentation +- Dreamfort: add link to Dreamfort tutorial youtube series: https://www.youtube.com/playlist?list=PLzXx9JcB9oXxmrtkO1y8ZXzBCFEZrKxve +- The error message that comes up if there is a version mismatch between DF and DFHack now informs you which DF versions are supported by the installed version of DFHack ## API +- ``DFHack::Units``: new function ``setPathGoal`` +- ``Units::setAutomaticProfessions``: bay12-provided entry point to assign labors based on work details - ``Maps::isPlantInBox``: function to determine if a tree's tile is present in a cuboid. ## Lua +- ``dfhack.units``: new function ``setPathGoal`` +- ``widgets.TabBar``: updated to allow for horizontal scrolling of tabs when there are too many to fit in the available space ## Removed +- UI focus strings for squad panel flows combined into a single tree: ``dwarfmode/SquadEquipment`` -> ``dwarfmode/Squads/Equipment``, ``dwarfmode/SquadSchedule`` -> ``dwarfmode/Squads/Schedule`` +- `faststart`: removed since the vanilla startup sequence is now sufficiently fast # 50.14-r1 From 7b301298060c21b37c84968f9c87b8c5b11dbf98 Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Tue, 10 Dec 2024 21:43:28 -0800 Subject: [PATCH 24/29] Fix changelog.txt --- docs/changelog.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index b07fcba3d7..24dbfd624b 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -54,14 +54,17 @@ Template for new versions: ## New Tools ## New Features +- `cleaners`: positional options to limit cleaning to a cuboid area. Options to remove grass or skip blood spatter removal. ## Fixes +- ``tile-material.lua``: made root tiles return tree material when not using GetLayerMat. ## Misc Improvements ## Documentation ## API +- ``Maps::isPlantInBox``: function to determine if a tree's tile is present in a cuboid. ## Lua @@ -75,7 +78,6 @@ Template for new versions: ## New Features - `tweak`: ``realistic-melting``: change melting return for inorganic armor parts, shields, weapons, trap components and tools to stop smelters from creating metal, bring melt return for adamantine in line with other metals to ~95% of forging cost. wear reduces melt return by 10% per level -- `cleaners`: positional options to limit cleaning to a cuboid area. Options to remove grass or skip blood spatter removal. ## Fixes - Fix mouse clicks bleeding through resizable DFHack windows when clicking in the space between the frame and the window content @@ -84,7 +86,6 @@ Template for new versions: - `timestream`: adjust the incubation counter on fertile eggs so they hatch at the expected time - `timestream`: adjust the timeout on traps so they can be re-triggered at normal rates - `logistics`: don't ignore rotten items when applying stockpile logistics operations (e.g. autodump, autoclaim, etc.) -- ``tile-material.lua``: made root tiles return tree material when not using GetLayerMat. ## Misc Improvements - DFHack now verifies that critical DF data structures have known sizes and refuses to start if there is a mismatch @@ -107,7 +108,6 @@ Template for new versions: ## API - ``DFHack::Units``: new function ``setPathGoal`` - ``Units::setAutomaticProfessions``: bay12-provided entry point to assign labors based on work details -- ``Maps::isPlantInBox``: function to determine if a tree's tile is present in a cuboid. ## Lua - ``dfhack.units``: new function ``setPathGoal`` From 82ec22e289b38d5e60f442233fbde7c6cb4f10d0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 11 Dec 2024 05:45:04 +0000 Subject: [PATCH 25/29] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .github/release_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/release_template.md b/.github/release_template.md index b572551d8a..2bfc7a3c04 100644 --- a/.github/release_template.md +++ b/.github/release_template.md @@ -61,4 +61,4 @@ Changelog New tools, fixes, and improvements %RELEASE_NOTES% - \ No newline at end of file + From 28f7fca72e0423e7315da405cce4a75b1fcd80b4 Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Fri, 27 Dec 2024 06:42:44 -0800 Subject: [PATCH 26/29] Update cleaners.cpp --- plugins/cleaners.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/cleaners.cpp b/plugins/cleaners.cpp index 906c637b56..c98005cad8 100644 --- a/plugins/cleaners.cpp +++ b/plugins/cleaners.cpp @@ -457,11 +457,11 @@ DFhackCExport command_result plugin_init(color_ostream &out, vector Date: Fri, 27 Dec 2024 06:46:53 -0800 Subject: [PATCH 27/29] Requested changes * Pos args for isPlantInBox * Update changelog.txt * Update cleaners.rst --- docs/changelog.txt | 6 +++--- docs/dev/Lua API.rst | 4 +++- docs/plugins/cleaners.rst | 2 +- library/LuaApi.cpp | 29 +++++++++++++++++++---------- library/include/modules/Maps.h | 1 + 5 files changed, 27 insertions(+), 15 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 0a0d2774eb..1ef0d9254e 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -54,19 +54,19 @@ Template for new versions: ## New Tools ## New Features -- `cleaners`: positional options to limit cleaning to a cuboid area. Options to remove grass or skip blood spatter removal. +- `clean`: positional options to limit cleaning to a cuboid area. Options to remove grass or skip blood spatter removal. ## Fixes -- ``tile-material.lua``: made root tiles return tree material when not using GetLayerMat. ## Misc Improvements ## Documentation ## API -- ``Maps::isPlantInBox``: function to determine if a tree's tile is present in a cuboid. +- ``Maps::isPlantInBox``: function to determine if a plant is present in a cuboid. ## Lua +- ``dfhack.maps.isPlantInBox``: function to determine if a plant is present in a cuboid. ## Removed diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 767be140ac..c76bceed02 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -2307,10 +2307,12 @@ Maps module Returns the plant struct that owns the tile at the specified position. +* ``dfhack.maps.isPlantInBox(plant, pos1, pos2)`` * ``dfhack.maps.isPlantInBox(plant,x1,y1,z1,x2,y2,z2)`` Returns true if the plant is within a box defined by the specified - coordinates, accounting for trees. + coordinates. For trees, returns true if any part of the tree is within + the box. * ``dfhack.maps.getWalkableGroup(pos)`` diff --git a/docs/plugins/cleaners.rst b/docs/plugins/cleaners.rst index a6eb6fe384..02a918ed26 100644 --- a/docs/plugins/cleaners.rst +++ b/docs/plugins/cleaners.rst @@ -5,7 +5,7 @@ cleaners ======== .. dfhack-tool:: - :summary: Provides commands for cleaning spatter (and grass) from the map. + :summary: Cleans spatter and/or grass from map tiles. :tags: adventure fort armok fps items map plants units :no-command: diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index fab7f48b60..149ed60232 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -2533,17 +2533,26 @@ static int maps_getPlantAtTile(lua_State *L) return 1; } -static int maps_isPlantInBox(lua_State *state) +static int maps_isPlantInBox(lua_State *L) { - auto plant = Lua::CheckDFObject(state, 1); - int x1 = luaL_checkint(state, 2); - int y1 = luaL_checkint(state, 3); - int z1 = luaL_checkint(state, 4); - int x2 = luaL_checkint(state, 5); - int y2 = luaL_checkint(state, 6); - int z2 = luaL_checkint(state, 7); - - lua_pushboolean(state, Maps::isPlantInBox(plant, x1, y1, z1, x2, y2, z2)); + auto plant = Lua::CheckDFObject(L, 1); + if (lua_gettop(L) > 3) + { + int x1 = luaL_checkint(L, 2); + int y1 = luaL_checkint(L, 3); + int z1 = luaL_checkint(L, 4); + int x2 = luaL_checkint(L, 5); + int y2 = luaL_checkint(L, 6); + int z2 = luaL_checkint(L, 7); + lua_pushboolean(L, Maps::isPlantInBox(plant, x1, y1, z1, x2, y2, z2)); + } + else + { + df::coord pos1, pos2; + Lua::CheckDFAssign(L, &pos1, 2); + Lua::CheckDFAssign(L, &pos2, 3); + lua_pushboolean(L, Maps::isPlantInBox(plant, pos1, pos2)); + } return 1; } diff --git a/library/include/modules/Maps.h b/library/include/modules/Maps.h index 93c4841dd0..b632bec16a 100644 --- a/library/include/modules/Maps.h +++ b/library/include/modules/Maps.h @@ -386,6 +386,7 @@ inline df::plant *getPlantAtTile(df::coord pos) { return getPlantAtTile(pos.x, p DFHACK_EXPORT bool isPlantInBox(df::plant *plant, const cuboid &bounds); inline bool isPlantInBox(df::plant *plant, int16_t x1, int16_t y1, int16_t z1, int16_t x2, int16_t y2, int16_t z2) { return isPlantInBox(plant, cuboid(x1, y1, z1, x2, y2, z2)); } +inline bool isPlantInBox(df::plant *plant, df::coord pos1, df::coord pos2) { return isPlantInBox(plant, cuboid(pos1, pos2)); } // Get the biome type at the given region coordinates. DFHACK_EXPORT df::enums::biome_type::biome_type getBiomeTypeWithRef(int16_t region_x, int16_t region_y, int16_t region_ref_y); From 2519439e063c33d590b22373cefd99c8d64fe6fe Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Fri, 27 Dec 2024 07:07:24 -0800 Subject: [PATCH 28/29] Update cleaners.cpp --- plugins/cleaners.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/cleaners.cpp b/plugins/cleaners.cpp index b6bd147296..c0dffe8965 100644 --- a/plugins/cleaners.cpp +++ b/plugins/cleaners.cpp @@ -1,4 +1,4 @@ -// Provides commands for cleaning spatter (and grass) from the map. +// Cleans spatter and/or grass from map tiles. #include "Debug.h" #include "LuaTools.h" From 9f7316725c0e6e42731f88ad7ff77d354dd16a0a Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Fri, 27 Dec 2024 07:10:17 -0800 Subject: [PATCH 29/29] Update cleaners.cpp --- plugins/cleaners.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/cleaners.cpp b/plugins/cleaners.cpp index c0dffe8965..b991bdc0c3 100644 --- a/plugins/cleaners.cpp +++ b/plugins/cleaners.cpp @@ -234,7 +234,7 @@ static bool clean_block(color_ostream &out, df::map_block *block, const cuboid & #undef DEL_BLEV command_result cleanmap(color_ostream &out, const cuboid &bounds, const clean_options &options) -{ // Invoked from clean(), already suspended +{ DEBUG(log, out).print("Cleaning map...\n"); cuboid my_bounds; // Local copy if (bounds.isValid()) @@ -260,7 +260,7 @@ command_result cleanmap(color_ostream &out, const cuboid &bounds, const clean_op } command_result cleanitems(color_ostream &out, const cuboid &bounds) -{ // Invoked from clean(), already suspended +{ DEBUG(log, out).print("Cleaning items...\n"); bool valid_cuboid = bounds.isValid(); // Allow for items outside map if false int cleaned_items = 0, cleaned_total = 0; @@ -304,7 +304,7 @@ command_result cleanitems(color_ostream &out, const cuboid &bounds) } command_result cleanunits(color_ostream &out, const cuboid &bounds) -{ // Invoked from clean(), already suspended +{ DEBUG(log, out).print("Cleaning units...\n"); bool valid_cuboid = bounds.isValid(); // Allow for dead/inactive units if false int cleaned_units = 0, cleaned_total = 0;