From 9ad8d767b4fcf0946444469d2c4072d46997b7e6 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 6 May 2012 19:09:11 +0400 Subject: [PATCH] Add code for adding abstract buildings and removing constructions. --- LUA_API.rst | 17 ++++ Lua API.html | 14 +++ library/LuaApi.cpp | 16 ++++ library/include/modules/Buildings.h | 11 +++ library/include/modules/Constructions.h | 3 + library/lua/dfhack/buildings.lua | 10 +- library/modules/Buildings.cpp | 121 ++++++++++++++++++++++++ library/modules/Constructions.cpp | 40 ++++++++ 8 files changed, 229 insertions(+), 3 deletions(-) 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; +}