From 2303a25bdefab30bea67f32d27d35e5002448e8a Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 29 Apr 2012 21:07:39 +0400 Subject: [PATCH] Implement unconstructed building instance creation and linking into world. For more flexibility, the base api is split into 3 phases: alloc, setSize, and construct. No support for non-actual buildings like stockpiles and activity zones at the moment. --- library/DataDefs.cpp | 13 +- library/LuaApi.cpp | 36 ++ library/LuaTypes.cpp | 5 + library/MiscUtils.cpp | 6 +- library/include/DataDefs.h | 3 + library/include/Error.h | 12 + library/include/modules/Buildings.h | 45 +++ library/include/modules/Job.h | 7 + library/lua/dfhack.lua | 34 +- library/modules/Buildings.cpp | 579 ++++++++++++++++++++++++++++ library/modules/Job.cpp | 37 ++ library/xml | 2 +- plugins/workflow.cpp | 2 +- 13 files changed, 764 insertions(+), 17 deletions(-) diff --git a/library/DataDefs.cpp b/library/DataDefs.cpp index 061110ecc..7f0bacc9e 100644 --- a/library/DataDefs.cpp +++ b/library/DataDefs.cpp @@ -232,15 +232,26 @@ void virtual_identity::doInit(Core *core) known[vtable_ptr] = this; } +virtual_identity *virtual_identity::find(const std::string &name) +{ + auto name_it = name_lookup.find(name); + + return (name_it != name_lookup.end()) ? name_it->second : NULL; +} + virtual_identity *virtual_identity::get(virtual_ptr instance_ptr) { if (!instance_ptr) return NULL; + return find(get_vtable(instance_ptr)); +} + +virtual_identity *virtual_identity::find(void *vtable) +{ // Actually, a reader/writer lock would be sufficient, // since the table is only written once per class. tthread::lock_guard lock(*known_mutex); - void *vtable = get_vtable(instance_ptr); std::map::iterator it = known.find(vtable); if (it != known.end()) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 8855a50da..82c8bb84b 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -47,6 +47,7 @@ distribution. #include "modules/Maps.h" #include "modules/MapCache.h" #include "modules/Burrows.h" +#include "modules/Buildings.h" #include "LuaWrapper.h" #include "LuaTools.h" @@ -762,6 +763,40 @@ static const luaL_Reg dfhack_burrows_funcs[] = { { NULL, NULL } }; +/***** Buildings module *****/ + +static const LuaWrapper::FunctionReg dfhack_buildings_module[] = { + WRAPM(Buildings, allocInstance), + WRAPM(Buildings, checkFreeTiles), + WRAPM(Buildings, setSize), + WRAPM(Buildings, constructWithItems), + WRAPM(Buildings, constructWithFilters), + { NULL, NULL } +}; + +static int buildings_getCorrectSize(lua_State *state) +{ + int w = luaL_optint(state, 1, 1); + int h = luaL_optint(state, 2, 1); + int t = luaL_optint(state, 3, -1); + int st = luaL_optint(state, 4, -1); + int cu = luaL_optint(state, 5, -1); + int d = luaL_optint(state, 6, 0); + df::coord2d size(w,h); + df::coord2d center; + bool flexible = Buildings::getCorrectSize(size, center, df::building_type(t), st, cu, d); + lua_pushboolean(state, flexible); + lua_pushinteger(state, size.x); + lua_pushinteger(state, size.y); + lua_pushinteger(state, center.x); + lua_pushinteger(state, center.y); + return 5; +} + +static const luaL_Reg dfhack_buildings_funcs[] = { + { "getCorrectSize", buildings_getCorrectSize }, + { NULL, NULL } +}; /************************ * Main Open function * @@ -779,4 +814,5 @@ void OpenDFHackApi(lua_State *state) OpenModule(state, "items", dfhack_items_module, dfhack_items_funcs); OpenModule(state, "maps", dfhack_maps_module, dfhack_maps_funcs); OpenModule(state, "burrows", dfhack_burrows_module, dfhack_burrows_funcs); + OpenModule(state, "buildings", dfhack_buildings_module, dfhack_buildings_funcs); } diff --git a/library/LuaTypes.cpp b/library/LuaTypes.cpp index b4a4f5d4f..02a89f9e2 100644 --- a/library/LuaTypes.cpp +++ b/library/LuaTypes.cpp @@ -1029,6 +1029,11 @@ int LuaWrapper::method_wrapper_core(lua_State *state, function_identity_base *id std::string tmp = stl_sprintf("NULL pointer: %s", vn ? vn : "?"); field_error(state, UPVAL_METHOD_NAME, tmp.c_str(), "invoke"); } + catch (Error::InvalidArgument &e) { + const char *vn = e.expr(); + std::string tmp = stl_sprintf("Invalid argument; expected: %s", vn ? vn : "?"); + field_error(state, UPVAL_METHOD_NAME, tmp.c_str(), "invoke"); + } catch (std::exception &e) { std::string tmp = stl_sprintf("C++ exception: %s", e.what()); field_error(state, UPVAL_METHOD_NAME, tmp.c_str(), "invoke"); diff --git a/library/MiscUtils.cpp b/library/MiscUtils.cpp index b9ff35cfc..8658788bb 100644 --- a/library/MiscUtils.cpp +++ b/library/MiscUtils.cpp @@ -41,7 +41,11 @@ distribution. #include const char *DFHack::Error::NullPointer::what() const throw() { - return "NULL pointer access"; + return "DFHack::Error::NullPointer"; +} + +const char *DFHack::Error::InvalidArgument::what() const throw() { + return "DFHack::Error::InvalidArgument"; } std::string stl_sprintf(const char *fmt, ...) { diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index a411ae667..57773e898 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -318,6 +318,9 @@ namespace DFHack public: static virtual_identity *get(virtual_ptr instance_ptr); + static virtual_identity *find(void *vtable); + static virtual_identity *find(const std::string &name); + bool is_instance(virtual_ptr instance_ptr) { if (!instance_ptr) return false; if (vtable_ptr) { diff --git a/library/include/Error.h b/library/include/Error.h index 0fbc6ba78..448c1a4f2 100644 --- a/library/include/Error.h +++ b/library/include/Error.h @@ -51,6 +51,18 @@ namespace DFHack #define CHECK_NULL_POINTER(var) \ { if (var == NULL) throw DFHack::Error::NullPointer(#var); } + class DFHACK_EXPORT InvalidArgument : public All { + const char *expr_; + public: + InvalidArgument(const char *expr_ = NULL) : expr_(expr_) {} + const char *expr() const { return expr_; } + virtual const char *what() const throw(); + }; + +#define CHECK_INVALID_ARGUMENT(expr) \ + { if (!(expr)) throw DFHack::Error::InvalidArgument(#expr); } + + class DFHACK_EXPORT AllSymbols : public All{}; // Syntax errors and whatnot, the xml can't be read class DFHACK_EXPORT SymbolsXmlParse : public AllSymbols diff --git a/library/include/modules/Buildings.h b/library/include/modules/Buildings.h index ec44f7713..5ec04f5e2 100644 --- a/library/include/modules/Buildings.h +++ b/library/include/modules/Buildings.h @@ -34,6 +34,13 @@ distribution. #include "df/siegeengine_type.h" #include "df/trap_type.h" +namespace df +{ + struct job_item; + struct item; + struct building_extents; +} + namespace DFHack { namespace Buildings @@ -84,5 +91,43 @@ DFHACK_EXPORT bool Read (const uint32_t index, t_building & building); */ DFHACK_EXPORT bool ReadCustomWorkshopTypes(std::map & btypes); +/** + * Allocates a building object using this type and position. + */ +DFHACK_EXPORT df::building *allocInstance(df::coord pos, df::building_type type, int subtype = -1, int custom = -1); + +/** + * Sets size and center to the correct dimensions of the building. + * If part of the original size value was used, returns true. + */ +DFHACK_EXPORT bool getCorrectSize(df::coord2d &size, df::coord2d ¢er, + df::building_type type, int subtype = -1, int custom = -1, + int direction = 0); + +/** + * Checks if the tiles are free to be built upon. + */ +DFHACK_EXPORT bool checkFreeTiles(df::coord pos, df::coord2d size, + df::building_extents *ext = NULL, bool create_ext = false); + +/** + * Sets the size of the building, using size and direction as guidance. + * Returns true if the building can be created at its position, using that size. + */ +DFHACK_EXPORT bool setSize(df::building *bld, df::coord2d size, int direction = 0); + +/** + * Initiates construction of the building, using specified items as inputs. + * Returns true if success. + */ +DFHACK_EXPORT bool constructWithItems(df::building *bld, std::vector items); + +/** + * Initiates construction of the building, using specified item filters. + * Assumes immediate ownership of the item objects, and deletes them in case of error. + * Returns true if success. + */ +DFHACK_EXPORT bool constructWithFilters(df::building *bld, std::vector items); + } } diff --git a/library/include/modules/Job.h b/library/include/modules/Job.h index d733c542d..a4e173a8d 100644 --- a/library/include/modules/Job.h +++ b/library/include/modules/Job.h @@ -30,6 +30,9 @@ distribution. #include "Module.h" #include +#include "DataDefs.h" +#include "df/job_item_ref.h" + namespace df { struct job; @@ -58,6 +61,10 @@ namespace DFHack // lists jobs with ids >= *id_var, and sets *id_var = *job_next_id; DFHACK_EXPORT bool listNewlyCreated(std::vector *pvec, int *id_var); + + DFHACK_EXPORT bool attachJobItem(df::job *job, df::item *item, + df::job_item_ref::T_role role, + int filter_idx = -1, int insert_idx = -1); } DFHACK_EXPORT bool operator== (const df::job_item &a, const df::job_item &b); diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index aedd460b1..6915cbb8a 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -199,6 +199,25 @@ function dfhack.interpreter(prompt,hfile,env) local t_prompt = nil local vcnt = 1 + local pfix_handlers = { + ['!'] = function(data) + print(table.unpack(data,2,data.n)) + end, + ['~'] = function(data) + print(table.unpack(data,2,data.n)) + printall(data[2]) + end, + ['='] = function(data) + for i=2,data.n do + local varname = '_'..vcnt + prompt_env[varname] = data[i] + dfhack.print(varname..' = ') + safecall(print, data[i]) + vcnt = vcnt + 1 + end + end + } + setmetatable(prompt_env, { __index = env or _G }) while true do @@ -209,7 +228,7 @@ function dfhack.interpreter(prompt,hfile,env) elseif cmdline ~= '' then local pfix = string.sub(cmdline,1,1) - if not t_prompt and (pfix == '!' or pfix == '=') then + if not t_prompt and pfix_handlers[pfix] then cmdline = 'return '..string.sub(cmdline,2) else pfix = nil @@ -236,18 +255,7 @@ function dfhack.interpreter(prompt,hfile,env) if data[1] and data.n > 1 then prompt_env._ = data[2] - - if pfix == '!' then - safecall(print, table.unpack(data,2,data.n)) - else - for i=2,data.n do - local varname = '_'..vcnt - prompt_env[varname] = data[i] - dfhack.print(varname..' = ') - safecall(print, data[i]) - vcnt = vcnt + 1 - end - end + safecall(pfix_handlers[pfix], data) end end end diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 6317f3e10..a4dc1852a 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -35,17 +35,41 @@ using namespace std; #include "Types.h" #include "Error.h" #include "modules/Buildings.h" +#include "modules/Maps.h" +#include "modules/Job.h" #include "ModuleFactory.h" #include "Core.h" +#include "TileTypes.h" +#include "MiscUtils.h" using namespace DFHack; #include "DataDefs.h" #include "df/world.h" +#include "df/ui.h" +#include "df/d_init.h" +#include "df/item.h" +#include "df/job.h" +#include "df/job_item.h" +#include "df/general_ref_building_holderst.h" +#include "df/buildings_other_id.h" +#include "df/building_design.h" #include "df/building_def.h" +#include "df/building_axle_horizontalst.h" +#include "df/building_trapst.h" +#include "df/building_bridgest.h" +#include "df/building_coffinst.h" +#include "df/building_furnacest.h" #include "df/building_workshopst.h" +#include "df/building_screw_pumpst.h" +#include "df/building_water_wheelst.h" +#include "df/building_wellst.h" using namespace df::enums; +using df::global::ui; using df::global::world; +using df::global::d_init; +using df::global::building_next_id; +using df::global::process_jobs; using df::building_def; uint32_t Buildings::getNumBuildings() @@ -86,3 +110,558 @@ bool Buildings::ReadCustomWorkshopTypes(map & btypes) return true; } +df::building *Buildings::allocInstance(df::coord pos, df::building_type type, int subtype, int custom) +{ + if (!building_next_id) + return NULL; + + // Allocate object + const char *classname = ENUM_ATTR(building_type, classname, type); + if (!classname) + return NULL; + + auto id = virtual_identity::find(classname); + if (!id) + return NULL; + + df::building *bld = (df::building*)id->allocate(); + if (!bld) + return NULL; + + // Init base fields + bld->x1 = bld->x2 = bld->centerx = pos.x; + bld->y1 = bld->y2 = bld->centery = pos.y; + bld->z = pos.z; + + bld->race = ui->race_id; + + if (subtype != -1) + bld->setSubtype(subtype); + if (custom != -1) + bld->setCustomType(custom); + + bld->setMaterialAmount(1); + + // Type specific init + switch (type) + { + case building_type::Well: + { + auto obj = (df::building_wellst*)bld; + obj->bucket_z = bld->z; + break; + } + case building_type::Furnace: + { + auto obj = (df::building_furnacest*)bld; + obj->melt_remainder.resize(df::inorganic_raw::get_vector().size(), 0); + break; + } + case building_type::Coffin: + { + auto obj = (df::building_coffinst*)bld; + if (d_init && d_init->flags3.is_set(d_init_flags3::COFFIN_NO_PETS_DEFAULT)) + obj->burial_mode.bits.no_pets = true; + break; + } + case building_type::Trap: + { + auto obj = (df::building_trapst*)bld; + if (obj->trap_type == trap_type::PressurePlate) + obj->unk_cc = 500; + break; + } + default: + break; + } + + return bld; +} + +static void makeOneDim(df::coord2d &size, df::coord2d ¢er, bool vertical) +{ + if (vertical) + size.x = 1; + else + size.y = 1; + center = size/2; +} + +bool Buildings::getCorrectSize(df::coord2d &size, df::coord2d ¢er, + df::building_type type, int subtype, int custom, int direction) +{ + using namespace df::enums::building_type; + + if (size.x <= 0) + size.x = 1; + if (size.y <= 0) + size.y = 1; + + switch (type) + { + case FarmPlot: + case Bridge: + case RoadDirt: + case RoadPaved: + case Stockpile: + case Civzone: + center = size/2; + return true; + + case TradeDepot: + case Shop: + size = df::coord2d(5,5); + center = df::coord2d(2,2); + return false; + + case SiegeEngine: + case Windmill: + case Wagon: + size = df::coord2d(3,3); + center = df::coord2d(1,1); + return false; + + case AxleHorizontal: + makeOneDim(size, center, direction); + return true; + + case WaterWheel: + size = df::coord2d(3,3); + makeOneDim(size, center, direction); + return false; + + case Workshop: + { + using namespace df::enums::workshop_type; + + switch ((df::workshop_type)subtype) + { + case Quern: + case Millstone: + case Tool: + size = df::coord2d(1,1); + center = df::coord2d(0,0); + break; + + case Siege: + case Kennels: + size = df::coord2d(5,5); + center = df::coord2d(2,2); + break; + + case Custom: + if (auto def = df::building_def::find(custom)) + { + size = df::coord2d(def->dim_x, def->dim_y); + center = df::coord2d(def->workloc_x, def->workloc_y); + break; + } + + default: + size = df::coord2d(3,3); + center = df::coord2d(1,1); + } + + return false; + } + + case Furnace: + { + using namespace df::enums::furnace_type; + + switch ((df::furnace_type)subtype) + { + case Custom: + if (auto def = df::building_def::find(custom)) + { + size = df::coord2d(def->dim_x, def->dim_y); + center = df::coord2d(def->workloc_x, def->workloc_y); + break; + } + + default: + size = df::coord2d(3,3); + center = df::coord2d(1,1); + } + + return false; + } + + case ScrewPump: + { + using namespace df::enums::screw_pump_direction; + + switch ((df::screw_pump_direction)direction) + { + case FromEast: + size = df::coord2d(2,1); + center = df::coord2d(1,0); + break; + case FromSouth: + size = df::coord2d(1,2); + center = df::coord2d(0,1); + break; + case FromWest: + size = df::coord2d(2,1); + center = df::coord2d(0,0); + break; + default: + size = df::coord2d(1,2); + center = df::coord2d(0,0); + } + + return false; + } + + default: + size = df::coord2d(1,1); + center = df::coord2d(0,0); + return false; + } +} + +static uint8_t *getExtentTile(df::building_extents &extent, df::coord2d tile) +{ + if (!extent.extents) + return NULL; + int dx = tile.x - extent.x; + int dy = tile.y - extent.y; + if (dx < 0 || dy < 0 || dx >= extent.width || dy >= extent.height) + return NULL; + return &extent.extents[dx + dy*extent.width]; +} + +bool Buildings::checkFreeTiles(df::coord pos, df::coord2d size, + df::building_extents *ext, bool create_ext) +{ + for (int dx = 0; dx < size.x; dx++) + { + for (int dy = 0; dy < size.y; dy++) + { + df::coord tile = pos + df::coord(dx,dy,0); + uint8_t *etile = (ext ? getExtentTile(*ext, tile) : NULL); + + if (etile && !*etile) + continue; + + df::map_block *block = Maps::getTileBlock(tile); + if (!block) + return false; + + df::coord2d btile = df::coord2d(tile) & 15; + + bool allowed = true; + + if (block->occupancy[btile.x][btile.y].bits.building) + allowed = false; + else + { + auto tile = block->tiletype[btile.x][btile.y]; + if (!HighPassable(tile)) + allowed = false; + } + + if (!allowed) + { + if (!ext || !create_ext) + return false; + + if (!ext->extents) + { + ext->extents = new uint8_t[size.x*size.y]; + ext->x = pos.x; + ext->y = pos.y; + ext->width = size.x; + ext->height = size.y; + + memset(ext->extents, 1, size.x*size.y); + etile = getExtentTile(*ext, tile); + } + + if (!etile) + return false; + + *etile = 0; + } + } + } + + return true; +} + +static bool checkBuildingTiles(df::building *bld, bool can_use_extents) +{ + df::coord pos(bld->x1,bld->y1,bld->z); + + return Buildings::checkFreeTiles(pos, df::coord2d(bld->x2+1,bld->y2+1) - pos, + &bld->room, can_use_extents); +} + +bool Buildings::setSize(df::building *bld, df::coord2d size, int direction) +{ + CHECK_NULL_POINTER(bld); + CHECK_INVALID_ARGUMENT(bld->id == -1); + + if (bld->room.extents) + { + delete[] bld->room.extents; + bld->room.extents = NULL; + } + + df::coord2d center; + getCorrectSize(size, center, bld->getType(), bld->getSubtype(), + bld->getCustomType(), direction); + + bld->x2 = bld->x1 + size.x - 1; + bld->y2 = bld->y1 + size.y - 1; + bld->centerx = bld->x1 + center.x; + bld->centery = bld->y1 + center.y; + + auto type = bld->getType(); + bool can_use_extents = false; + + using namespace df::enums::building_type; + + switch (type) + { + case WaterWheel: + { + auto obj = (df::building_water_wheelst*)bld; + obj->is_vertical = !!direction; + break; + } + case AxleHorizontal: + { + auto obj = (df::building_axle_horizontalst*)bld; + obj->is_vertical = !!direction; + break; + } + case ScrewPump: + { + auto obj = (df::building_screw_pumpst*)bld; + obj->direction = (df::screw_pump_direction)direction; + break; + } + case Bridge: + { + auto obj = (df::building_bridgest*)bld; + // TODO: computes some kind of flag from tile data + obj->direction = (df::building_bridgest::T_direction)direction; + break; + } + case FarmPlot: + case RoadDirt: + case RoadPaved: + case Stockpile: + case Civzone: + can_use_extents = true; + break; + default: + break; + } + + bool ok = checkBuildingTiles(bld, can_use_extents); + + if (type != building_type::Construction) + { + int cnt = size.x * size.y; + + if (bld->room.extents) + { + cnt = 0; + for (int i = 0; i < bld->room.width * bld->room.height; i++) + if (bld->room.extents[i] == 1) + cnt++; + } + + bld->setMaterialAmount(cnt/4 + 1); + } + + return ok; +} + +static void markBuildingTiles(df::building *bld, df::tile_building_occ occv, bool stockpile) +{ + for (int tx = bld->x1; tx <= bld->x2; tx++) + { + for (int ty = bld->y1; ty <= bld->y2; ty++) + { + df::coord tile(tx,ty,bld->z); + + uint8_t *etile = getExtentTile(bld->room, tile); + if (etile && !*etile) + continue; + + df::map_block *block = Maps::getTileBlock(tile); + df::coord2d btile = df::coord2d(tile) & 15; + + auto &occ = block->occupancy[btile.x][btile.y]; + occ.bits.building = occv; + + auto &des = block->designation[btile.x][btile.y]; + des.bits.dig = tile_dig_designation::No; + des.bits.pile = stockpile; + } + } +} + +static void linkRooms(df::building *bld) +{ + auto &vec = world->buildings.other[buildings_other_id::ANY_FREE]; + + bool changed = false; + + for (size_t i = 0; i < vec.size(); i++) + { + auto room = vec[i]; + if (!room->is_room || room->z != bld->z) + continue; + + uint8_t *pext = getExtentTile(room->room, df::coord2d(bld->x1, bld->y1)); + if (!pext || !*pext) + continue; + + changed = true; + room->children.push_back(bld); + bld->parents.push_back(room); + + // TODO: the game updates room rent here if economy is enabled + } + + if (changed) + df::global::ui->equipment.update.bits.buildings = true; +} + +static void linkBuilding(df::building *bld) +{ + bld->id = (*building_next_id)++; + + world->buildings.all.push_back(bld); + bld->categorize(true); + + markBuildingTiles(bld, tile_building_occ::Planned, false); + + linkRooms(bld); + + if (process_jobs) + *process_jobs = true; +} + +static void createDesign(df::building *bld, bool rough) +{ + auto job = bld->jobs[0]; + + job->mat_type = bld->mat_type; + job->mat_index = bld->mat_index; + + if (bld->needsDesign()) + { + auto act = (df::building_actual*)bld; + act->design = new df::building_design(); + + act->design->flags.bits.rough = rough; + } +} + +static bool linkForConstruct(df::job* &job, df::building *bld) +{ + if (!checkBuildingTiles(bld, false)) + return false; + + auto ref = df::allocate(); + if (!ref) + { + Core::printerr("Could not allocate general_ref_building_holderst\n"); + return false; + } + + linkBuilding(bld); + + ref->building_id = bld->id; + + job = new df::job(); + job->job_type = df::job_type::ConstructBuilding; + job->pos = df::coord(bld->centerx, bld->centery, bld->z); + job->references.push_back(ref); + + bld->jobs.push_back(job); + + Job::linkIntoWorld(job); + + return true; +} + +bool Buildings::constructWithItems(df::building *bld, std::vector items) +{ + CHECK_NULL_POINTER(bld); + CHECK_INVALID_ARGUMENT(!items.empty()); + CHECK_INVALID_ARGUMENT(bld->id == -1); + CHECK_INVALID_ARGUMENT(bld->isActual()); + + for (size_t i = 0; i < items.size(); i++) + { + CHECK_NULL_POINTER(items[i]); + + if (items[i]->flags.bits.in_job) + return false; + } + + df::job *job = NULL; + if (!linkForConstruct(job, bld)) + return false; + + bool rough = false; + + for (size_t i = 0; i < items.size(); i++) + { + Job::attachJobItem(job, items[i], df::job_item_ref::Hauled); + + if (items[i]->getType() == item_type::BOULDER) + rough = true; + if (bld->mat_type == -1) + bld->mat_type = items[i]->getMaterial(); + if (bld->mat_index == -1) + bld->mat_index = items[i]->getMaterialIndex(); + } + + createDesign(bld, rough); + return true; +} + +bool Buildings::constructWithFilters(df::building *bld, std::vector items) +{ + CHECK_NULL_POINTER(bld); + CHECK_INVALID_ARGUMENT(!items.empty()); + CHECK_INVALID_ARGUMENT(bld->id == -1); + CHECK_INVALID_ARGUMENT(bld->isActual()); + + for (size_t i = 0; i < items.size(); i++) + CHECK_NULL_POINTER(items[i]); + + df::job *job = NULL; + if (!linkForConstruct(job, bld)) + { + for (size_t i = 0; i < items.size(); i++) + delete items[i]; + + return false; + } + + bool rough = false; + + for (size_t i = 0; i < items.size(); i++) + { + job->job_items.push_back(items[i]); + + if (items[i]->item_type == item_type::BOULDER) + rough = true; + if (bld->mat_type == -1) + bld->mat_type = items[i]->mat_type; + if (bld->mat_index == -1) + bld->mat_index = items[i]->mat_index; + } + + createDesign(bld, rough); + return true; +} + diff --git a/library/modules/Job.cpp b/library/modules/Job.cpp index 5ce6647d9..8e8e025d4 100644 --- a/library/modules/Job.cpp +++ b/library/modules/Job.cpp @@ -311,3 +311,40 @@ bool DFHack::Job::listNewlyCreated(std::vector *pvec, int *id_var) return true; } + +bool DFHack::Job::attachJobItem(df::job *job, df::item *item, + df::job_item_ref::T_role role, + int filter_idx, int insert_idx) +{ + CHECK_NULL_POINTER(job); + CHECK_NULL_POINTER(item); + + /* + * Functionality 100% reverse-engineered from DF code. + */ + + if (role != df::job_item_ref::TargetContainer) + { + if (item->flags.bits.in_job) + return false; + + item->flags.bits.in_job = true; + } + + auto item_link = new df::item::T_jobs(); + item_link->type = 2; + item_link->job = job; + item->jobs.push_back(item_link); + + auto job_link = new df::job_item_ref(); + job_link->item = item; + job_link->role = role; + job_link->job_item_idx = filter_idx; + + if (size_t(insert_idx) < job->items.size()) + vector_insert_at(job->items, insert_idx, job_link); + else + job->items.push_back(job_link); + + return true; +} diff --git a/library/xml b/library/xml index f649d3100..d75556019 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit f649d31001e6023a9df5fe83c7971c17afe0d87d +Subproject commit d755560192ec318fbb52133e4e52972bee67d1e0 diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index 5e5ca84bc..0d6caa472 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -1084,7 +1084,7 @@ static bool itemInRealJob(df::item *item) return false; if (item->jobs.size() != 1 || - item->jobs[0]->unk1 != 2 || + item->jobs[0]->type != 2 || item->jobs[0]->job == NULL) return true;