diff --git a/LUA_API.rst b/LUA_API.rst
index 673053189..d5af5e7a4 100644
--- a/LUA_API.rst
+++ b/LUA_API.rst
@@ -695,6 +695,10 @@ Units module
The unit is capable of rational action, i.e. not dead, insane or zombie.
+* ``dfhack.units.clearBurrowMembers(burrow)``
+
+ Removes all units from the burrow.
+
* ``dfhack.units.isInBurrow(unit,burrow)``
Checks if the unit is in the burrow.
@@ -776,6 +780,10 @@ Maps module
Returns a table of map block pointers.
+* ``dfhack.maps.clearBurrowTiles(burrow)``
+
+ Removes all tiles from the burrow.
+
* ``dfhack.maps.isBurrowTile(burrow,tile_coord)``
Checks if the tile is in burrow.
diff --git a/Lua API.html b/Lua API.html
index 32a3b7f04..66385840b 100644
--- a/Lua API.html
+++ b/Lua API.html
@@ -924,6 +924,9 @@ a lua list containing them.
dfhack.units.isSane(unit)
The unit is capable of rational action, i.e. not dead, insane or zombie.
+dfhack.units.clearBurrowMembers(burrow)
+Removes all units from the burrow.
+
dfhack.units.isInBurrow(unit,burrow)
Checks if the unit is in the burrow.
@@ -989,6 +992,9 @@ Returns false in case of error.
dfhack.maps.listBurrowBlocks(burrow)
Returns a table of map block pointers.
+dfhack.maps.clearBurrowTiles(burrow)
+Removes all tiles from the burrow.
+
dfhack.maps.isBurrowTile(burrow,tile_coord)
Checks if the tile is in burrow.
diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp
index 6792c921e..917e44d38 100644
--- a/library/LuaApi.cpp
+++ b/library/LuaApi.cpp
@@ -634,6 +634,7 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = {
WRAPM(Units, isDead),
WRAPM(Units, isAlive),
WRAPM(Units, isSane),
+ WRAPM(Units, clearBurrowMembers),
WRAPM(Units, isInBurrow),
WRAPM(Units, setInBurrow),
{ NULL, NULL }
@@ -707,6 +708,7 @@ static const LuaWrapper::FunctionReg dfhack_maps_module[] = {
WRAPM(Maps, getGlobalInitFeature),
WRAPM(Maps, getLocalInitFeature),
WRAPM(Maps, findBurrowByName),
+ WRAPM(Maps, clearBurrowTiles),
WRAPN(isBlockBurrowTile, maps_isBlockBurrowTile),
WRAPN(setBlockBurrowTile, maps_setBlockBurrowTile),
WRAPM(Maps, isBurrowTile),
diff --git a/library/include/df/custom/block_burrow.methods.inc b/library/include/df/custom/block_burrow.methods.inc
index 7754ab0db..216a58662 100644
--- a/library/include/df/custom/block_burrow.methods.inc
+++ b/library/include/df/custom/block_burrow.methods.inc
@@ -16,4 +16,11 @@ inline void setassignment( int x, int y, bool bit )
tile_bitmask[y] |= (1 << x);
else
tile_bitmask[y] &= ~(1 << x);
-}
\ No newline at end of file
+}
+bool has_assignments()
+{
+ for (int i = 0; i < 16; i++)
+ if (tile_bitmask[i])
+ return true;
+ return false;
+}
diff --git a/library/include/df/custom/block_square_event_mineralst.methods.inc b/library/include/df/custom/block_square_event_mineralst.methods.inc
index 7754ab0db..216a58662 100644
--- a/library/include/df/custom/block_square_event_mineralst.methods.inc
+++ b/library/include/df/custom/block_square_event_mineralst.methods.inc
@@ -16,4 +16,11 @@ inline void setassignment( int x, int y, bool bit )
tile_bitmask[y] |= (1 << x);
else
tile_bitmask[y] &= ~(1 << x);
-}
\ No newline at end of file
+}
+bool has_assignments()
+{
+ for (int i = 0; i < 16; i++)
+ if (tile_bitmask[i])
+ return true;
+ return false;
+}
diff --git a/library/include/modules/Maps.h b/library/include/modules/Maps.h
index 3cd1187a5..b7341f87c 100644
--- a/library/include/modules/Maps.h
+++ b/library/include/modules/Maps.h
@@ -263,8 +263,16 @@ extern DFHACK_EXPORT bool RemoveBlockEvent(uint32_t x, uint32_t y, uint32_t z, d
DFHACK_EXPORT df::burrow *findBurrowByName(std::string name);
DFHACK_EXPORT void listBurrowBlocks(std::vector *pvec, df::burrow *burrow);
+DFHACK_EXPORT void clearBurrowTiles(df::burrow *burrow);
DFHACK_EXPORT df::block_burrow *getBlockBurrowMask(df::burrow *burrow, df::map_block *block, bool create = false);
+DFHACK_EXPORT bool deleteBlockBurrowMask(df::burrow *burrow, df::map_block *block, df::block_burrow *mask);
+
+inline bool deleteBlockBurrowMask(df::burrow *burrow, df::map_block *block)
+{
+ return deleteBlockBurrowMask(burrow, block, getBlockBurrowMask(burrow, block));
+}
+
DFHACK_EXPORT bool isBlockBurrowTile(df::burrow *burrow, df::map_block *block, df::coord2d tile);
DFHACK_EXPORT bool setBlockBurrowTile(df::burrow *burrow, df::map_block *block, df::coord2d tile, bool enable);
diff --git a/library/include/modules/Units.h b/library/include/modules/Units.h
index d7dadb21b..e093ed1ef 100644
--- a/library/include/modules/Units.h
+++ b/library/include/modules/Units.h
@@ -206,6 +206,10 @@ DFHACK_EXPORT df::nemesis_record *getNemesis(df::unit *unit);
DFHACK_EXPORT bool isDead(df::unit *unit);
DFHACK_EXPORT bool isAlive(df::unit *unit);
DFHACK_EXPORT bool isSane(df::unit *unit);
+DFHACK_EXPORT bool isCitizen(df::unit *unit);
+DFHACK_EXPORT bool isDwarf(df::unit *unit);
+
+DFHACK_EXPORT void clearBurrowMembers(df::burrow *burrow);
DFHACK_EXPORT bool isInBurrow(df::unit *unit, df::burrow *burrow);
DFHACK_EXPORT void setInBurrow(df::unit *unit, df::burrow *burrow, bool enable);
diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp
index fce80d775..327e26986 100644
--- a/library/modules/Maps.cpp
+++ b/library/modules/Maps.cpp
@@ -715,6 +715,42 @@ void Maps::listBurrowBlocks(std::vector *pvec, df::burrow *burro
}
}
+static void destroyBurrowMask(df::block_burrow *mask)
+{
+ if (!mask) return;
+
+ auto link = mask->link;
+
+ link->prev->next = link->next;
+ if (link->next)
+ link->next->prev = link->prev;
+ delete link;
+
+ delete mask;
+}
+
+void Maps::clearBurrowTiles(df::burrow *burrow)
+{
+ CHECK_NULL_POINTER(burrow);
+
+ df::coord base(world->map.region_x*3,world->map.region_y*3,world->map.region_z);
+
+ for (size_t i = 0; i < burrow->block_x.size(); i++)
+ {
+ df::coord pos(burrow->block_x[i], burrow->block_y[i], burrow->block_z[i]);
+
+ auto block = getBlock(pos - base);
+ if (!block)
+ continue;
+
+ destroyBurrowMask(getBlockBurrowMask(burrow, block));
+ }
+
+ burrow->block_x.clear();
+ burrow->block_y.clear();
+ burrow->block_z.clear();
+}
+
df::block_burrow *Maps::getBlockBurrowMask(df::burrow *burrow, df::map_block *block, bool create)
{
CHECK_NULL_POINTER(burrow);
@@ -754,6 +790,36 @@ df::block_burrow *Maps::getBlockBurrowMask(df::burrow *burrow, df::map_block *bl
return NULL;
}
+bool Maps::deleteBlockBurrowMask(df::burrow *burrow, df::map_block *block, df::block_burrow *mask)
+{
+ CHECK_NULL_POINTER(burrow);
+ CHECK_NULL_POINTER(block);
+
+ if (!mask)
+ return false;
+
+ df::coord base(world->map.region_x*3,world->map.region_y*3,world->map.region_z);
+ df::coord pos = base + block->map_pos/16;
+
+ destroyBurrowMask(mask);
+
+ for (size_t i = 0; i < burrow->block_x.size(); i++)
+ {
+ df::coord cur(burrow->block_x[i], burrow->block_y[i], burrow->block_z[i]);
+
+ if (cur == pos)
+ {
+ vector_erase_at(burrow->block_x, i);
+ vector_erase_at(burrow->block_y, i);
+ vector_erase_at(burrow->block_z, i);
+
+ break;
+ }
+ }
+
+ return true;
+}
+
bool Maps::isBlockBurrowTile(df::burrow *burrow, df::map_block *block, df::coord2d tile)
{
CHECK_NULL_POINTER(burrow);
@@ -774,8 +840,13 @@ bool Maps::setBlockBurrowTile(df::burrow *burrow, df::map_block *block, df::coor
auto mask = getBlockBurrowMask(burrow, block, enable);
if (mask)
+ {
mask->setassignment(tile & 15, enable);
+ if (!enable && !mask->has_assignments())
+ deleteBlockBurrowMask(burrow, block, mask);
+ }
+
return true;
}
diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp
index 7dbb40c97..8b44d4cf9 100644
--- a/library/modules/Units.cpp
+++ b/library/modules/Units.cpp
@@ -653,6 +653,51 @@ bool DFHack::Units::isSane(df::unit *unit)
return true;
}
+bool DFHack::Units::isCitizen(df::unit *unit)
+{
+ CHECK_NULL_POINTER(unit);
+
+ return unit->civ_id == ui->civ_id &&
+ !unit->flags1.bits.merchant &&
+ !unit->flags1.bits.diplomat &&
+ !unit->flags2.bits.resident &&
+ !unit->flags1.bits.dead &&
+ !unit->flags3.bits.ghostly;
+}
+
+bool DFHack::Units::isDwarf(df::unit *unit)
+{
+ CHECK_NULL_POINTER(unit);
+
+ return unit->race == ui->race_id;
+}
+
+void DFHack::Units::clearBurrowMembers(df::burrow *burrow)
+{
+ CHECK_NULL_POINTER(burrow);
+
+ for (size_t i = 0; i < burrow->units.size(); i++)
+ {
+ auto unit = df::unit::find(burrow->units[i]);
+
+ if (unit)
+ erase_from_vector(unit->burrows, burrow->id);
+ }
+
+ burrow->units.clear();
+
+ // Sync ui if active
+ if (ui && ui->main.mode == ui_sidebar_mode::Burrows &&
+ ui->burrows.in_add_units_mode && ui->burrows.sel_id == burrow->id)
+ {
+ auto &sel = ui->burrows.sel_units;
+
+ for (size_t i = 0; i < sel.size(); i++)
+ sel[i] = false;
+ }
+}
+
+
bool DFHack::Units::isInBurrow(df::unit *unit, df::burrow *burrow)
{
CHECK_NULL_POINTER(unit);
diff --git a/plugins/burrows.cpp b/plugins/burrows.cpp
index a559573ac..c973405ee 100644
--- a/plugins/burrows.cpp
+++ b/plugins/burrows.cpp
@@ -8,6 +8,7 @@
#include "modules/Maps.h"
#include "modules/MapCache.h"
#include "modules/World.h"
+#include "modules/Units.h"
#include "TileTypes.h"
#include "DataDefs.h"
@@ -16,6 +17,7 @@
#include "df/unit.h"
#include "df/burrow.h"
#include "df/map_block.h"
+#include "df/block_burrow.h"
#include "df/job.h"
#include "df/job_list_link.h"
@@ -52,11 +54,28 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector grow_burrows;
DFhackCExport command_result plugin_onupdate(color_ostream &out)
{
- if (!active || !auto_grow)
+ if (!active)
return CR_OK;
detect_burrow_renames(out);
- detect_digging(out);
+
+ if (auto_grow)
+ detect_digging(out);
+
return CR_OK;
}
@@ -213,24 +235,39 @@ DFhackCExport command_result plugin_onupdate(color_ostream &out)
* Config and processing
*/
+static std::map name_lookup;
+
static void parse_names()
{
auto &list = ui->burrows.list;
grow_burrows.clear();
+ name_lookup.clear();
for (size_t i = 0; i < list.size(); i++)
{
auto burrow = list[i];
- if (!burrow->name.empty() && burrow->name[burrow->name.size()-1] == '+')
- grow_burrows.push_back(burrow->id);
+ std::string name = burrow->name;
+
+ if (!name.empty())
+ {
+ name_lookup[name] = burrow->id;
+
+ if (name[name.size()-1] == '+')
+ {
+ grow_burrows.push_back(burrow->id);
+ name.resize(name.size()-1);
+ }
+
+ if (!name.empty())
+ name_lookup[name] = burrow->id;
+ }
}
}
static void reset_tracking()
{
- name_burrow_id = -1;
diggers.clear();
next_job_id_save = 0;
}
@@ -244,6 +281,8 @@ static void init_map(color_ostream &out)
}
parse_names();
+ name_burrow_id = -1;
+
reset_tracking();
active = true;
@@ -286,10 +325,7 @@ static void enable_auto_grow(color_ostream &out, bool enable)
auto_grow = enable;
if (enable)
- {
- parse_names();
reset_tracking();
- }
}
static void handle_burrow_rename(color_ostream &out, df::burrow *burrow)
@@ -371,6 +407,137 @@ static void handle_dig_complete(color_ostream &out, df::job_type job, df::coord
}
}
+static df::burrow *findByName(color_ostream &out, std::string name, bool silent = false)
+{
+ int id = -1;
+ if (name_lookup.count(name))
+ id = name_lookup[name];
+ auto rv = df::burrow::find(id);
+ if (!rv && !silent)
+ out.printerr("Burrow not found: '%s'\n", name.c_str());
+ return rv;
+}
+
+static void copyUnits(df::burrow *target, df::burrow *source, bool enable)
+{
+ if (source == target)
+ {
+ if (!enable)
+ Units::clearBurrowMembers(target);
+
+ return;
+ }
+
+ for (size_t i = 0; i < source->units.size(); i++)
+ {
+ auto unit = df::unit::find(source->units[i]);
+
+ if (unit)
+ Units::setInBurrow(unit, target, enable);
+ }
+}
+
+static void copyTiles(df::burrow *target, df::burrow *source, bool enable)
+{
+ if (source == target)
+ {
+ if (!enable)
+ Maps::clearBurrowTiles(target);
+
+ return;
+ }
+
+ std::vector pvec;
+ Maps::listBurrowBlocks(&pvec, source);
+
+ for (size_t i = 0; i < pvec.size(); i++)
+ {
+ auto block = pvec[i];
+ auto smask = Maps::getBlockBurrowMask(source, block);
+ if (!smask)
+ continue;
+
+ auto tmask = Maps::getBlockBurrowMask(target, block, enable);
+ if (!tmask)
+ continue;
+
+ if (enable)
+ {
+ for (int j = 0; j < 16; j++)
+ tmask->tile_bitmask[j] |= smask->tile_bitmask[j];
+ }
+ else
+ {
+ for (int j = 0; j < 16; j++)
+ tmask->tile_bitmask[j] &= ~smask->tile_bitmask[j];
+
+ if (!tmask->has_assignments())
+ Maps::deleteBlockBurrowMask(target, block, tmask);
+ }
+ }
+}
+
+static void setTilesByDesignation(df::burrow *target, df::tile_designation d_mask,
+ df::tile_designation d_value, bool enable)
+{
+ auto &blocks = world->map.map_blocks;
+
+ for (size_t i = 0; i < blocks.size(); i++)
+ {
+ auto block = blocks[i];
+ df::block_burrow *mask = NULL;
+
+ for (int x = 0; x < 16; x++)
+ {
+ for (int y = 0; y < 16; y++)
+ {
+ if ((block->designation[x][y].whole & d_mask.whole) != d_value.whole)
+ continue;
+
+ if (!mask)
+ mask = Maps::getBlockBurrowMask(target, block, enable);
+ if (!mask)
+ goto next_block;
+
+ mask->setassignment(x, y, enable);
+ }
+ }
+
+ if (mask && !enable && !mask->has_assignments())
+ Maps::deleteBlockBurrowMask(target, block, mask);
+
+ next_block:;
+ }
+}
+
+static bool setTilesByKeyword(df::burrow *target, std::string name, bool enable)
+{
+ df::tile_designation mask(0);
+ df::tile_designation value(0);
+
+ if (name == "ABOVE_GROUND")
+ mask.bits.subterranean = true;
+ else if (name == "SUBTERRANEAN")
+ mask.bits.subterranean = value.bits.subterranean = true;
+ else if (name == "LIGHT")
+ mask.bits.light = value.bits.light = true;
+ else if (name == "DARK")
+ mask.bits.light = true;
+ else if (name == "OUTSIDE")
+ mask.bits.outside = value.bits.outside = true;
+ else if (name == "INSIDE")
+ mask.bits.outside = true;
+ else if (name == "HIDDEN")
+ mask.bits.hidden = value.bits.hidden = true;
+ else if (name == "REVEALED")
+ mask.bits.hidden = true;
+ else
+ return false;
+
+ setTilesByDesignation(target, mask, value, enable);
+ return true;
+}
+
static command_result burrow(color_ostream &out, vector ¶meters)
{
CoreSuspender suspend;
@@ -402,6 +569,83 @@ static command_result burrow(color_ostream &out, vector ¶meters)
return CR_WRONG_USAGE;
}
}
+ else if (cmd == "clear-units")
+ {
+ if (parameters.size() < 2)
+ return CR_WRONG_USAGE;
+
+ for (int i = 1; i < parameters.size(); i++)
+ {
+ auto target = findByName(out, parameters[i]);
+ if (!target)
+ return CR_WRONG_USAGE;
+
+ Units::clearBurrowMembers(target);
+ }
+ }
+ else if (cmd == "set-units" || cmd == "add-units" || cmd == "remove-units")
+ {
+ if (parameters.size() < 3)
+ return CR_WRONG_USAGE;
+
+ auto target = findByName(out, parameters[1]);
+ if (!target)
+ return CR_WRONG_USAGE;
+
+ if (cmd == "set-units")
+ Units::clearBurrowMembers(target);
+
+ bool enable = (cmd != "remove-units");
+
+ for (int i = 2; i < parameters.size(); i++)
+ {
+ auto source = findByName(out, parameters[i]);
+ if (!source)
+ return CR_WRONG_USAGE;
+
+ copyUnits(target, source, enable);
+ }
+ }
+ else if (cmd == "clear-tiles")
+ {
+ if (parameters.size() < 2)
+ return CR_WRONG_USAGE;
+
+ for (int i = 1; i < parameters.size(); i++)
+ {
+ auto target = findByName(out, parameters[i]);
+ if (!target)
+ return CR_WRONG_USAGE;
+
+ Maps::clearBurrowTiles(target);
+ }
+ }
+ else if (cmd == "set-tiles" || cmd == "add-tiles" || cmd == "remove-tiles")
+ {
+ if (parameters.size() < 3)
+ return CR_WRONG_USAGE;
+
+ auto target = findByName(out, parameters[1]);
+ if (!target)
+ return CR_WRONG_USAGE;
+
+ if (cmd == "set-tiles")
+ Maps::clearBurrowTiles(target);
+
+ bool enable = (cmd != "remove-tiles");
+
+ for (int i = 2; i < parameters.size(); i++)
+ {
+ if (setTilesByKeyword(target, parameters[i], enable))
+ continue;
+
+ auto source = findByName(out, parameters[i]);
+ if (!source)
+ return CR_WRONG_USAGE;
+
+ copyTiles(target, source, enable);
+ }
+ }
else
{
if (!parameters.empty() && cmd != "?")