Add code for adding abstract buildings and removing constructions.

develop
Alexander Gavrilov 2012-05-06 19:09:11 +04:00
parent b70130cf36
commit 9ad8d767b4
8 changed files with 229 additions and 3 deletions

@ -926,6 +926,12 @@ Low-level building creation functions;
final values used by the building; true_area is less than rect_area final values used by the building; true_area is less than rect_area
if any tiles were removed from designation. 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)`` * ``dfhack.buildings.constructWithItems(building, items)``
Links a fully configured object created by ``allocInstance`` into the 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 Use a negative ``quantity`` field value to auto-compute the amount
from the size of the building. 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 More high-level functions are implemented in lua and can be loaded by
``require('dfhack.buildings')``. See ``hack/lua/dfhack/buildings.lua``. ``require('dfhack.buildings')``. See ``hack/lua/dfhack/buildings.lua``.
@ -954,6 +965,12 @@ Constructions module
Returns *true*, or *false* if obstructed. Returns *true*, or *false* if obstructed.
Note that designated constructions are technically buildings. 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 Core interpreter context
======================== ========================

@ -1126,6 +1126,11 @@ height, rect_area, true_area</em>. Returned width and height are the
final values used by the building; true_area is less than rect_area final values used by the building; true_area is less than rect_area
if any tiles were removed from designation.</p> if any tiles were removed from designation.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.constructAbstract(building)</tt></p>
<p>Links a fully configured object created by <tt class="docutils literal">allocInstance</tt> into the
world. The object must be an abstract building, i.e. a stockpile or civzone.
Returns <em>true</em>, or <em>false</em> if impossible.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.constructWithItems(building, items)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.buildings.constructWithItems(building, items)</tt></p>
<p>Links a fully configured object created by <tt class="docutils literal">allocInstance</tt> into the <p>Links a fully configured object created by <tt class="docutils literal">allocInstance</tt> into the
world for construction, using a list of specific items as material. world for construction, using a list of specific items as material.
@ -1139,6 +1144,10 @@ and possibly destroyed in any case.
Use a negative <tt class="docutils literal">quantity</tt> field value to auto-compute the amount Use a negative <tt class="docutils literal">quantity</tt> field value to auto-compute the amount
from the size of the building.</p> from the size of the building.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.deconstruct(building)</tt></p>
<p>Destroys the building, or queues a deconstruction job.
Returns <em>true</em> if the building was destroyed and deallocated immediately.</p>
</li>
</ul> </ul>
<p>More high-level functions are implemented in lua and can be loaded by <p>More high-level functions are implemented in lua and can be loaded by
<tt class="docutils literal"><span class="pre">require('dfhack.buildings')</span></tt>. See <tt class="docutils literal">hack/lua/dfhack/buildings.lua</tt>.</p> <tt class="docutils literal"><span class="pre">require('dfhack.buildings')</span></tt>. See <tt class="docutils literal">hack/lua/dfhack/buildings.lua</tt>.</p>
@ -1152,6 +1161,11 @@ a planned but not completed construction there, changes its type.
Returns <em>true</em>, or <em>false</em> if obstructed. Returns <em>true</em>, or <em>false</em> if obstructed.
Note that designated constructions are technically buildings.</p> Note that designated constructions are technically buildings.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">dfhack.constructions.designateRemove(pos)</tt>, or <tt class="docutils literal">designateRemove(x,y,z)</tt></p>
<p>If there is a construction or a planned construction at the specified
coordinates, designates it for removal, or instantly cancels the planned one.
Returns <em>true, was_only_planned</em> if removed; or <em>false</em> if none found.</p>
</li>
</ul> </ul>
</div> </div>
</div> </div>

@ -821,8 +821,10 @@ static const LuaWrapper::FunctionReg dfhack_buildings_module[] = {
WRAPM(Buildings, countExtentTiles), WRAPM(Buildings, countExtentTiles),
WRAPN(containsTile, buildings_containsTile), WRAPN(containsTile, buildings_containsTile),
WRAPM(Buildings, hasSupport), WRAPM(Buildings, hasSupport),
WRAPM(Buildings, constructAbstract),
WRAPM(Buildings, constructWithItems), WRAPM(Buildings, constructWithItems),
WRAPM(Buildings, constructWithFilters), WRAPM(Buildings, constructWithFilters),
WRAPM(Buildings, deconstruct),
{ NULL, NULL } { NULL, NULL }
}; };
@ -900,6 +902,20 @@ static const LuaWrapper::FunctionReg dfhack_constructions_module[] = {
{ NULL, NULL } { 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 * * Main Open function *

@ -149,6 +149,11 @@ DFHACK_EXPORT bool setSize(df::building *bld, df::coord2d size, int direction =
*/ */
DFHACK_EXPORT std::pair<df::coord,df::coord2d> getSize(df::building *bld); DFHACK_EXPORT std::pair<df::coord,df::coord2d> 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. * Initiates construction of the building, using specified items as inputs.
* Returns true if success. * Returns true if success.
@ -162,5 +167,11 @@ DFHACK_EXPORT bool constructWithItems(df::building *bld, std::vector<df::item*>
*/ */
DFHACK_EXPORT bool constructWithFilters(df::building *bld, std::vector<df::job_item*> items); DFHACK_EXPORT bool constructWithFilters(df::building *bld, std::vector<df::job_item*> items);
/**
* Deconstructs or queues deconstruction of a building.
* Returns true if the building has been destroyed instantly.
*/
DFHACK_EXPORT bool deconstruct(df::building *bld);
} }
} }

@ -62,6 +62,9 @@ DFHACK_EXPORT df::construction * getConstruction (const int32_t index);
DFHACK_EXPORT bool designateNew(df::coord pos, df::construction_type type, DFHACK_EXPORT bool designateNew(df::coord pos, df::construction_type type,
df::item_type item = df::item_type::NONE, int mat_index = -1); df::item_type item = df::item_type::NONE, int mat_index = -1);
DFHACK_EXPORT bool designateRemove(df::coord pos, bool *immediate = NULL);
} }
} }
#endif #endif

@ -28,6 +28,8 @@ local buildings = dfhack.buildings
items = { item, item ... }, items = { item, item ... },
-- OR -- OR
filter = { { ... }, { ... }... } filter = { { ... }, { ... }... }
-- OR
abstract = true
} }
Returns: the created building, or 'nil, error' Returns: the created building, or 'nil, error'
@ -41,8 +43,8 @@ function buildings.constructBuilding(info)
if not (info.pos or info.x) then if not (info.pos or info.x) then
error('position is required') error('position is required')
end end
if not (info.items or info.filters) then if not (info.abstract or info.items or info.filters) then
error('either items or filters are required') error('one of items, filters or abstract are required')
elseif info.filters then elseif info.filters then
for _,v in ipairs(info.filters) do for _,v in ipairs(info.filters) do
v.new = true v.new = true
@ -77,7 +79,9 @@ function buildings.constructBuilding(info)
if info.full_rectangle and area ~= r_area then if info.full_rectangle and area ~= r_area then
return nil, "not all tiles can be used" return nil, "not all tiles can be used"
end end
if info.items then if info.abstract then
ok = buildings.constructAbstract(instance)
elseif info.items then
ok = buildings.constructWithItems(instance, info.items) ok = buildings.constructWithItems(instance, info.items)
else else
ok = buildings.constructWithFilters(instance, info.filters) ok = buildings.constructWithFilters(instance, info.filters)

@ -46,6 +46,7 @@ using namespace DFHack;
#include "DataDefs.h" #include "DataDefs.h"
#include "df/world.h" #include "df/world.h"
#include "df/ui.h" #include "df/ui.h"
#include "df/ui_look_list.h"
#include "df/d_init.h" #include "df/d_init.h"
#include "df/item.h" #include "df/item.h"
#include "df/job.h" #include "df/job.h"
@ -59,6 +60,7 @@ using namespace DFHack;
#include "df/building_bridgest.h" #include "df/building_bridgest.h"
#include "df/building_coffinst.h" #include "df/building_coffinst.h"
#include "df/building_civzonest.h" #include "df/building_civzonest.h"
#include "df/building_stockpilest.h"
#include "df/building_furnacest.h" #include "df/building_furnacest.h"
#include "df/building_workshopst.h" #include "df/building_workshopst.h"
#include "df/building_screw_pumpst.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; 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) static void linkBuilding(df::building *bld)
{ {
bld->id = (*building_next_id)++; 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<df::building_stockpilest>(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<df::building_stockpilest>(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) static bool linkForConstruct(df::job* &job, df::building *bld)
{ {
if (!checkBuildingTiles(bld, false)) if (!checkBuildingTiles(bld, false))
@ -836,3 +896,64 @@ bool Buildings::constructWithFilters(df::building *bld, std::vector<df::job_item
return true; return true;
} }
bool Buildings::deconstruct(df::building *bld)
{
using df::global::ui;
using df::global::world;
using df::global::process_jobs;
using df::global::process_dig;
using df::global::ui_look_list;
CHECK_NULL_POINTER(bld);
if (bld->isActual() && 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;
}

@ -129,3 +129,43 @@ bool Constructions::designateNew(df::coord pos, df::construction_type type,
return true; 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<df::building_constructionst>(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;
}