diff --git a/LUA_API.rst b/LUA_API.rst
index 681caef54..e20b946d8 100644
--- a/LUA_API.rst
+++ b/LUA_API.rst
@@ -926,6 +926,12 @@ Low-level building creation functions;
final values used by the building; true_area is less than rect_area
if any tiles were removed from designation.
+* ``dfhack.buildings.constructAbstract(building)``
+
+ Links a fully configured object created by ``allocInstance`` into the
+ world. The object must be an abstract building, i.e. a stockpile or civzone.
+ Returns *true*, or *false* if impossible.
+
* ``dfhack.buildings.constructWithItems(building, items)``
Links a fully configured object created by ``allocInstance`` into the
@@ -941,6 +947,11 @@ Low-level building creation functions;
Use a negative ``quantity`` field value to auto-compute the amount
from the size of the building.
+* ``dfhack.buildings.deconstruct(building)``
+
+ Destroys the building, or queues a deconstruction job.
+ Returns *true* if the building was destroyed and deallocated immediately.
+
More high-level functions are implemented in lua and can be loaded by
``require('dfhack.buildings')``. See ``hack/lua/dfhack/buildings.lua``.
@@ -954,6 +965,12 @@ Constructions module
Returns *true*, or *false* if obstructed.
Note that designated constructions are technically buildings.
+* ``dfhack.constructions.designateRemove(pos)``, or ``designateRemove(x,y,z)``
+
+ If there is a construction or a planned construction at the specified
+ coordinates, designates it for removal, or instantly cancels the planned one.
+ Returns *true, was_only_planned* if removed; or *false* if none found.
+
Core interpreter context
========================
diff --git a/Lua API.html b/Lua API.html
index 094b522c3..7aa8e651a 100644
--- a/Lua API.html
+++ b/Lua API.html
@@ -1126,6 +1126,11 @@ height, rect_area, true_area. Returned width and height are the
final values used by the building; true_area is less than rect_area
if any tiles were removed from designation.
+dfhack.buildings.constructAbstract(building)
+Links a fully configured object created by allocInstance into the
+world. The object must be an abstract building, i.e. a stockpile or civzone.
+Returns true, or false if impossible.
+
dfhack.buildings.constructWithItems(building, items)
Links a fully configured object created by allocInstance into the
world for construction, using a list of specific items as material.
@@ -1139,6 +1144,10 @@ and possibly destroyed in any case.
Use a negative quantity field value to auto-compute the amount
from the size of the building.
+dfhack.buildings.deconstruct(building)
+Destroys the building, or queues a deconstruction job.
+Returns true if the building was destroyed and deallocated immediately.
+
More high-level functions are implemented in lua and can be loaded by
require('dfhack.buildings'). See hack/lua/dfhack/buildings.lua.
@@ -1152,6 +1161,11 @@ a planned but not completed construction there, changes its type.
Returns true, or false if obstructed.
Note that designated constructions are technically buildings.
+dfhack.constructions.designateRemove(pos), or designateRemove(x,y,z)
+If there is a construction or a planned construction at the specified
+coordinates, designates it for removal, or instantly cancels the planned one.
+Returns true, was_only_planned if removed; or false if none found.
+
diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp
index bf2ec9364..d554754e4 100644
--- a/library/LuaApi.cpp
+++ b/library/LuaApi.cpp
@@ -821,8 +821,10 @@ static const LuaWrapper::FunctionReg dfhack_buildings_module[] = {
WRAPM(Buildings, countExtentTiles),
WRAPN(containsTile, buildings_containsTile),
WRAPM(Buildings, hasSupport),
+ WRAPM(Buildings, constructAbstract),
WRAPM(Buildings, constructWithItems),
WRAPM(Buildings, constructWithFilters),
+ WRAPM(Buildings, deconstruct),
{ NULL, NULL }
};
@@ -900,6 +902,20 @@ static const LuaWrapper::FunctionReg dfhack_constructions_module[] = {
{ NULL, NULL }
};
+static int constructions_designateRemove(lua_State *L)
+{
+ auto pos = CheckCoordXYZ(L, 1, true);
+ bool imm = false;
+ lua_pushboolean(L, Constructions::designateRemove(pos, &imm));
+ lua_pushboolean(L, imm);
+ return 2;
+}
+
+static const luaL_Reg dfhack_constructions_funcs[] = {
+ { "designateRemove", constructions_designateRemove },
+ { NULL, NULL }
+};
+
/************************
* Main Open function *
diff --git a/library/include/modules/Buildings.h b/library/include/modules/Buildings.h
index 9bb3ba001..6e0a22052 100644
--- a/library/include/modules/Buildings.h
+++ b/library/include/modules/Buildings.h
@@ -149,6 +149,11 @@ DFHACK_EXPORT bool setSize(df::building *bld, df::coord2d size, int direction =
*/
DFHACK_EXPORT std::pair getSize(df::building *bld);
+/**
+ * Constructs an abstract building, i.e. stockpile or civzone.
+ */
+DFHACK_EXPORT bool constructAbstract(df::building *bld);
+
/**
* Initiates construction of the building, using specified items as inputs.
* Returns true if success.
@@ -162,5 +167,11 @@ DFHACK_EXPORT bool constructWithItems(df::building *bld, std::vector
*/
DFHACK_EXPORT bool constructWithFilters(df::building *bld, std::vector items);
+/**
+ * Deconstructs or queues deconstruction of a building.
+ * Returns true if the building has been destroyed instantly.
+ */
+DFHACK_EXPORT bool deconstruct(df::building *bld);
+
}
}
diff --git a/library/include/modules/Constructions.h b/library/include/modules/Constructions.h
index 17da12711..f8a3e5c61 100644
--- a/library/include/modules/Constructions.h
+++ b/library/include/modules/Constructions.h
@@ -62,6 +62,9 @@ DFHACK_EXPORT df::construction * getConstruction (const int32_t index);
DFHACK_EXPORT bool designateNew(df::coord pos, df::construction_type type,
df::item_type item = df::item_type::NONE, int mat_index = -1);
+
+DFHACK_EXPORT bool designateRemove(df::coord pos, bool *immediate = NULL);
+
}
}
#endif
diff --git a/library/lua/dfhack/buildings.lua b/library/lua/dfhack/buildings.lua
index 57427e9fb..9650dc4cd 100644
--- a/library/lua/dfhack/buildings.lua
+++ b/library/lua/dfhack/buildings.lua
@@ -28,6 +28,8 @@ local buildings = dfhack.buildings
items = { item, item ... },
-- OR
filter = { { ... }, { ... }... }
+ -- OR
+ abstract = true
}
Returns: the created building, or 'nil, error'
@@ -41,8 +43,8 @@ function buildings.constructBuilding(info)
if not (info.pos or info.x) then
error('position is required')
end
- if not (info.items or info.filters) then
- error('either items or filters are required')
+ if not (info.abstract or info.items or info.filters) then
+ error('one of items, filters or abstract are required')
elseif info.filters then
for _,v in ipairs(info.filters) do
v.new = true
@@ -77,7 +79,9 @@ function buildings.constructBuilding(info)
if info.full_rectangle and area ~= r_area then
return nil, "not all tiles can be used"
end
- if info.items then
+ if info.abstract then
+ ok = buildings.constructAbstract(instance)
+ elseif info.items then
ok = buildings.constructWithItems(instance, info.items)
else
ok = buildings.constructWithFilters(instance, info.filters)
diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp
index 2c5162673..557eb1881 100644
--- a/library/modules/Buildings.cpp
+++ b/library/modules/Buildings.cpp
@@ -46,6 +46,7 @@ using namespace DFHack;
#include "DataDefs.h"
#include "df/world.h"
#include "df/ui.h"
+#include "df/ui_look_list.h"
#include "df/d_init.h"
#include "df/item.h"
#include "df/job.h"
@@ -59,6 +60,7 @@ using namespace DFHack;
#include "df/building_bridgest.h"
#include "df/building_coffinst.h"
#include "df/building_civzonest.h"
+#include "df/building_stockpilest.h"
#include "df/building_furnacest.h"
#include "df/building_workshopst.h"
#include "df/building_screw_pumpst.h"
@@ -683,6 +685,18 @@ static void linkRooms(df::building *bld)
df::global::ui->equipment.update.bits.buildings = true;
}
+static void unlinkRooms(df::building *bld)
+{
+ for (size_t i = 0; i < bld->parents.size(); i++)
+ {
+ auto parent = bld->parents[i];
+ int idx = linear_index(parent->children, bld);
+ vector_erase_at(parent->children, idx);
+ }
+
+ bld->parents.clear();
+}
+
static void linkBuilding(df::building *bld)
{
bld->id = (*building_next_id)++;
@@ -715,6 +729,52 @@ static void createDesign(df::building *bld, bool rough)
}
}
+static int getMaxStockpileId()
+{
+ auto &vec = world->buildings.other[buildings_other_id::STOCKPILE];
+ int max_id = 0;
+
+ for (size_t i = 0; i < vec.size(); i++)
+ {
+ auto bld = strict_virtual_cast(vec[i]);
+ if (bld)
+ max_id = std::max(max_id, bld->stockpile_number);
+ }
+
+ return max_id;
+}
+
+bool Buildings::constructAbstract(df::building *bld)
+{
+ CHECK_NULL_POINTER(bld);
+ CHECK_INVALID_ARGUMENT(bld->id == -1);
+ CHECK_INVALID_ARGUMENT(!bld->isActual());
+
+ if (!checkBuildingTiles(bld, false))
+ return false;
+
+ switch (bld->getType())
+ {
+ case building_type::Stockpile:
+ if (auto stock = strict_virtual_cast(bld))
+ stock->stockpile_number = getMaxStockpileId() + 1;
+ break;
+
+ default:
+ break;
+ }
+
+ linkBuilding(bld);
+
+ if (!bld->flags.bits.exists)
+ {
+ bld->flags.bits.exists = true;
+ bld->initFarmSeasons();
+ }
+
+ return true;
+}
+
static bool linkForConstruct(df::job* &job, df::building *bld)
{
if (!checkBuildingTiles(bld, false))
@@ -836,3 +896,64 @@ bool Buildings::constructWithFilters(df::building *bld, std::vectorisActual() && bld->getBuildStage() > 0)
+ {
+ bld->queueDestroy();
+ return false;
+ }
+
+ /* Immediate destruction code path.
+ Should only happen for abstract and unconstructed buildings.*/
+
+ if (bld->isSettingOccupancy())
+ {
+ markBuildingTiles(bld, true);
+ bld->cleanupMap();
+ }
+
+ bld->removeUses(false, false);
+ // Assume: no parties.
+ unlinkRooms(bld);
+ // Assume: not unit destroy target
+ vector_erase_at(ui->unk8.unk10, linear_index(ui->unk8.unk10, bld));
+ // Assume: not used in punishment
+ // Assume: not used in non-own jobs
+ // Assume: does not affect pathfinding
+ bld->deconstructItems(false, false);
+ // Don't clear arrows.
+
+ bld->uncategorize();
+ delete bld;
+
+ if (world->selected_building == bld)
+ {
+ world->selected_building = NULL;
+ world->update_selected_building = true;
+ }
+
+ for (int i = ui_look_list->items.size()-1; i >= 0; i--)
+ {
+ auto item = ui_look_list->items[i];
+ if (item->type == df::ui_look_list::T_items::Building &&
+ item->building == bld)
+ {
+ vector_erase_at(ui_look_list->items, i);
+ delete item;
+ }
+ }
+
+ if (process_dig) *process_dig = true;
+ if (process_jobs) *process_jobs = true;
+
+ return true;
+}
diff --git a/library/modules/Constructions.cpp b/library/modules/Constructions.cpp
index a38048391..2d61c447a 100644
--- a/library/modules/Constructions.cpp
+++ b/library/modules/Constructions.cpp
@@ -129,3 +129,43 @@ bool Constructions::designateNew(df::coord pos, df::construction_type type,
return true;
}
+bool Constructions::designateRemove(df::coord pos, bool *immediate)
+{
+ using df::global::process_dig;
+
+ if (immediate)
+ *immediate = false;
+
+ if (auto current = Buildings::findAtTile(pos))
+ {
+ auto cons = strict_virtual_cast(current);
+ if (!cons)
+ return false;
+
+ if (Buildings::deconstruct(cons))
+ {
+ if (immediate)
+ *immediate = true;
+ }
+
+ return true;
+ }
+
+ auto block = Maps::getTileBlock(pos);
+ if (!block)
+ return false;
+
+ auto ttype = block->tiletype[pos.x&15][pos.y&15];
+
+ if (tileMaterial(ttype) == tiletype_material::CONSTRUCTION)
+ {
+ auto &dsgn = block->designation[pos.x&15][pos.y&15];
+ dsgn.bits.dig = tile_dig_designation::Default;
+ block->flags.bits.designated = true;
+ if (process_dig)
+ *process_dig = true;
+ return true;
+ }
+
+ return false;
+}