Skip to content

Commit

Permalink
Merge pull request #4891 from Bumber64/regrass_cuboid
Browse files Browse the repository at this point in the history
Update regrass plugin; DFHack::cuboid fixes
  • Loading branch information
myk002 authored Sep 1, 2024
2 parents 99cd166 + 9307649 commit ac36a89
Show file tree
Hide file tree
Showing 5 changed files with 295 additions and 364 deletions.
3 changes: 3 additions & 0 deletions docs/changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ Template for new versions:
- `buildingplan`: fixed processing errors when using quick material filter slot '0'
- DFHack screens that allow keyboard cursor and camera movement while active now also allow diagonal and Z-change keyboard cursor keys
- `strangemood`: manually-triggered Macabre moods will now correctly request up to 3 bones/remains for the primary component instead of only 1.
- `regrass`: no longer add all compatible grass types when using ``--force`` without ``--new``
- `regrass`: ``--mud`` now converts muddy slade to grass, consistent with normal DF behavior

## Misc Improvements
- `sort`: can now search for stockpiles on the Places>Stockpile tab by name, number, or enabled item categories
Expand All @@ -76,6 +78,7 @@ Template for new versions:
## API
- ``Units``: new ``isWildlife`` and ``isAgitated`` property checks
- ``Items::createItem``: removed growth_print parameter; now determined automatically
- ``DFHack::cuboid``: ``cuboid::clampMap`` now returns the cuboid itself (instead of boolean) to allow method chaining; call ``cuboid::isValid`` to determine success instead

## Lua
- ``dfhack.units``: ``isWildlife`` and ``isAgitated`` property checks
Expand Down
70 changes: 36 additions & 34 deletions docs/plugins/regrass.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ Usage

Regrasses the entire map by default, restricted to compatible tiles in map
blocks that had grass at some point. Supplying a ``pos`` argument can limit
operation to a single tile. Supplying both can operate on a cuboid. ``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 ``--block`` and ``--zlevel`` options use the ``pos``
values differently.
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 ``--block`` and ``--zlevel`` options use the
``pos`` values differently.

Examples
--------
Expand All @@ -36,26 +36,26 @@ Examples
90, refilling existing and depleted grass.
``regrass 0,0,100 19,19,119 --ashes --mud``
Regrass tiles in the 20 x 20 x 20 cube defined by the coords, refilling
existing and depleted grass, and converting ashes and muddy stone (if
respective blocks ever had grass).
existing and depleted grass, and converting ashes and muddy stone. Fails
in any block that never had grass.
``regrass 10,10,100 -baudnm``
Regrass the block that contains the given coord; converting ashes, muddy
stone, and tiles under buildings; adding all compatible grass types, and
filling each grass type to max.
Regrass the block that contains the given coord. Converts ashes, muddy
stone, and tiles under buildings. Adds all compatible grass types and
fills each grass type to max.
``regrass -f``
Regrass the entire map, refilling existing and depleted grass, else filling
with a randomly selected grass type if non-existent.
with a single randomly selected grass type if non-existent.
``regrass -l``
Print all valid grass raw IDs for use with ``--plant``. Both numerical and
string IDs are provided. This ignores all other options and skips regrass.
``regrass -zf -p 128``
Regrass the current z-level, refilling existing and depleted grass, else
filling with ``underlichen`` if non-existent.
filling with ``underlichen`` (subject to world's raws) if non-existent.
``regrass here -bnf -p "dog's tooth grass"``
Regrass the selected block, adding all compatible grass types to block data,
``dog's tooth grass`` if no compatible types exist. Refill existing grass
on each tile, else select one of the block's types if depleted or
previously non-existent.
and ``dog's tooth grass`` if no compatible types exist for a tile. Refill
existing grass on each tile, else select a compatible (or forced) type if
depleted or previously non-existent.

Options
-------
Expand All @@ -65,38 +65,39 @@ Options
option. The map will not be affected when running with this option.
``-m``, ``--max``
Maxes out every grass type in the tile, giving extra grazing time.
Not normal DF behavior. Tile will appear to be the first type of grass
present in the map block until that is depleted, moving on to the next
type. When this option isn't used, non-depleted grass tiles will have their
existing type refilled, while grass-depleted soils will have a type
selected randomly.
Not normal DF behavior. The tile will appear to be the first type of grass
present in the map block until that is depleted for the tile, moving on to
the next type. Ignores biome compatibility per tile, using every type
present in the block data. When this option *isn't* used, non-depleted
grass tiles will have their existing type refilled, while grass-depleted
soils will have a type selected randomly.
``-n``, ``--new``
Adds 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
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.
``-f``, ``--force``
Force a grass type on tiles with no compatible grass types. The ``--new``
option takes precedence for compatible biomes, otherwise such tiles will be
forced instead. By default, a single random grass type is selected from
the world's raws. The ``--plant`` option allows a specific grass type to be
specified.
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
compatible biomes, otherwise such tiles will be forced instead. By default,
a single random grass type is selected from the world's raws to be the
forced type for the duration of the command. The ``--plant`` option allows
a specific grass type to be specified.
``-p``, ``--plant <grass_id>``
Specify a grass type for the ``--force`` option. ``grass_id`` is not
case-sensitive, but must be enclosed in quotes if spaces exist. A numerical
ID can also be used.
``-a``, ``--ashes``
Regrass tiles that've been burnt to ash. Usually ash must revert to soil
first before grass can grow.
first before grass can regrow.
``-d``, ``--buildings``
Regrass tiles under certain passable building tiles including stockpiles,
planned buildings, workshops, and farms. (Farms will convert grass tiles to
furrowed soil after a short while, but is useful with ``--mud`` option.)
furrowed soil after a short while, but is useful with the ``--mud`` option.)
Doesn't work on "dynamic" buildings such as doors and floor grates.
(Dynamic buildings also include levers and cage traps, for some reason.)
Existing grass tiles will always be refilled, regardless of building tile.
``-u``, ``--mud``
Converts non-smoothed, mud-spattered stone into grass. Valid for layer
stone, obsidian, and ore.
Converts non-smoothed, mud-spattered stone into grass.
``-b``, ``--block``
Only regrass the map block that contains the first ``pos`` argument.
`devel/block-borders` can be used to visualize map blocks.
Expand All @@ -108,9 +109,10 @@ Options
Troubleshooting
---------------

``debugfilter set Debug regrass log`` can be used to figure out why regrass
is failing on a tile. (Avoid regrassing large parts of the map with this
enabled, as it will make the game unresponsive and flood the console for
Use ``debugfilter set Debug regrass log`` (or
``debugfilter set Trace regrass log`` for more detail) to figure out why
regrass is failing on a tile. (Avoid regrassing large parts of the map with
this enabled, as it will make the game unresponsive and flood the console for
several minutes!)

Disable with ``debugfilter set Info regrass log``.
20 changes: 10 additions & 10 deletions library/include/modules/Maps.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,36 +190,36 @@ class cuboid {
int16_t z_min = -1;
int16_t z_max = -1;

// Default constructor
// Default constructor.
DFHACK_EXPORT cuboid() {}

// Construct from two corners
// Construct from two corners.
DFHACK_EXPORT cuboid(int16_t x1, int16_t y1, int16_t z1, int16_t x2, int16_t y2, int16_t z2);
DFHACK_EXPORT cuboid(const df::coord &p1, const df::coord &p2) : cuboid(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z) {}

// Construct as single tile
// Construct as single tile.
DFHACK_EXPORT cuboid(int16_t x, int16_t y, int16_t z);
DFHACK_EXPORT cuboid(const df::coord &p) : cuboid(p.x, p.y, p.z) {}

// Construct from map block
// Construct from map block.
DFHACK_EXPORT cuboid(const df::map_block *block);

// Valid cuboid? True if all max >= min >= 0
// Valid cuboid? True if all max >= min >= 0.
DFHACK_EXPORT bool isValid() const;

// Clear cuboid dimensions, making it invalid.
DFHACK_EXPORT void clear() { x_min = x_max = y_min = y_max = z_min = z_max = -1; }

// Clamp this cuboid within another cuboid. If valid input, invalid result implies no intersection.
// Clamp this cuboid within another cuboid. Invalid result implies no intersection or invalid input.
DFHACK_EXPORT cuboid clamp(const cuboid &other);

// Return a new cuboid representing overlapping volume. If valid input, invalid result implies no intersection.
// Return a new cuboid representing overlapping volume. Invalid result implies no intersection or invalid input.
DFHACK_EXPORT cuboid clampNew(const cuboid &other) const { return cuboid(*this).clamp(other); }

/// Clamp cuboid within map area and ensure max >= min. Fails if map not loaded or any bound < 0.
/// Clamp this cuboid within map area. Invalid result implies no intersection or invalid input (e.g., no map).
/// Can optionally treat cuboid as map blocks instead of tiles.
/// Note: A point being in map area isn't sufficient to know that a tile block is allocated there!
DFHACK_EXPORT bool clampMap(bool block = false);
DFHACK_EXPORT cuboid clampMap(bool block = false);

// Expand cuboid to include point. Returns true if bounds changed. Fails if x/y/z < 0.
DFHACK_EXPORT bool addPos(int16_t x, int16_t y, int16_t z);
Expand All @@ -236,7 +236,7 @@ class cuboid {
/// Iterate over every non-NULL map block intersecting the tile cuboid from top-down, N-S, then W-E.
/// Will also supply the intersection of this cuboid and block to your "fn" for use with cuboid::forCoord.
/// Can optionally attempt to create map blocks if they aren't allocated.
/// "fn" should return true to keep iterating. Won't iterate if map not loaded or any bound < 0.
/// "fn" should return true to keep iterating. Won't iterate if cuboid::clampMap() would fail.
DFHACK_EXPORT void forBlock(std::function<bool(df::map_block *, cuboid)> fn, bool ensure_block = false) const;
};

Expand Down
83 changes: 35 additions & 48 deletions library/modules/Maps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,7 @@ const char * DFHack::sa_feature(df::feature_type index)
/*
* Cuboid class fns
*/
cuboid::cuboid(int16_t x1, int16_t y1, int16_t z1, int16_t x2, int16_t y2, int16_t z2)
{
cuboid::cuboid(int16_t x1, int16_t y1, int16_t z1, int16_t x2, int16_t y2, int16_t z2) {
x_min = min(x1, x2);
x_max = max(x1, x2);
y_min = min(y1, y2);
Expand All @@ -120,17 +119,14 @@ cuboid::cuboid(int16_t x1, int16_t y1, int16_t z1, int16_t x2, int16_t y2, int16
z_max = max(z1, z2);
}

cuboid::cuboid(int16_t x, int16_t y, int16_t z)
{
cuboid::cuboid(int16_t x, int16_t y, int16_t z) {
x_min = x_max = x;
y_min = y_max = y;
z_min = z_max = z;
}

cuboid::cuboid(const df::map_block *block)
{
if (block)
{
cuboid::cuboid(const df::map_block *block) {
if (block) {
auto &pos = block->map_pos;
x_min = pos.x;
x_max = pos.x + 15;
Expand All @@ -140,48 +136,41 @@ cuboid::cuboid(const df::map_block *block)
}
}

bool cuboid::isValid() const
{
bool cuboid::isValid() const {
return x_min >= 0 && y_min >= 0 && z_min >= 0 &&
x_max >= x_min && y_max >= y_min && z_max >= z_min;
}

cuboid cuboid::clamp(const cuboid &other)
{
x_min = max(x_min, other.x_min);
y_min = max(y_min, other.y_min);
z_min = max(z_min, other.z_min);
x_max = min(x_max, other.x_max);
y_max = min(y_max, other.y_max);
z_max = min(z_max, other.z_max);
if (isValid() && other.isValid()) {
x_min = max(x_min, other.x_min);
y_min = max(y_min, other.y_min);
z_min = max(z_min, other.z_min);
x_max = min(x_max, other.x_max);
y_max = min(y_max, other.y_max);
z_max = min(z_max, other.z_max);
}
else
clear();

return *this;
}

bool cuboid::clampMap(bool block)
cuboid cuboid::clampMap(bool block)
{
if (!Maps::IsValid() || x_min < 0 || y_min < 0 || z_min < 0 ||
x_max < 0 || y_max < 0 || z_max < 0)
{
return false;
if (!Maps::IsValid() || !isValid()) {
clear();
return *this;
}

int32_t xy_mult = block ? 1 : 16;
int16_t tile_max = world->map.x_count_block * xy_mult - 1;
x_min = min(x_min, tile_max);
x_max = min(x_max, tile_max);
tile_max = world->map.y_count_block * xy_mult - 1;
y_min = min(y_min, tile_max);
y_max = min(y_max, tile_max);
tile_max = world->map.z_count_block - 1;
z_min = min(z_min, tile_max);
z_max = min(z_max, tile_max);

if (x_min > x_max) std::swap(x_min, x_max);
if (y_min > y_max) std::swap(y_min, y_max);
if (z_min > z_max) std::swap(z_min, z_max);
int32_t x, y, z;
if (block)
Maps::getSize(x, y, z);
else
Maps::getTileSize(x, y, z);

return true;
return clamp(cuboid(0, 0, 0, x-1, y-1, z-1));
}

bool cuboid::addPos(int16_t x, int16_t y, int16_t z)
Expand All @@ -201,42 +190,40 @@ bool cuboid::addPos(int16_t x, int16_t y, int16_t z)
return true;
}

bool cuboid::containsPos(int16_t x, int16_t y, int16_t z) const
{
bool cuboid::containsPos(int16_t x, int16_t y, int16_t z) const {
return x >= x_min && y >= y_min && z >= z_min &&
x <= x_max && y <= y_max && z <= z_max;
}

void cuboid::forCoord(std::function<bool(df::coord)> fn) const
{
if (isValid()) // Only iterate if valid cuboid
if (isValid()) // Only iterate if valid cuboid.
Maps::forCoord(fn, x_min, y_min, z_max, x_max, y_max, z_min);
}

void cuboid::forBlock(std::function<bool(df::map_block *, cuboid)> fn, bool ensure_block) const
{
auto c = *this; // Create a copy to modify
if (!c.clampMap()) // Bound < 0 or no map
auto c = *this; // Create a copy to modify.
if (!c.clampMap().isValid()) // No intersection.
return;

// Process z, y, then x
// Process z, y, then x.
for (int16_t x = (c.x_min >> 4) << 4; x <= c.x_max; x += 16)
for (int16_t y = (c.y_min >> 4) << 4; y <= c.y_max; y += 16)
for (int16_t z = c.z_max; z >= c.z_min; z--)
{
auto *block = ensure_block ? Maps::ensureTileBlock(x, y, z) : Maps::getTileBlock(x, y, z);
if (!block) // Skip unallocated block
if (!block) // Skip unallocated block.
continue;
else if (!fn(block, cuboid(block).clamp(c)))
return; // Break iterator
return; // Break iterator.
}
}

/*
* The Maps module
*/
bool Maps::IsValid ()
{
bool Maps::IsValid() {
return (world->map.block_index != NULL);
}

Expand All @@ -247,12 +234,12 @@ void Maps::forCoord(std::function<bool(df::coord)> fn, int16_t x1, int16_t y1, i
int16_t dy = y1 > y2 ? -1 : 1;
int16_t dz = z1 > z2 ? -1 : 1;

// Process z, y, then x
// Process z, y, then x.
for (int16_t x = x1; x != x2 + dx; x += dx)
for (int16_t y = y1; y != y2 + dy; y += dy)
for (int16_t z = z1; z != z2 + dz; z += dz)
if (!fn(df::coord(x, y, z)))
return; // Break iterator
return; // Break iterator.
}

// getter for map size in blocks
Expand Down
Loading

0 comments on commit ac36a89

Please sign in to comment.