From dca19e9dadbc2adcc7c47cc148ae74e95668a06b Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 3 Mar 2023 11:13:25 -0800 Subject: [PATCH 01/28] Adds more logging for dig-now --- plugins/dig-now.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/dig-now.cpp b/plugins/dig-now.cpp index e94cb41da..037e7ae84 100644 --- a/plugins/dig-now.cpp +++ b/plugins/dig-now.cpp @@ -81,6 +81,7 @@ private: public: void load(MapExtras::MapCache &map) { designations.clear(); + DEBUG(general).print("DesignationJobs: reading jobs list\n"); df::job_list_link* node = df::global::world->jobs.list.next; while (node) { df::job* job = node->item; @@ -134,6 +135,7 @@ public: jobs.emplace(job->pos, job); } } + DEBUG(general).print("DesignationJobs: DONE reading jobs list\n"); } void remove(const df::coord &pos) { if(jobs.count(pos)) { @@ -731,9 +733,11 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, Random::MersenneRNG rng; DesignationJobs jobs; + DEBUG(general).print("do_dig(): starting..\n"); jobs.load(map); rng.init(); + DEBUG(general).print("do_dig(): reading map..\n"); std::unordered_set buffer; // go down levels instead of up so stacked ramps behave as expected for (int16_t z = options.end.z; z >= options.start.z; --z) { @@ -765,6 +769,7 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, } } + DEBUG(general).print("do_dig(): processing designations..\n"); // process designations for(auto &d : buffer) { auto pos = d.pos; @@ -812,6 +817,7 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, } } + DEBUG(general).print("do_dig(): write changes to map..\n"); map.WriteAll(); } From 161f84e7b7b350e5a0f6475f0203dd7cc44fa9c3 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 6 Mar 2023 01:27:27 -0800 Subject: [PATCH 02/28] turn down automaterial (superseded by buildingplan) --- docs/about/Removed.rst | 8 + docs/changelog.txt | 1 + docs/plugins/automaterial.rst | 53 -- plugins/CMakeLists.txt | 1 - plugins/automaterial.cpp | 1340 --------------------------------- plugins/lua/automaterial.lua | 23 - 6 files changed, 9 insertions(+), 1417 deletions(-) delete mode 100644 docs/plugins/automaterial.rst delete mode 100644 plugins/automaterial.cpp delete mode 100644 plugins/lua/automaterial.lua diff --git a/docs/about/Removed.rst b/docs/about/Removed.rst index 954aa2ce2..eea11b088 100644 --- a/docs/about/Removed.rst +++ b/docs/about/Removed.rst @@ -18,6 +18,14 @@ An automated labor management tool that only addressed hauling labors, leaving t of skilled labors entirely up to the player. Fundamentally incompatible with the work detail system of labor management in v50 of Dwarf Fortress. +.. _automaterial: + +automaterial +============ +Moved frequently used materials to the top of the materials list when building +buildings. Also offered extended options when building constructions. All +functionality has been merged into `buildingplan`. + .. _combine-drinks: combine-drinks diff --git a/docs/changelog.txt b/docs/changelog.txt index 4a3428031..3eb93b1c0 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -56,6 +56,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Removed -@ ``gui.THIN_FRAME``: replaced by ``gui.INTERIOR_FRAME`` +- `automaterial`: all functionality has been merged into `buildingplan` # 50.07-alpha2 diff --git a/docs/plugins/automaterial.rst b/docs/plugins/automaterial.rst deleted file mode 100644 index 54653a685..000000000 --- a/docs/plugins/automaterial.rst +++ /dev/null @@ -1,53 +0,0 @@ -automaterial -============ - -.. dfhack-tool:: - :summary: Sorts building materials by recent usage. - :tags: untested fort design productivity buildings map - :no-command: - -This plugin makes building constructions (walls, floors, fortifications, etc) -much easier by saving you from having to trawl through long lists of materials -each time you place one. - -It moves the last used material for a given construction type to the top of the -list, if there are any left. So if you build a wall with chalk blocks, the next -time you place a wall the chalk blocks will be at the top of the list, -regardless of distance (it only does this in "grouped" mode, as individual item -lists could be huge). This means you can place most constructions without having -to search for your preferred material type. - -Usage ------ - -:: - - enable automaterial - -.. image:: ../images/automaterial-mat.png - -Pressing :kbd:`a` while highlighting any material will enable that material for -"auto select" for this construction type. You can enable multiple materials. Now -the next time you place this type of construction, the plugin will automatically -choose materials for you from the kinds you enabled. If there is enough to -satisfy the whole placement, you won't be prompted with the material screen at -all -- the construction will be placed and you will be back in the construction -menu. - -When choosing the construction placement, you will see a couple of options: - -.. image:: ../images/automaterial-pos.png - -Use :kbd:`a` here to temporarily disable the material autoselection, e.g. if you -need to go to the material selection screen so you can toggle some materials on -or off. - -The other option (auto type selection, off by default) can be toggled on with -:kbd:`t`. If you toggle this option on, instead of returning you to the main -construction menu after selecting materials, it returns you back to this screen. -If you use this along with several autoselect enabled materials, you should be -able to place complex constructions more conveniently. - -The ``automaterial`` plugin also enables extra construction placement modes, -such as designating areas larger than 10x10 and allowing you to designate hollow -rectangles instead of the default filled ones. diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index d87b41978..477e83436 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -82,7 +82,6 @@ dfhack_plugin(autodump autodump.cpp) dfhack_plugin(autofarm autofarm.cpp) #dfhack_plugin(autogems autogems.cpp LINK_LIBRARIES jsoncpp_static) add_subdirectory(autolabor) -#dfhack_plugin(automaterial automaterial.cpp LINK_LIBRARIES lua) dfhack_plugin(automelt automelt.cpp LINK_LIBRARIES lua) dfhack_plugin(autonestbox autonestbox.cpp LINK_LIBRARIES lua) #dfhack_plugin(autotrade autotrade.cpp) diff --git a/plugins/automaterial.cpp b/plugins/automaterial.cpp deleted file mode 100644 index 9783d28dc..000000000 --- a/plugins/automaterial.cpp +++ /dev/null @@ -1,1340 +0,0 @@ -// Auto Material Select - -#include -#include -#include - -#include "Core.h" -#include "LuaTools.h" -#include -#include -#include -#include - - -// DF data structure definition headers -#include "DataDefs.h" -#include "Debug.h" -#include "MiscUtils.h" -#include "TileTypes.h" -#include "df/build_req_choice_genst.h" -#include "df/build_req_choice_specst.h" -#include "df/construction_type.h" -#include "df/item.h" -#include "df/plotinfost.h" -#include "df/buildreq.h" -#include "df/viewscreen_dwarfmodest.h" -#include "df/items_other_id.h" -#include "df/job.h" -#include "df/world.h" -#include "df/building_constructionst.h" -#include "df/job_item.h" - -#include "modules/Gui.h" -#include "modules/Screen.h" -#include "modules/Items.h" -#include "modules/Constructions.h" -#include "modules/Buildings.h" -#include "modules/Maps.h" -#include "modules/MapCache.h" - -#include "uicommon.h" - -using namespace std; -using std::map; -using std::string; -using std::vector; - -using namespace DFHack; -using namespace df::enums; - -DFHACK_PLUGIN("automaterial"); -REQUIRE_GLOBAL(gps); -REQUIRE_GLOBAL(plotinfo); -REQUIRE_GLOBAL(ui_build_selector); - -namespace DFHack { - DBG_DECLARE(automaterial,log,DebugCategory::LINFO); -} - -struct MaterialDescriptor -{ - df::item_type item_type; - int16_t item_subtype; - int16_t type; - int32_t index; - bool valid; - - bool matches(const MaterialDescriptor &a) const - { - return a.valid && valid && - a.type == type && - a.index == index && - a.item_type == item_type && - a.item_subtype == item_subtype; - } -}; - -DFhackCExport command_result plugin_shutdown ( color_ostream &out ) -{ - return CR_OK; -} - -void AMOutputToggleString(int &x, int &y, const char *text, const char *hotkey, bool state, bool newline = true, int left_margin = 0, int8_t color = COLOR_WHITE) -{ - OutputHotkeyString(x, y, text, hotkey); - OutputString(COLOR_WHITE, x, y, ": "); - if (state) - OutputString(COLOR_GREEN, x, y, "Enabled", newline, left_margin); - else - OutputString(COLOR_GREY, x, y, "Disabled", newline, left_margin); -} - -//START UI Functions - -static enum t_box_select_mode {SELECT_FIRST, SELECT_SECOND, SELECT_MATERIALS, AUTOSELECT_MATERIALS} box_select_mode = SELECT_FIRST; -static coord32_t box_first, box_second; -static bool box_select_enabled = false; -static bool show_box_selection = true; -static bool hollow_selection = false; -static deque box_select_materials; - -#define SELECTION_IGNORE_TICKS 1 -static int ignore_selection = SELECTION_IGNORE_TICKS; - -static map last_used_material; -static map last_moved_material; -static map< int16_t, vector > preferred_materials; -static map< int16_t, df::interface_key > hotkeys; -static bool last_used_moved = false; -static bool auto_choose_materials = true; -static bool revert_to_last_used_type = false; -static bool allow_future_placement = false; - -static inline bool in_material_choice_stage() -{ - return Gui::build_selector_hotkey(Core::getTopViewscreen()) && - ui_build_selector->building_type == df::building_type::Construction && - plotinfo->main.mode == ui_sidebar_mode::Build && - ui_build_selector->stage == 2; -} - -static inline bool in_placement_stage() -{ - return Gui::dwarfmode_hotkey(Core::getTopViewscreen()) && - plotinfo->main.mode == ui_sidebar_mode::Build && - ui_build_selector && - ui_build_selector->building_type == df::building_type::Construction && - ui_build_selector->stage == 1; -} - -static inline bool in_type_choice_stage() -{ - return Gui::dwarfmode_hotkey(Core::getTopViewscreen()) && - plotinfo->main.mode == ui_sidebar_mode::Build && - ui_build_selector && - ui_build_selector->building_type < 0; -} - -static inline vector &get_curr_constr_prefs() -{ - if (preferred_materials.find(ui_build_selector->building_subtype) == preferred_materials.end()) - preferred_materials[ui_build_selector->building_subtype] = vector(); - - return preferred_materials[ui_build_selector->building_subtype]; -} - -static inline MaterialDescriptor &get_last_used_material() -{ - if (last_used_material.find(ui_build_selector->building_subtype) == last_used_material.end()) - last_used_material[ui_build_selector->building_subtype] = MaterialDescriptor(); - - return last_used_material[ui_build_selector->building_subtype]; -} - -static void set_last_used_material(const MaterialDescriptor &matetial) -{ - last_used_material[ui_build_selector->building_subtype] = matetial; -} - -static MaterialDescriptor &get_last_moved_material() -{ - if (last_moved_material.find(ui_build_selector->building_subtype) == last_moved_material.end()) - last_moved_material[ui_build_selector->building_subtype] = MaterialDescriptor(); - - return last_moved_material[ui_build_selector->building_subtype]; -} - -static void set_last_moved_material(const MaterialDescriptor &matetial) -{ - last_moved_material[ui_build_selector->building_subtype] = matetial; -} - -static MaterialDescriptor get_material_in_list(size_t i) -{ - MaterialDescriptor result; - result.valid = false; - - if (VIRTUAL_CAST_VAR(gen, df::build_req_choice_genst, ui_build_selector->choices[i])) - { - result.item_type = gen->item_type; - result.item_subtype = gen->item_subtype; - result.type = gen->mat_type; - result.index = gen->mat_index; - result.valid = true; - } - else if (VIRTUAL_CAST_VAR(spec, df::build_req_choice_specst, ui_build_selector->choices[i])) - { - result.item_type = spec->candidate->getType(); - result.item_subtype = spec->candidate->getSubtype(); - result.type = spec->candidate->getActualMaterial(); - result.index = spec->candidate->getActualMaterialIndex(); - result.valid = true; - } - - return result; -} - -static bool is_material_in_autoselect(size_t &i, MaterialDescriptor &material) -{ - for (i = 0; i < get_curr_constr_prefs().size(); i++) - { - if (get_curr_constr_prefs()[i].matches(material)) - return true; - } - - return false; -} - -static bool is_material_in_list(size_t &i, MaterialDescriptor &material) -{ - const size_t size = ui_build_selector->choices.size(); //Just because material list could be very big - for (i = 0; i < size; i++) - { - if (get_material_in_list(i).matches(material)) - return true; - } - - return false; -} - -static bool move_material_to_top(MaterialDescriptor &material) -{ - size_t i; - if (is_material_in_list(i, material)) - { - auto sel_item = ui_build_selector->choices[i]; - ui_build_selector->choices.erase(ui_build_selector->choices.begin() + i); - ui_build_selector->choices.insert(ui_build_selector->choices.begin(), sel_item); - - ui_build_selector->sel_index = 0; - set_last_moved_material(material); - return true; - } - - set_last_moved_material(MaterialDescriptor()); - return false; -} - -static bool check_autoselect(MaterialDescriptor &material, bool toggle) -{ - size_t idx; - if (is_material_in_autoselect(idx, material)) - { - if (toggle) - vector_erase_at(get_curr_constr_prefs(), idx); - - return true; - } - else - { - if (toggle) - get_curr_constr_prefs().push_back(material); - - return false; - } -} - -static void cancel_box_selection() -{ - if (box_select_mode == SELECT_FIRST) - return; - - box_select_mode = SELECT_FIRST; - box_select_materials.clear(); - if (!show_box_selection) - Gui::setDesignationCoords(-1, -1, -1); -} -//END UI Functions - - -//START Building and Verification -struct building_site -{ - df::coord pos; - bool in_open_air; - - building_site(df::coord pos, bool in_open_air) - { - this->pos = pos; - this->in_open_air = in_open_air; - } - - building_site() {} -}; - -static deque valid_building_sites; -static deque open_air_sites; -static building_site anchor; - -static bool is_orthogonal_to_pending_construction(building_site &site) -{ - for (deque::iterator it = valid_building_sites.begin(); it != valid_building_sites.end(); it++) - { - if ((it->pos.x == site.pos.x && abs(it->pos.y - site.pos.y) == 1) || (it->pos.y == site.pos.y && abs(it->pos.x - site.pos.x) == 1)) - { - site.in_open_air = true; - return true; - } - } - - return false; -} - -static df::building_constructionst *get_construction_on_tile(const df::coord &pos) -{ - auto current = Buildings::findAtTile(pos); - if (current) - return strict_virtual_cast(current); - - return NULL; -} - -static df::tiletype *read_tile_shapes(const df::coord &pos, df::tiletype_shape &shape, df::tiletype_shape_basic &shape_basic) -{ - if (!Maps::isValidTilePos(pos)) - return NULL; - - auto ttype = Maps::getTileType(pos); - - if (!ttype) - return NULL; - - shape = tileShape(*ttype); - shape_basic = tileShapeBasic(shape); - - return ttype; -} - -static bool is_valid_building_site(building_site &site, bool orthogonal_check, bool check_placed_constructions, bool in_future_placement_mode) -{ - df::tiletype_shape shape; - df::tiletype_shape_basic shape_basic; - - auto ttype = read_tile_shapes(site.pos, shape, shape_basic); - if (!ttype) - return false; - - if (shape_basic == tiletype_shape_basic::Open) - { - if (orthogonal_check) - { - // Check if this is a valid tile to have a construction placed orthogonally to it - if (!in_future_placement_mode) - return false; - - df::building_constructionst *cons = get_construction_on_tile(site.pos); - if (cons && cons->type == construction_type::Floor) - { - site.in_open_air = true; - return true; - } - - return false; - } - - // Stairs can be placed in open space, if they can connect to other stairs - df::tiletype_shape shape_s; - df::tiletype_shape_basic shape_basic_s; - - if (ui_build_selector->building_subtype == construction_type::DownStair || - ui_build_selector->building_subtype == construction_type::UpDownStair) - { - df::coord below(site.pos.x, site.pos.y, site.pos.z - 1); - auto ttype_s = read_tile_shapes(below, shape_s, shape_basic_s); - if (ttype_s) - { - if (shape_s == tiletype_shape::STAIR_UP || shape_s == tiletype_shape::STAIR_UPDOWN) - return true; - } - } - - if (ui_build_selector->building_subtype == construction_type::UpStair || - ui_build_selector->building_subtype == construction_type::UpDownStair) - { - df::coord above(site.pos.x, site.pos.y, site.pos.z + 1); - auto ttype_s = read_tile_shapes(above, shape_s, shape_basic_s); - if (ttype_s) - { - if (shape_s == tiletype_shape::STAIR_DOWN || shape_s == tiletype_shape::STAIR_UPDOWN) - return true; - } - } - - // Check if there is a valid tile orthogonally adjacent - bool valid_orthogonal_tile_found = false; - df::coord orthagonal_pos; - orthagonal_pos.z = site.pos.z; - for (orthagonal_pos.x = site.pos.x-1; orthagonal_pos.x <= site.pos.x+1 && !valid_orthogonal_tile_found; orthagonal_pos.x++) - { - for (orthagonal_pos.y = site.pos.y-1; orthagonal_pos.y <= site.pos.y+1; orthagonal_pos.y++) - { - if ((site.pos.x == orthagonal_pos.x) == (site.pos.y == orthagonal_pos.y)) - continue; - - building_site orthogonal_site(orthagonal_pos, false); - if (is_valid_building_site(orthogonal_site, true, check_placed_constructions, in_future_placement_mode)) - { - valid_orthogonal_tile_found = true; - if (orthogonal_site.in_open_air) - site.in_open_air = true; - break; - } - - } - } - - if (!(valid_orthogonal_tile_found || (check_placed_constructions && is_orthogonal_to_pending_construction(site)))) - { - site.in_open_air = true; - return false; - } - } - else if (orthogonal_check) - { - if (shape != tiletype_shape::RAMP && - shape_basic != tiletype_shape_basic::Floor && - shape_basic != tiletype_shape_basic::Stair) - return false; - } - else - { - auto material = tileMaterial(*ttype); - if (shape == tiletype_shape::RAMP) - { - if (material == tiletype_material::CONSTRUCTION) - return false; - } - else - { - if (shape != tiletype_shape::STAIR_DOWN && shape_basic != tiletype_shape_basic::Floor) - return false; - - // Can build on top of a wall, but not on other construction - auto construction = Constructions::findAtTile(site.pos); - if (construction) - { - if (construction->flags.bits.top_of_wall==0) - return false; - } - - if (material == tiletype_material::FIRE || - material == tiletype_material::POOL || - material == tiletype_material::BROOK || - material == tiletype_material::RIVER || - material == tiletype_material::MAGMA || - material == tiletype_material::DRIFTWOOD || - material == tiletype_material::CAMPFIRE - ) - - return false; - } - } - - if (orthogonal_check) - return true; - - auto designation = Maps::getTileDesignation(site.pos); - if (designation->bits.flow_size > 2) - return false; - - auto current = Buildings::findAtTile(site.pos); - if (current) - return false; - - df::coord2d size(1,1); - return Buildings::checkFreeTiles(site.pos, size, NULL, false, false); -} - - -static bool find_anchor_in_spiral(const df::coord &start) -{ - bool found = false; - - for (anchor.pos.z = start.z; anchor.pos.z > start.z - 4; anchor.pos.z--) - { - int x, y, dx, dy; - x = y = dx = 0; - dy = -1; - const int side = 11; - const int maxI = side*side; - for (int i = 0; i < maxI; i++) - { - if (-side/2 < x && x <= side/2 && -side/2 < y && y <= side/2) - { - anchor.pos.x = start.x + x; - anchor.pos.y = start.y + y; - if (is_valid_building_site(anchor, false, false, false)) - { - found = true; - break; - } - } - - if ((x == y) || ((x < 0) && (x == -y)) || ((x > 0) && (x == 1-y))) - { - int tmp = dx; - dx = -dy; - dy = tmp; - } - - x += dx; - y += dy; - } - - if (found) - break; - } - - return found; -} - -static bool find_valid_building_sites(bool in_future_placement_mode, bool use_buildingplan) -{ - valid_building_sites.clear(); - open_air_sites.clear(); - - int xD = (box_second.x > box_first.x) ? 1 : -1; - int yD = (box_second.y > box_first.y) ? 1 : -1; - for (int32_t xB = box_first.x; (xD > 0) ? (xB <= box_second.x) : (xB >= box_second.x); xB += xD) - { - for (int32_t yB = box_first.y; (yD > 0) ? (yB <= box_second.y) : (yB >= box_second.y); yB += yD) - { - if (hollow_selection && !(xB == box_first.x || xB == box_second.x || yB == box_first.y || yB == box_second.y)) - continue; - - building_site site(df::coord(xB, yB, box_second.z), false); - // if we're using buildingplan, it will take care of filtering out bad tiles - if (use_buildingplan || is_valid_building_site(site, false, true, in_future_placement_mode)) - valid_building_sites.push_back(site); - else if (site.in_open_air) - { - if (in_future_placement_mode) - valid_building_sites.push_back(site); - else - open_air_sites.push_back(site); - } - } - } - - if (!use_buildingplan) - { - size_t last_open_air_count = 0; - while (valid_building_sites.size() > 0 && open_air_sites.size() != last_open_air_count) - { - last_open_air_count = open_air_sites.size(); - deque current_open_air_list = open_air_sites; - open_air_sites.clear(); - for (deque::iterator it = current_open_air_list.begin(); it != current_open_air_list.end(); it++) - { - if (is_orthogonal_to_pending_construction(*it)) - valid_building_sites.push_back(*it); - else - open_air_sites.push_back(*it); - } - - } - } - - return valid_building_sites.size() > 0; -} - -static bool is_buildingplan_enabled() -{ - auto L = Lua::Core::State; - color_ostream_proxy out(Core::getInstance().getConsole()); - Lua::StackUnwinder top(L); - - if (!(lua_checkstack(L, 1) && - Lua::PushModulePublic(out, L, "plugins.buildingplan", "isEnabled") && - Lua::SafeCall(out, L, 0, 1))) - { - return false; - } - - return lua_toboolean(L, -1); -} - -static bool is_buildingplan_planmode_enabled( - df::building_type type, int16_t subtype, int32_t custom) -{ - auto L = Lua::Core::State; - color_ostream_proxy out(Core::getInstance().getConsole()); - Lua::StackUnwinder top(L); - - if (!lua_checkstack(L, 4) || - !Lua::PushModulePublic( - out, L, "plugins.buildingplan", "isPlanModeEnabled")) - return false; - - Lua::Push(L, type); - Lua::Push(L, subtype); - Lua::Push(L, custom); - - if (!Lua::SafeCall(out, L, 3, 1)) - return false; - - return lua_toboolean(L, -1); -} - -static bool is_buildingplan_managed() -{ - return is_buildingplan_enabled() && - is_buildingplan_planmode_enabled(ui_build_selector->building_type, - ui_build_selector->building_subtype, - ui_build_selector->custom_type); -} - -static bool build_with_buildingplan_box_select(const df::coord &pos) -{ - auto L = Lua::Core::State; - color_ostream_proxy out(Core::getInstance().getConsole()); - - CoreSuspendClaimer suspend; - Lua::StackUnwinder top(L); - - if (!lua_checkstack(L, 5) || - !Lua::PushModulePublic( - out, L, "plugins.automaterial", - "build_with_buildingplan_box_select")) - { - return false; - } - - Lua::Push(L, ui_build_selector->building_subtype); - Lua::Push(L, pos.x); - Lua::Push(L, pos.y); - Lua::Push(L, pos.z); - - if (!Lua::SafeCall(out, L, 4, 1)) - return false; - - return lua_toboolean(L, -1); -} - -static bool build_with_buildingplan_ui() -{ - auto L = Lua::Core::State; - color_ostream_proxy out(Core::getInstance().getConsole()); - - CoreSuspendClaimer suspend; - Lua::StackUnwinder top(L); - - return lua_checkstack(L, 1) && - Lua::PushModulePublic(out, L, "plugins.automaterial", - "build_with_buildingplan_ui") && - Lua::SafeCall(out, L, 0, 1); -} - -static bool designate_new_construction(df::coord &pos, df::construction_type &type, df::item *item) -{ - auto newinst = Buildings::allocInstance(pos, building_type::Construction, type); - if (!newinst) - return false; - - vector items; - items.push_back(item); - Maps::ensureTileBlock(pos); - - if (!Buildings::constructWithItems(newinst, items)) - { - delete newinst; - return false; - } - - return true; -} -//END Building and Verification - - -//START Viewscreen Hook -struct jobutils_hook : public df::viewscreen_dwarfmodest -{ - //START UI Methods - typedef df::viewscreen_dwarfmodest interpose_base; - - void send_key(const df::interface_key &key) - { - set< df::interface_key > keys; - keys.insert(key); - this->feed(&keys); - } - - bool select_material_at_index(size_t i) - { - ui_build_selector->sel_index = i; - std::set< df::interface_key > keys; - keys.insert(df::interface_key::SELECT_ALL); - this->feed(&keys); - return !in_material_choice_stage(); - } - - bool choose_materials() - { - if (!auto_choose_materials || get_curr_constr_prefs().size() == 0) - return false; - - size_t size = ui_build_selector->choices.size(); - for (size_t i = 0; i < size; i++) - { - MaterialDescriptor material = get_material_in_list(i); - size_t j; - if (is_material_in_autoselect(j, material)) - { - return select_material_at_index(i); - } - } - - return false; - } - - void draw_box_selection() - { - if (!box_select_enabled) - return; - - if (plotinfo->main.mode != df::ui_sidebar_mode::Build || - ui_build_selector->building_type != df::building_type::Construction) - return; - - df::coord vport = Gui::getViewportPos(); - - //Even if selection drawing is disabled, paint a green cursor as we can place box selection anywhere - - if (box_select_mode == SELECT_FIRST || (!show_box_selection && box_select_mode == SELECT_SECOND)) - { - int32_t x, y, z; - if (!Gui::getCursorCoords(x, y, z)) - return; - - x = x - vport.x + 1; - y = y - vport.y + 1; - OutputString(COLOR_GREEN, x, y, "X", false, 0, 0, true /* map */); - } - else if (show_box_selection && box_select_mode == SELECT_SECOND) - { - if (!Gui::getCursorCoords(box_second.x, box_second.y, box_second.z)) - return; - - Gui::DwarfmodeDims dims = Gui::getDwarfmodeViewDims(); - int32_t startx = std::max((int32_t)vport.x, std::min(box_first.x, box_second.x)); - int32_t endx = std::min(vport.x + dims.map_x2 - dims.map_x1, std::max(box_first.x, box_second.x)); - int32_t starty = std::max((int32_t)vport.y, std::min(box_first.y, box_second.y)); - int32_t endy = std::min(vport.y + dims.map_y2 - dims.map_y1, std::max(box_first.y, box_second.y)); - for (int32_t yB = starty; yB <= endy; ++yB) { - for (int32_t xB = startx; xB <= endx; ++xB) { - if (hollow_selection && !(xB == box_first.x || xB == box_second.x || yB == box_first.y || yB == box_second.y)) - continue; - - int8_t color = (xB == box_second.x && yB == box_second.y) ? COLOR_GREEN : COLOR_BROWN; - - int32_t x = xB - vport.x + 1; - int32_t y = yB - vport.y + 1; - OutputString(color, x, y, "X", false, 0, 0, true /* map */); - } - } - } - else if (show_box_selection && box_select_mode == SELECT_MATERIALS) - { - for (deque::iterator it = valid_building_sites.begin(); it != valid_building_sites.end(); it++) - { - int32_t x = it->pos.x - vport.x + 1; - int32_t y = it->pos.y - vport.y + 1; - OutputString(COLOR_GREEN, x, y, "X", false, 0, 0, true /* map */); - } - } - } - - void reset_existing_selection() - { - for (int i = 0; i < 10; i++) - { - send_key(df::interface_key::BUILDING_DIM_Y_DOWN); - send_key(df::interface_key::BUILDING_DIM_X_DOWN); - } - } - - void handle_input(set *input) - { - if (ui_build_selector->building_subtype >= 7) - return; - - if (in_material_choice_stage()) - { - if (input->count(interface_key::LEAVESCREEN)) - { - box_select_mode = SELECT_FIRST; - } - - MaterialDescriptor material = get_material_in_list(ui_build_selector->sel_index); - if (material.valid) - { - if (input->count(interface_key::SELECT) || input->count(interface_key::SELECT_ALL)) - { - if (get_last_moved_material().matches(material)) - last_used_moved = false; //Keep selected material on top - - set_last_used_material(material); - - if (box_select_enabled) - { - auto curr_index = ui_build_selector->sel_index; - vector gen_material; - gen_material.push_back(get_material_in_list(curr_index)); - box_select_materials.clear(); - // Populate material list with selected material - populate_box_materials(gen_material, ((input->count(interface_key::SELECT_ALL) && ui_build_selector->is_grouped) ? -1 : 1)); - - input->clear(); // Let the apply_box_selection routine allocate the construction - input->insert(interface_key::LEAVESCREEN); - } - } - else if (input->count(interface_key::CUSTOM_A)) - { - check_autoselect(material, true); - input->clear(); - } - } - } - else if (in_placement_stage()) - { - bool use_buildingplan = is_buildingplan_managed(); - - if (!use_buildingplan && input->count(interface_key::CUSTOM_A)) - { - auto_choose_materials = !auto_choose_materials; - } - else if (!use_buildingplan && input->count(interface_key::CUSTOM_T)) - { - revert_to_last_used_type = !revert_to_last_used_type; - } - else if (input->count(interface_key::CUSTOM_B)) - { - reset_existing_selection(); - box_select_enabled = !box_select_enabled; - if (!box_select_enabled) - cancel_box_selection(); - - return; - } - else if (!use_buildingplan && input->count(interface_key::CUSTOM_O)) - { - allow_future_placement = !allow_future_placement; - } - else if (input->count(interface_key::LEAVESCREEN)) - { - switch (box_select_mode) - { - case SELECT_FIRST: - case SELECT_SECOND: - cancel_box_selection(); - - default: - break; - } - } - else if (box_select_enabled) - { - if (input->count(interface_key::SELECT)) - { - switch (box_select_mode) - { - case SELECT_FIRST: - if (!Gui::getCursorCoords(box_first.x, box_first.y, box_first.z)) - { - cancel_box_selection(); - return; - } - box_select_mode = SELECT_SECOND; - if (!show_box_selection) - Gui::setDesignationCoords(box_first.x, box_first.y, box_first.z); - input->clear(); - return; - - case SELECT_SECOND: - if (!Gui::getCursorCoords(box_second.x, box_second.y, box_second.z)) - { - cancel_box_selection(); - return; - } - cancel_box_selection(); - input->clear(); - apply_box_selection(true); - return; - - default: - break; - } - } - else if (input->count(interface_key::CUSTOM_X)) - { - show_box_selection = !show_box_selection; - if (box_select_mode == SELECT_SECOND) - { - if (show_box_selection) - { - Gui::setDesignationCoords(-1, -1, -1); - } - else - { - Gui::setDesignationCoords(box_first.x, box_first.y, box_first.z); - } - } - } - else if (input->count(interface_key::CUSTOM_H)) - { - hollow_selection = !hollow_selection; - } - else if (input->count(interface_key::BUILDING_DIM_Y_UP) || - input->count(interface_key::BUILDING_DIM_Y_DOWN) || - input->count(interface_key::BUILDING_DIM_X_UP) || - input->count(interface_key::BUILDING_DIM_X_DOWN)) - { - input->clear(); - return; - } - } - else if (use_buildingplan - && ui_build_selector->errors.size() == 0 - && input->count(interface_key::SELECT)) - { - build_with_buildingplan_ui(); - Gui::refreshSidebar(); - input->clear(); - return; - } - } - } - //END UI Methods - - //START Building Application - bool populate_box_materials(vector &gen_materials, int32_t count = -1) - { - bool result = false; - - if (gen_materials.size() == 0) - return result; - - if (ui_build_selector->is_grouped) - send_key(interface_key::BUILDING_EXPAND_CONTRACT); - - size_t size = ui_build_selector->choices.size(); - vector::iterator gen_material; - for (size_t i = 0; i < size; i++) - { - if (VIRTUAL_CAST_VAR(spec, df::build_req_choice_specst, ui_build_selector->choices[i])) - { - for (gen_material = gen_materials.begin(); gen_material != gen_materials.end(); gen_material++) - { - if (gen_material->item_type == spec->candidate->getType() && - gen_material->item_subtype == spec->candidate->getSubtype() && - gen_material->type == spec->candidate->getActualMaterial() && - gen_material->index == spec->candidate->getActualMaterialIndex()) - { - box_select_materials.push_back(spec->candidate); - if (count > -1) - return true; // Right now we only support 1 or all materials - - result = true; - break; - } - } - } - } - send_key(interface_key::BUILDING_EXPAND_CONTRACT); - - return result; - } - - void move_cursor(df::coord &pos) - { - int32_t x, y, z; - Gui::getCursorCoords(x, y, z); - DEBUG(log).print("moving cursor from %d, %d, %d to %d, %d, %d\n", - x, y, z, pos.x, pos.y, pos.z); - - Gui::setCursorCoords(pos.x, pos.y, pos.z); - Gui::refreshSidebar(); - } - - void move_cursor(coord32_t &pos) - { - df::coord c((int16_t) pos.x, (int16_t) pos.y, (int16_t) pos.z); - move_cursor(c); - } - - void apply_box_selection(bool new_start) - { - static bool saved_revert_setting = false; - static bool auto_select_applied = false; - - bool use_buildingplan = is_buildingplan_managed(); - box_select_mode = SELECT_MATERIALS; - if (new_start) - { - bool ok_to_continue = false; - bool in_future_placement_mode = false; - if (!find_valid_building_sites(false, use_buildingplan)) - { - if (allow_future_placement) - { - in_future_placement_mode = find_valid_building_sites(true, use_buildingplan); - } - } - else - { - ok_to_continue = true; - } - - // if using buildingplan, we don't need an anchor - if (!use_buildingplan) - { - if (in_future_placement_mode) - { - ok_to_continue = - find_anchor_in_spiral(valid_building_sites[0].pos); - } - else if (ok_to_continue) - { - // First valid site is guaranteed to be anchored, either on - // a tile or against a valid orthogonal tile - // Use it as an anchor point to generate materials list - anchor = valid_building_sites.front(); - valid_building_sites.pop_front(); - valid_building_sites.push_back(anchor); - } - } - - if (!ok_to_continue) - { - cancel_box_selection(); - hollow_selection = false; - return; - } - - saved_revert_setting = revert_to_last_used_type; - revert_to_last_used_type = true; - auto_select_applied = false; - box_select_materials.clear(); - - } - - while (valid_building_sites.size() > 0) - { - building_site site = valid_building_sites.front(); - valid_building_sites.pop_front(); - - if (use_buildingplan) - { - // we don't actually care if this fails. buildingplan will return - // false when it filters out bad tiles, and that's ok. - build_with_buildingplan_box_select(site.pos); - continue; - } - - if (box_select_materials.size() > 0) - { - df::construction_type type = (df::construction_type) ui_build_selector->building_subtype; - df::item *item = NULL; - while (box_select_materials.size() > 0) - { - item = box_select_materials.front(); - if (!item->flags.bits.in_job) - break; - box_select_materials.pop_front(); - item = NULL; - } - - if (item != NULL) - { - if (designate_new_construction(site.pos, type, item)) - { - box_select_materials.pop_front(); - box_select_mode = AUTOSELECT_MATERIALS; - send_key(interface_key::LEAVESCREEN); //Must do this to register items in use - send_key(hotkeys[type]); - box_select_mode = SELECT_MATERIALS; - } - continue; - } - } - - // Generate material list using regular construction placement routine - - if (site.in_open_air) - { - // Cannot invoke material selection on an unconnected tile, use anchor instead - move_cursor(anchor.pos); - send_key(df::interface_key::SELECT); - } - - move_cursor(site.pos); - - if (!site.in_open_air) - send_key(df::interface_key::SELECT); - - if (in_material_choice_stage()) - { - valid_building_sites.push_front(site); //Redo current tile with whatever gets selected - if (!auto_select_applied) - { - // See if any auto select materials are available - auto_select_applied = true; - if (auto_choose_materials && populate_box_materials(preferred_materials[ui_build_selector->building_subtype])) - { - continue; - } - } - - last_used_moved = false; - return; // No auto select materials left, ask user - } - } - - // Allocation done, reset - move_cursor(box_second); - - // if we're using buildingplan, we never actually leave the placement - // screen, so there's no need to re-enter the screen - revert_to_last_used_type = saved_revert_setting; - if (!use_buildingplan && !revert_to_last_used_type) - { - send_key(df::interface_key::LEAVESCREEN); - } - - cancel_box_selection(); - hollow_selection = false; - ignore_selection = 0; - } - //END Building Application - - DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) - { - if (ignore_selection < SELECTION_IGNORE_TICKS) - { - //FIXME: Sometimes there's an extra ENTER key left over after box selection - ignore_selection = SELECTION_IGNORE_TICKS; - return; - } - - if (box_select_mode != AUTOSELECT_MATERIALS) - handle_input(input); - - int16_t last_used_constr_subtype = (in_material_choice_stage()) ? ui_build_selector->building_subtype : -1; - INTERPOSE_NEXT(feed)(input); - - if (revert_to_last_used_type && - last_used_constr_subtype >= 0 && - in_type_choice_stage() && - hotkeys.find(last_used_constr_subtype) != hotkeys.end()) - { - input->clear(); - input->insert(hotkeys[last_used_constr_subtype]); - INTERPOSE_NEXT(feed)(input); - - if (box_select_mode == SELECT_MATERIALS) - { - apply_box_selection(false); - } - } - } - - DEFINE_VMETHOD_INTERPOSE(void, render, ()) - { - if (ignore_selection < SELECTION_IGNORE_TICKS) - { - ++ignore_selection; - } - - if (in_material_choice_stage()) - { - if (!last_used_moved && ui_build_selector->is_grouped) - { - last_used_moved = true; - if (!box_select_enabled && choose_materials()) - { - return; - } - else - { - move_material_to_top(get_last_used_material()); - } - } - else if (!ui_build_selector->is_grouped) - { - last_used_moved = false; - } - } - else - { - last_used_moved = false; - } - - INTERPOSE_NEXT(render)(); - - draw_box_selection(); - - if (in_type_choice_stage()) - { - cancel_box_selection(); - return; - } - - auto dims = Gui::getDwarfmodeViewDims(); - int left_margin = dims.menu_x1 + 1; - int x = left_margin; - int y = 25; - if (in_material_choice_stage()) - { - MaterialDescriptor material = get_material_in_list(ui_build_selector->sel_index); - if (material.valid) - { - AMOutputToggleString(x, y, "Autoselect", "a", check_autoselect(material, false), true, left_margin); - - if (box_select_mode == SELECT_MATERIALS) - { - ++y; - OutputString(COLOR_BROWN, x, y, "Construction:", true, left_margin); - OutputString(COLOR_WHITE, x, y, int_to_string(valid_building_sites.size()) + " tiles to fill", true, left_margin); - } - } - } - else if (in_placement_stage() && ui_build_selector->building_subtype < 7) - { - bool use_buildingplan = is_buildingplan_managed(); - - OutputString(COLOR_BROWN, x, y, "DFHack Automaterial Options", true, left_margin); - if (use_buildingplan) - { - y += 2; - } - else - { - AMOutputToggleString(x, y, "Auto Mat-select", "a", auto_choose_materials, true, left_margin); - AMOutputToggleString(x, y, "Reselect Type", "t", revert_to_last_used_type, true, left_margin); - } - - ++y; - AMOutputToggleString(x, y, "Box Select", "b", box_select_enabled, true, left_margin); - if (box_select_enabled) - { - AMOutputToggleString(x, y, "Show Box Mask", "x", show_box_selection, true, left_margin); - OutputHotkeyString(x, y, (hollow_selection) ? "Make Solid" : "Make Hollow", "h", true, left_margin); - - if (use_buildingplan) - ++y; - else - AMOutputToggleString(x, y, "Open Placement", "o", allow_future_placement, true, left_margin); - } - else - { - y += 3; - } - y += 2; - if (is_buildingplan_enabled()) - OutputString(COLOR_BROWN, x, y, "DFHack Buildingplan Options", true, left_margin); - - if (box_select_enabled) - { - Screen::Pen pen(' ',COLOR_BLACK); - y = dims.y1 + 2; - Screen::fillRect(pen, x, y, dims.menu_x2, y + 17); - - y += 2; - switch (box_select_mode) - { - case SELECT_FIRST: - OutputString(COLOR_BROWN, x, y, "Choose first corner", true, left_margin); - break; - - case SELECT_SECOND: - { - OutputString(COLOR_GREEN, x, y, "Choose second corner", true, left_margin); - - int32_t curr_x, curr_y, curr_z; - Gui::getCursorCoords(curr_x, curr_y, curr_z); - int dX = abs(box_first.x - curr_x) + 1; - int dY = abs(box_first.y - curr_y) + 1; - stringstream label; - label << "Selection: " << dX << "x" << dY; - OutputString(COLOR_WHITE, x, ++y, label.str(), true, left_margin); - - df::coord vport = Gui::getViewportPos(); - int cx = box_first.x - vport.x + 1; - int cy = box_first.y - vport.y + 1; - - Gui::DwarfmodeDims dims = Gui::getDwarfmodeViewDims(); - if (cx >= 1 && cx <= dims.map_x2 && cy >= 1 && cy <= dims.map_y2) - OutputString(COLOR_BROWN, cx, cy, "X", false, 0, 0, true /* map */); - break; - } - - default: - break; - } - - OutputString(COLOR_BROWN, x, ++y, "Ignore Building Restrictions", true, left_margin); - } - } - } -}; -//END Viewscreen Hook - -color_ostream_proxy console_out(Core::getInstance().getConsole()); - - -IMPLEMENT_VMETHOD_INTERPOSE(jobutils_hook, feed); -IMPLEMENT_VMETHOD_INTERPOSE(jobutils_hook, render); - -DFHACK_PLUGIN_IS_ENABLED(is_enabled); - -DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable) -{ - if (!gps) - return CR_FAILURE; - - if (enable != is_enabled) - { - if (!INTERPOSE_HOOK(jobutils_hook, feed).apply(enable) || - !INTERPOSE_HOOK(jobutils_hook, render).apply(enable)) - return CR_FAILURE; - - is_enabled = enable; - } - - return CR_OK; -} - -DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) -{ - hotkeys[construction_type::Wall] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_WALL; - hotkeys[construction_type::Floor] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_FLOOR; - hotkeys[construction_type::Ramp] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_RAMP; - hotkeys[construction_type::UpStair] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_STAIR_UP; - hotkeys[construction_type::DownStair] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_STAIR_DOWN; - hotkeys[construction_type::UpDownStair] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_STAIR_UPDOWN; - hotkeys[construction_type::Fortification] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_FORTIFICATION; - //Ignore tracks, DF already returns to track menu - - return CR_OK; -} diff --git a/plugins/lua/automaterial.lua b/plugins/lua/automaterial.lua deleted file mode 100644 index 1cd7e9faf..000000000 --- a/plugins/lua/automaterial.lua +++ /dev/null @@ -1,23 +0,0 @@ -local _ENV = mkmodule('plugins.automaterial') - -local buildingplan = require('plugins.buildingplan') - --- construct the building and register it with buildingplan for item selection -function build_with_buildingplan_box_select(subtype, x, y, z) - local pos = xyz2pos(x, y, z) - local bld, err = dfhack.buildings.constructBuilding{ - type=df.building_type.Construction, subtype=subtype, pos=pos} - -- it's not a user error if we can't place a building here; just indicate - -- that no building was placed by returning false. - if err then return false end - buildingplan.addPlannedBuilding(bld) - return true -end - -function build_with_buildingplan_ui() - for _,bld in ipairs(buildingplan.construct_buildings_from_ui_state()) do - buildingplan.addPlannedBuilding(bld) - end -end - -return _ENV From 1eaee610447244784c5bca8dc20dd700cde5ceb5 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Mon, 6 Mar 2023 11:46:43 -0800 Subject: [PATCH 03/28] Fixes dig-now job list iteration/scanning --- plugins/dig-now.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/dig-now.cpp b/plugins/dig-now.cpp index 037e7ae84..be431722b 100644 --- a/plugins/dig-now.cpp +++ b/plugins/dig-now.cpp @@ -85,10 +85,11 @@ public: df::job_list_link* node = df::global::world->jobs.list.next; while (node) { df::job* job = node->item; + node = node->next; + if(!job || !Maps::isValidTilePos(job->pos)) continue; - node = node->next; df::tile_designation td = map.designationAt(job->pos); df::tile_occupancy to = map.occupancyAt(job->pos); const auto ctd = td.whole; From 0459831c6a9b86371813ef8072d01593fc826d69 Mon Sep 17 00:00:00 2001 From: Myk Date: Mon, 6 Mar 2023 12:33:02 -0800 Subject: [PATCH 04/28] Revert "revert recent changes to dig-now due to lockups" --- docs/changelog.txt | 2 + plugins/dig-now.cpp | 250 ++++++++++++++++++++++++++++++++++++-------- 2 files changed, 207 insertions(+), 45 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 4a3428031..7cec154f3 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -66,6 +66,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `autodump`: changed behaviour to only change ``dump`` and ``forbid`` flags if an item is successfully dumped. -@ `autochop`: generate default names for burrows with no assigned names - ``Buildings::StockpileIterator``: fix check for stockpile items on block boundary. +- `dig-now`: fixed multi-layer channel designations only channeling every second layer - `tailor`: block making clothing sized for toads; make replacement clothing orders use the size of the wearer, not the size of the garment -@ `confirm`: fix fps drop when enabled - `channel-safely`: fix an out of bounds error regarding the REPORT event listener receiving (presumably) stale id's @@ -76,6 +77,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `showmood`: now shows the number of items needed for cloth and bars in addition to the technically correct but always confusing "total dimension" (150 per bar or 10,000 per cloth) -@ Stopped mouse clicks from affecting the map when a click on a DFHack screen dismisses the window - `confirm`: configuration data is now persisted globally. +- `dig-now`: added handling of dig designations that have been converted into active jobs - `tailor`: add support for adamantine cloth (off by default); improve logging ## API diff --git a/plugins/dig-now.cpp b/plugins/dig-now.cpp index 6227f44f5..e94cb41da 100644 --- a/plugins/dig-now.cpp +++ b/plugins/dig-now.cpp @@ -6,6 +6,7 @@ #include "PluginManager.h" #include "TileTypes.h" #include "LuaTools.h" +#include "Debug.h" #include "modules/Buildings.h" #include "modules/Gui.h" @@ -14,6 +15,8 @@ #include "modules/Random.h" #include "modules/Units.h" #include "modules/World.h" +#include "modules/EventManager.h" +#include "modules/Job.h" #include #include @@ -26,12 +29,129 @@ #include #include +#include +#include +#include + DFHACK_PLUGIN("dig-now"); REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(world); +// Debugging +namespace DFHack { + DBG_DECLARE(dignow, general, DebugCategory::LINFO); + DBG_DECLARE(dignow, channels, DebugCategory::LINFO); +} + +#define COORD "%" PRIi16 " %" PRIi16 " %" PRIi16 +#define COORDARGS(id) id.x, id.y, id.z + using namespace DFHack; +struct designation{ + df::coord pos; + df::tile_designation type; + df::tile_occupancy occupancy; + designation() = default; + designation(const df::coord &c, const df::tile_designation &td, const df::tile_occupancy &to) : pos(c), type(td), occupancy(to) {} + + bool operator==(const designation &rhs) const { + return pos == rhs.pos; + } + + bool operator!=(const designation &rhs) const { + return !(rhs == *this); + } +}; + +namespace std { + template<> + struct hash { + std::size_t operator()(const designation &c) const { + std::hash hash_coord; + return hash_coord(c.pos); + } + }; +} + +class DesignationJobs { +private: + std::unordered_map designations; + std::unordered_map jobs; +public: + void load(MapExtras::MapCache &map) { + designations.clear(); + df::job_list_link* node = df::global::world->jobs.list.next; + while (node) { + df::job* job = node->item; + if(!job || !Maps::isValidTilePos(job->pos)) + continue; + + node = node->next; + df::tile_designation td = map.designationAt(job->pos); + df::tile_occupancy to = map.occupancyAt(job->pos); + const auto ctd = td.whole; + const auto cto = to.whole; + switch (job->job_type){ + case job_type::Dig: + td.bits.dig = tile_dig_designation::Default; + break; + case job_type::DigChannel: + td.bits.dig = tile_dig_designation::Channel; + break; + case job_type::CarveRamp: + td.bits.dig = tile_dig_designation::Ramp; + break; + case job_type::CarveUpwardStaircase: + td.bits.dig = tile_dig_designation::UpStair; + break; + case job_type::CarveDownwardStaircase: + td.bits.dig = tile_dig_designation::DownStair; + break; + case job_type::CarveUpDownStaircase: + td.bits.dig = tile_dig_designation::UpDownStair; + break; + case job_type::DetailWall: + case job_type::DetailFloor: { + df::tiletype tt = map.tiletypeAt(job->pos); + if (tileSpecial(tt) != df::tiletype_special::SMOOTH) { + td.bits.smooth = 1; + } + break; + } + case job_type::CarveTrack: + to.bits.carve_track_north = (job->item_category.whole >> 18) & 1; + to.bits.carve_track_south = (job->item_category.whole >> 19) & 1; + to.bits.carve_track_west = (job->item_category.whole >> 20) & 1; + to.bits.carve_track_east = (job->item_category.whole >> 21) & 1; + break; + default: + break; + } + if (ctd != td.whole || cto != to.whole) { + // we found a designation job + designations.emplace(job->pos, designation(job->pos, td, to)); + jobs.emplace(job->pos, job); + } + } + } + void remove(const df::coord &pos) { + if(jobs.count(pos)) { + Job::removeJob(jobs[pos]); + jobs.erase(pos); + } + } + designation get(const df::coord &pos) { + if (designations.count(pos)) { + return designations[pos]; + } + return {}; + } + bool count(const df::coord &pos) { + return jobs.count(pos); + } +}; + struct boulder_percent_options { // percent chance ([0..100]) for creating a boulder for the given rock type uint32_t layer; @@ -320,8 +440,19 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, std::vector &dug_tiles) { df::tiletype tt = map.tiletypeAt(pos); - if (!is_diggable(map, pos, tt)) + if (!is_diggable(map, pos, tt)) { + DEBUG(general).print("dig_tile: not diggable\n"); return false; + } + + /** The algorithm process seems to be: + * for each tile + * check for a designation + * if a designation exists send it to dig_tile + * + * dig_tile (below) then digs the layer below the channel designated tile + * thereby changing it and causing its designation to be lost + * */ df::tiletype target_type = df::tiletype::Void; switch(designation) { @@ -341,19 +472,22 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, DFCoord pos_below(pos.x, pos.y, pos.z-1); if (can_dig_channel(tt) && map.ensureBlockAt(pos_below) && is_diggable(map, pos_below, map.tiletypeAt(pos_below))) { + TRACE(channels).print("dig_tile: channeling at (" COORD ") [can_dig_channel: true]\n",COORDARGS(pos_below)); target_type = df::tiletype::OpenSpace; DFCoord pos_above(pos.x, pos.y, pos.z+1); - if (map.ensureBlockAt(pos_above)) + if (map.ensureBlockAt(pos_above)) { remove_ramp_top(map, pos_above); - df::tile_dig_designation td_below = - map.designationAt(pos_below).bits.dig; - if (dig_tile(out, map, pos_below, - df::tile_dig_designation::Ramp, dug_tiles)) { + } + df::tile_dig_designation td_below = map.designationAt(pos_below).bits.dig; + if (dig_tile(out, map, pos_below, df::tile_dig_designation::Ramp, dug_tiles)) { clean_ramps(map, pos_below); - if (td_below == df::tile_dig_designation::Default) + if (td_below == df::tile_dig_designation::Default) { dig_tile(out, map, pos_below, td_below, dug_tiles); + } return true; } + } else { + DEBUG(channels).print("dig_tile: failed to channel at (" COORD ") [can_dig_channel: false]\n", COORDARGS(pos_below)); } break; } @@ -407,7 +541,8 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, if (target_type == df::tiletype::Void || target_type == tt) return false; - dug_tiles.push_back(dug_tile_info(map, pos)); + dug_tiles.emplace_back(map, pos); + TRACE(general).print("dig_tile: digging the designation tile at (" COORD ")\n",COORDARGS(pos)); dig_type(map, pos, target_type); // let light filter down to newly exposed tiles @@ -594,9 +729,12 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, item_coords_t &item_coords, const dig_now_options &options) { MapExtras::MapCache map; Random::MersenneRNG rng; + DesignationJobs jobs; + jobs.load(map); rng.init(); + std::unordered_set buffer; // go down levels instead of up so stacked ramps behave as expected for (int16_t z = options.end.z; z >= options.start.z; --z) { for (int16_t y = options.start.y; y <= options.end.y; ++y) { @@ -609,46 +747,68 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, DFCoord pos(x, y, z); df::tile_designation td = map.designationAt(pos); df::tile_occupancy to = map.occupancyAt(pos); - if (td.bits.dig != df::tile_dig_designation::No && - !to.bits.dig_marked) { - std::vector dug_tiles; - if (dig_tile(out, map, pos, td.bits.dig, dug_tiles)) { - for (auto info : dug_tiles) { - td = map.designationAt(info.pos); - td.bits.dig = df::tile_dig_designation::No; - map.setDesignationAt(info.pos, td); - - dug_coords.push_back(info.pos); - refresh_adjacent_smooth_walls(map, info.pos); - if (info.imat < 0) - continue; - if (produces_item(options.boulder_percents, - map, rng, info)) { - auto k = std::make_pair(info.itype, info.imat); - item_coords[k].push_back(info.pos); - } - } - } - } else if (td.bits.smooth == 1) { - if (smooth_tile(out, map, pos)) { - td = map.designationAt(pos); - td.bits.smooth = 0; - map.setDesignationAt(pos, td); - } - } else if (to.bits.carve_track_north == 1 - || to.bits.carve_track_east == 1 - || to.bits.carve_track_south == 1 - || to.bits.carve_track_west == 1) { - if (carve_tile(map, pos, to)) { - to = map.occupancyAt(pos); - to.bits.carve_track_north = 0; - to.bits.carve_track_east = 0; - to.bits.carve_track_south = 0; - to.bits.carve_track_west = 0; - map.setOccupancyAt(pos, to); + if (jobs.count(pos)) { + buffer.emplace(jobs.get(pos)); + jobs.remove(pos); + // if it does get removed, then we're gonna buffer the jobs info then remove the job + } else if ((td.bits.dig != df::tile_dig_designation::No && !to.bits.dig_marked) + || td.bits.smooth == 1 + || to.bits.carve_track_north == 1 + || to.bits.carve_track_east == 1 + || to.bits.carve_track_south == 1 + || to.bits.carve_track_west == 1) { + + // we're only buffering designations, so that processing doesn't affect what we're buffering + buffer.emplace(pos, td, to); + } + } + } + } + + // process designations + for(auto &d : buffer) { + auto pos = d.pos; + auto td = d.type; + auto to = d.occupancy; + + if (td.bits.dig != df::tile_dig_designation::No && !to.bits.dig_marked) { + std::vector dug_tiles; + + if (dig_tile(out, map, pos, td.bits.dig, dug_tiles)) { + for (auto info: dug_tiles) { + td = map.designationAt(info.pos); + td.bits.dig = df::tile_dig_designation::No; + map.setDesignationAt(info.pos, td); + + dug_coords.push_back(info.pos); + refresh_adjacent_smooth_walls(map, info.pos); + if (info.imat < 0) + continue; + if (produces_item(options.boulder_percents, + map, rng, info)) { + auto k = std::make_pair(info.itype, info.imat); + item_coords[k].push_back(info.pos); } } } + } else if (td.bits.smooth == 1) { + if (smooth_tile(out, map, pos)) { + td = map.designationAt(pos); + td.bits.smooth = 0; + map.setDesignationAt(pos, td); + } + } else if (to.bits.carve_track_north == 1 + || to.bits.carve_track_east == 1 + || to.bits.carve_track_south == 1 + || to.bits.carve_track_west == 1) { + if (carve_tile(map, pos, to)) { + to = map.occupancyAt(pos); + to.bits.carve_track_north = 0; + to.bits.carve_track_east = 0; + to.bits.carve_track_south = 0; + to.bits.carve_track_west = 0; + map.setOccupancyAt(pos, to); + } } } From 8cb3c0cf66e06fa128c98832e370cf7b239490e4 Mon Sep 17 00:00:00 2001 From: Myk Date: Mon, 6 Mar 2023 12:38:14 -0800 Subject: [PATCH 05/28] Update changelog.txt --- docs/changelog.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 7cec154f3..f58dea0b3 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,8 +38,10 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes -@ ``widgets.HotkeyLabel``: don't trigger on click if the widget is disabled - ``dfhack.job.isSuitableMaterial``: now properly detects lack of fire and magma safety for vulnerable materials with high melting points +- `dig-now`: fixed multi-layer channel designations only channeling every second layer ## Misc Improvements +- `dig-now`: added handling of dig designations that have been converted into active jobs ## Documentation @@ -66,7 +68,6 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `autodump`: changed behaviour to only change ``dump`` and ``forbid`` flags if an item is successfully dumped. -@ `autochop`: generate default names for burrows with no assigned names - ``Buildings::StockpileIterator``: fix check for stockpile items on block boundary. -- `dig-now`: fixed multi-layer channel designations only channeling every second layer - `tailor`: block making clothing sized for toads; make replacement clothing orders use the size of the wearer, not the size of the garment -@ `confirm`: fix fps drop when enabled - `channel-safely`: fix an out of bounds error regarding the REPORT event listener receiving (presumably) stale id's @@ -77,7 +78,6 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `showmood`: now shows the number of items needed for cloth and bars in addition to the technically correct but always confusing "total dimension" (150 per bar or 10,000 per cloth) -@ Stopped mouse clicks from affecting the map when a click on a DFHack screen dismisses the window - `confirm`: configuration data is now persisted globally. -- `dig-now`: added handling of dig designations that have been converted into active jobs - `tailor`: add support for adamantine cloth (off by default); improve logging ## API From 1c36031073a7152e3879a956eff8bd33964087d2 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 6 Mar 2023 15:18:06 -0800 Subject: [PATCH 06/28] update all build envs to ubuntu-22.04 --- .github/workflows/build.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 595bde8ed..024a8fe6b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,15 +10,14 @@ jobs: fail-fast: false matrix: os: - - ubuntu-18.04 + - ubuntu-22.04 gcc: - 4.8 - 7 plugins: - default include: - - os: ubuntu-22.04 - gcc: 12 + - gcc: 12 plugins: all steps: - name: Set up Python 3 @@ -159,7 +158,7 @@ jobs: path: build/win64-cross/output/* docs: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - name: Set up Python 3 uses: actions/setup-python@v2 @@ -182,7 +181,7 @@ jobs: path: docs/html lint: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - name: Set up Python 3 uses: actions/setup-python@v2 From 3569f7e6877b9f2990a23c3da68ce561d3c38795 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 6 Mar 2023 15:22:15 -0800 Subject: [PATCH 07/28] update ruby action --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 024a8fe6b..f3c133bab 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -188,7 +188,7 @@ jobs: with: python-version: 3 - name: Set up Ruby 2.7 - uses: actions/setup-ruby@v1 + uses: ruby/setup-ruby@v1 with: ruby-version: 2.7 - name: Install Lua From 363a3d484eac74b34c87b9db45ab0cc901876994 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 6 Mar 2023 15:23:45 -0800 Subject: [PATCH 08/28] fix matrix build? --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f3c133bab..49333f110 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,7 +17,8 @@ jobs: plugins: - default include: - - gcc: 12 + - os: ubuntu-22.04 + gcc: 12 plugins: all steps: - name: Set up Python 3 From 1bf79afd54fa815cd968231e477a2dc638b22cbe Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 6 Mar 2023 15:29:11 -0800 Subject: [PATCH 09/28] ditch gcc 4.8 and 7, replace with 10 --- .github/workflows/build.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 49333f110..abee9a86a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,8 +12,7 @@ jobs: os: - ubuntu-22.04 gcc: - - 4.8 - - 7 + - 10 plugins: - default include: From bc77a91d46bddc335ea9a63b34bd44e4efed56b3 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 6 Mar 2023 20:51:41 -0800 Subject: [PATCH 10/28] build zero-item buildings with buildingplan so they can be multi-built like all other buildings (the vanilla ui was handling the build and then exiting out of the build menu) --- plugins/lua/buildingplan.lua | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index a666103e5..0fcfd4c72 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -1713,9 +1713,6 @@ function PlannerOverlay:onInput(keys) if is_choosing_area() or cur_building_has_no_area() then local filters = get_cur_filters() local num_filters = #filters - if num_filters == 0 then - return false -- we don't add value; let the game place it - end local choose = self.subviews.choose if choose.enabled() and choose:getOptionValue() then self:save_placement() From 26a257eb8c0cb4c684483e7a443fd6a9cdaa42d5 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Tue, 7 Mar 2023 07:14:18 +0000 Subject: [PATCH 11/28] Auto-update submodules library/xml: master --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 8ae81f8d8..9ed162333 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 8ae81f8d8f1f96d82b9074b205073bb8e8d29f96 +Subproject commit 9ed16233380857a145b276bfa62a89ea515c0514 From fc4d2605f205d55dd1ccaa0e5257cc79adca0e44 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 7 Mar 2023 08:00:13 -0800 Subject: [PATCH 12/28] don't softlock the game on modal popups overlays can prevent clicks from being handled by DF. likewise, vanilla modal popups can prevent clicks from getting handled by DFHack. to prevent a softlock, overlay will skip sending input to the overlay widgets when a modal dialog is visible --- plugins/overlay.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/plugins/overlay.cpp b/plugins/overlay.cpp index 4c7384f3b..8e27c8308 100644 --- a/plugins/overlay.cpp +++ b/plugins/overlay.cpp @@ -13,6 +13,7 @@ #include "df/viewscreen_titlest.h" #include "df/viewscreen_update_regionst.h" #include "df/viewscreen_worldst.h" +#include "df/world.h" #include "Debug.h" #include "LuaTools.h" @@ -27,6 +28,8 @@ using namespace DFHack; DFHACK_PLUGIN("overlay"); DFHACK_PLUGIN_IS_ENABLED(is_enabled); +REQUIRE_GLOBAL(world); + namespace DFHack { DBG_DECLARE(overlay, control, DebugCategory::LINFO); DBG_DECLARE(overlay, event, DebugCategory::LINFO); @@ -67,13 +70,15 @@ struct viewscreen_overlay : T { } DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set *input)) { bool input_is_handled = false; - call_overlay_lua(NULL, "feed_viewscreen_widgets", 2, 1, - [&](lua_State *L) { - Lua::Push(L, T::_identity.getName()); - Lua::PushInterfaceKeys(L, *input); - }, [&](lua_State *L) { - input_is_handled = lua_toboolean(L, -1); - }); + // don't send input to the overlays if there is a modal dialog up + if (!world->status.popups.size()) + call_overlay_lua(NULL, "feed_viewscreen_widgets", 2, 1, + [&](lua_State *L) { + Lua::Push(L, T::_identity.getName()); + Lua::PushInterfaceKeys(L, *input); + }, [&](lua_State *L) { + input_is_handled = lua_toboolean(L, -1); + }); if (!input_is_handled) INTERPOSE_NEXT(feed)(input); } From fccd4cdec59232957ec222afaf672c8a68fe8d35 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 7 Mar 2023 17:13:41 -0800 Subject: [PATCH 13/28] add steam deploy workflow --- .github/workflows/steam.yml | 57 +++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 .github/workflows/steam.yml diff --git a/.github/workflows/steam.yml b/.github/workflows/steam.yml new file mode 100644 index 000000000..683b97fe7 --- /dev/null +++ b/.github/workflows/steam.yml @@ -0,0 +1,57 @@ +name: Deploy to Steam + +on: + workflow_dispatch: + inputs: + commit_hash: + description: Commit hash + type: string + required: true + version: + description: Version + type: string + required: true + release_channel: + description: Release channel + type: string + required: true + default: beta + +jobs: + deploy-to-steam: + name: Deploy to Steam + runs-on: ubuntu-22.04 + steps: + - name: Clone DFHack + uses: actions/checkout@v3 + with: + submodules: true + fetch-depth: 0 + ref: ${{ github.event.inputs.commit_hash }} + - name: Fetch ccache + uses: actions/cache@v3 + with: + path: build/win64-cross/ccache + key: ccache-win64-cross-msvc-${{ github.event.inputs.commit_hash }} + restore-keys: | + ccache-win64-cross-msvc-${{ github.event.inputs.commit_hash }} + ccache-win64-cross-msvc + - name: Cross-compile win64 artifacts + env: + CMAKE_EXTRA_ARGS: '-DBUILD_STONESENSE:BOOL=1' + run: | + cd build + bash -x build-win64-from-linux.sh + - name: Steam deploy + uses: game-ci/steam-deploy@v2 + with: + username: ${{ secrets.STEAM_USERNAME }} + password: ${{ secrets.STEAM_PASSWORD }} + configVdf: ${{ secrets.STEAM_CONFIG_VDF}} + ssfnFileName: ${{ secrets.STEAM_SSFN_FILE_NAME }} + ssfnFileContents: ${{ secrets.STEAM_SSFN_FILE_CONTENTS }} + appId: 2346660 + buildDescription: ${{ github.event.inputs.version }} + rootPath: build/win64-cross/output + depot1Path: dfhack-windows-x64 + releaseBranch: ${{ github.event.inputs.release_channel }} From 980f6734540b211c21086010d88af4d66efc85b1 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 7 Mar 2023 17:34:44 -0800 Subject: [PATCH 14/28] fix depot file path --- .github/workflows/steam.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/steam.yml b/.github/workflows/steam.yml index 683b97fe7..035caf805 100644 --- a/.github/workflows/steam.yml +++ b/.github/workflows/steam.yml @@ -52,6 +52,6 @@ jobs: ssfnFileContents: ${{ secrets.STEAM_SSFN_FILE_CONTENTS }} appId: 2346660 buildDescription: ${{ github.event.inputs.version }} - rootPath: build/win64-cross/output - depot1Path: dfhack-windows-x64 + rootPath: build + depot1Path: win64-cross/output releaseBranch: ${{ github.event.inputs.release_channel }} From f974ac043f994e870741af49d07e0f18c7a3cfd6 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 7 Mar 2023 22:13:08 -0800 Subject: [PATCH 15/28] add launchdf binary so steam has an exe to launch --- CMakeLists.txt | 2 ++ package/windows/CMakeLists.txt | 7 ++++++ package/windows/launchdf.c | 42 ++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 package/windows/CMakeLists.txt create mode 100644 package/windows/launchdf.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 56c81ba72..1fb43fa07 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -615,3 +615,5 @@ if(BUILD_SIZECHECK) add_subdirectory(depends/sizecheck) add_dependencies(dfhack sizecheck) endif() + +add_subdirectory(package/windows) diff --git a/package/windows/CMakeLists.txt b/package/windows/CMakeLists.txt new file mode 100644 index 000000000..a5877f117 --- /dev/null +++ b/package/windows/CMakeLists.txt @@ -0,0 +1,7 @@ +project(package_windows) + +if(WIN32) + add_executable(launchdf WIN32 launchdf.c) + install(TARGETS launchdf + DESTINATION ${DFHACK_DATA_DESTINATION}) +endif() diff --git a/package/windows/launchdf.c b/package/windows/launchdf.c new file mode 100644 index 000000000..44a2d4d23 --- /dev/null +++ b/package/windows/launchdf.c @@ -0,0 +1,42 @@ +#include + +int WINAPI wWinMain(HINSTANCE hi, HINSTANCE hpi, PWSTR cmd, int ns) +{ + STARTUPINFOA si; + PROCESS_INFORMATION pi; + + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + + CHAR dfdir[1024]; + + if (GetFullPathNameA("..", 1024, dfdir, NULL) == 0) + { + MessageBoxA(NULL, "could not get current directory", NULL, 0); + exit(1); + } + + if (SetCurrentDirectoryA(dfdir) == 0) + { + MessageBoxA(NULL, "could not change to DF directory", NULL, 0); + exit(1); + } + + if (CreateProcessA("Dwarf Fortress.exe", + NULL, + NULL, + NULL, + FALSE, + 0, + NULL, + NULL, + &si, + &pi) == 0) + { + MessageBoxA(NULL, "could not launch 'Dwarf Fortress.exe'", NULL, 0); + exit(1); + } + + exit(0); +} From 787844e8e6de472b63947292d6cc250356e18572 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 7 Mar 2023 22:27:50 -0800 Subject: [PATCH 16/28] fix ccache restore key --- .github/workflows/steam.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/steam.yml b/.github/workflows/steam.yml index 035caf805..aae1621af 100644 --- a/.github/workflows/steam.yml +++ b/.github/workflows/steam.yml @@ -34,7 +34,7 @@ jobs: path: build/win64-cross/ccache key: ccache-win64-cross-msvc-${{ github.event.inputs.commit_hash }} restore-keys: | - ccache-win64-cross-msvc-${{ github.event.inputs.commit_hash }} + ccache-win64-cross-msvc-develop-${{ github.event.inputs.commit_hash }} ccache-win64-cross-msvc - name: Cross-compile win64 artifacts env: From fcfe7e4ef86b3381856224e29872c239e532c2d3 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Wed, 8 Mar 2023 07:15:19 +0000 Subject: [PATCH 17/28] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 9ed162333..09ddae3c2 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 9ed16233380857a145b276bfa62a89ea515c0514 +Subproject commit 09ddae3c2851f207c7257b4767182f4c6c938f18 diff --git a/scripts b/scripts index 288b38c9e..c8ceb198b 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 288b38c9e9d8fabf1a0934ffbe23104274c39883 +Subproject commit c8ceb198b4fc62155b31a5554b9c96cc6bcb8d55 From eee911b807a6b1463e2e92412af1e3de3d2d8838 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 8 Mar 2023 00:00:22 -0800 Subject: [PATCH 18/28] remove changedir, cwd is already root --- package/windows/launchdf.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/package/windows/launchdf.c b/package/windows/launchdf.c index 44a2d4d23..9f4b01fcb 100644 --- a/package/windows/launchdf.c +++ b/package/windows/launchdf.c @@ -9,20 +9,6 @@ int WINAPI wWinMain(HINSTANCE hi, HINSTANCE hpi, PWSTR cmd, int ns) si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); - CHAR dfdir[1024]; - - if (GetFullPathNameA("..", 1024, dfdir, NULL) == 0) - { - MessageBoxA(NULL, "could not get current directory", NULL, 0); - exit(1); - } - - if (SetCurrentDirectoryA(dfdir) == 0) - { - MessageBoxA(NULL, "could not change to DF directory", NULL, 0); - exit(1); - } - if (CreateProcessA("Dwarf Fortress.exe", NULL, NULL, From 6373832490843d7cf8bb2fa2a19690f182018d5e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 9 Mar 2023 21:18:45 -0800 Subject: [PATCH 19/28] refactor buildingplan into smaller files --- plugins/buildingplan/buildingplan.cpp | 10 +- plugins/lua/buildingplan.lua | 1986 +---------------- plugins/lua/buildingplan/filterselection.lua | 731 ++++++ plugins/lua/buildingplan/inspectoroverlay.lua | 148 ++ plugins/lua/buildingplan/itemselection.lua | 347 +++ plugins/lua/buildingplan/pens.lua | 31 + plugins/lua/buildingplan/planneroverlay.lua | 734 ++++++ 7 files changed, 2020 insertions(+), 1967 deletions(-) create mode 100644 plugins/lua/buildingplan/filterselection.lua create mode 100644 plugins/lua/buildingplan/inspectoroverlay.lua create mode 100644 plugins/lua/buildingplan/itemselection.lua create mode 100644 plugins/lua/buildingplan/pens.lua create mode 100644 plugins/lua/buildingplan/planneroverlay.lua diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index ec85d7952..310d03a5e 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -259,7 +259,7 @@ static void validate_config(color_ostream &out, bool verbose = false) { static void clear_state(color_ostream &out) { call_buildingplan_lua(&out, "signal_reset"); - call_buildingplan_lua(&out, "reload_cursors"); + call_buildingplan_lua(&out, "reload_pens"); planned_buildings.clear(); tasks.clear(); cur_heat_safety.clear(); @@ -318,14 +318,6 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { return CR_OK; } -DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { - if (event == SC_WORLD_UNLOADED) { - DEBUG(status,out).print("world unloaded; clearing state for %s\n", plugin_name); - clear_state(out); - } - return CR_OK; -} - static bool cycle_requested = false; static void do_cycle(color_ostream &out) { diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 0fcfd4c72..d606101be 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -2,7 +2,7 @@ local _ENV = mkmodule('plugins.buildingplan') --[[ - Native functions: + Public native functions: * bool isPlannableBuilding(df::building_type type, int16_t subtype, int32_t custom) * bool isPlannedBuilding(df::building *bld) @@ -13,15 +13,11 @@ local _ENV = mkmodule('plugins.buildingplan') --]] local argparse = require('argparse') -local gui = require('gui') -local guidm = require('gui.dwarfmode') -local overlay = require('plugins.overlay') -local utils = require('utils') -local widgets = require('gui.widgets') +local inspector = require('plugins.buildingplan.inspectoroverlay') +local pens = require('plugins.buildingplan.pens') +local planner = require('plugins.buildingplan.planneroverlay') require('dfhack.buildings') -local uibs = df.global.buildreq - local function process_args(opts, args) if args[1] == 'help' then opts.help = true @@ -44,7 +40,7 @@ function parse_commandline(...) local command = table.remove(positionals, 1) if not command or command == 'status' then printStatus() - elseif command == 'set' then + elseif command == 'set' and positionals then setSetting(positionals[1], positionals[2] == 'true') else return false @@ -66,36 +62,6 @@ function get_job_item(btype, subtype, custom, index) return obj end -local function get_cur_filters() - return dfhack.buildings.getFiltersByType({}, uibs.building_type, - uibs.building_subtype, uibs.custom_type) -end - -local function is_choosing_area() - return uibs.selection_pos.x >= 0 -end - -local function get_cur_area_dims(placement_data) - if not placement_data and not is_choosing_area() then return 1, 1, 1 end - local selection_pos = placement_data and placement_data.p1 or uibs.selection_pos - local pos = placement_data and placement_data.p2 or uibs.pos - return math.abs(selection_pos.x - pos.x) + 1, - math.abs(selection_pos.y - pos.y) + 1, - math.abs(selection_pos.z - pos.z) + 1 -end - -local function get_quantity(filter, hollow, placement_data) - local quantity = filter.quantity or 1 - local dimx, dimy, dimz = get_cur_area_dims(placement_data) - if quantity < 1 then - return (((dimx * dimy) // 4) + 1) * dimz - end - if hollow and dimx > 2 and dimy > 2 then - return quantity * (2*dimx + 2*dimy - 4) * dimz - end - return quantity * dimx * dimy * dimz -end - local function to_title_case(str) str = str:gsub('(%a)([%w_]*)', function (first, rest) return first:upper()..rest:lower() end) @@ -114,12 +80,12 @@ function get_desc(filter) elseif filter.vector_id and filter.vector_id > -1 then desc = to_title_case(df.job_item_vector_id[filter.vector_id]) elseif filter.flags2 and filter.flags2.building_material then - desc = 'Building material'; - if filter.flags2.fire_safe then - desc = 'Fire-safe material'; - end if filter.flags2.magma_safe then - desc = 'Magma-safe material'; + desc = 'Magma-safe material' + elseif filter.flags2.fire_safe then + desc = 'Fire-safe material' + else + desc = 'Building material' end end @@ -131,1930 +97,34 @@ function get_desc(filter) elseif desc == 'Wood' then desc = 'Log' end - return desc -end - -local BUTTON_START_PEN, BUTTON_END_PEN, SELECTED_ITEM_PEN = nil, nil, nil -local reset_counts_flag = false -local reset_inspector_flag = false -function signal_reset() - BUTTON_START_PEN = nil - BUTTON_END_PEN = nil - SELECTED_ITEM_PEN = nil - reset_counts_flag = true - reset_inspector_flag = true -end - -local to_pen = dfhack.pen.parse -local function get_button_start_pen() - if not BUTTON_START_PEN then - local texpos_base = dfhack.textures.getControlPanelTexposStart() - BUTTON_START_PEN = to_pen{ch='[', fg=COLOR_YELLOW, - tile=texpos_base > 0 and texpos_base + 13 or nil} - end - return BUTTON_START_PEN -end -local function get_button_end_pen() - if not BUTTON_END_PEN then - local texpos_base = dfhack.textures.getControlPanelTexposStart() - BUTTON_END_PEN = to_pen{ch=']', fg=COLOR_YELLOW, - tile=texpos_base > 0 and texpos_base + 15 or nil} - end - return BUTTON_END_PEN -end -local function get_selected_item_pen() - if not SELECTED_ITEM_PEN then - local texpos_base = dfhack.textures.getControlPanelTexposStart() - SELECTED_ITEM_PEN = to_pen{ch='x', fg=COLOR_GREEN, - tile=texpos_base > 0 and texpos_base + 9 or nil} - end - return SELECTED_ITEM_PEN -end - -BuildingplanScreen = defclass(BuildingplanScreen, gui.ZScreen) -BuildingplanScreen.ATTRS { - pass_movement_keys=true, - pass_mouse_clicks=false, - defocusable=false, -} - --------------------------------- --- ItemSelection --- - -local BUILD_TEXT_PEN = to_pen{fg=COLOR_BLACK, bg=COLOR_GREEN, keep_lower=true} -local BUILD_TEXT_HPEN = to_pen{fg=COLOR_WHITE, bg=COLOR_GREEN, keep_lower=true} - --- map of building type -> {set=set of recently used, list=list of recently used} --- most recent entries are at the *end* of the list -local recently_used = {} - -local function sort_by_type(a, b) - local ad, bd = a.data, b.data - return ad.item_type < bd.item_type or - (ad.item_type == bd.item_type and ad.item_subtype < bd.item_subtype) or - (ad.item_type == bd.item_type and ad.item_subtype == bd.item_subtype and a.search_key < b.search_key) or - (ad.item_type == bd.item_type and ad.item_subtype == bd.item_subtype and a.search_key == b.search_key and ad.quality > bd.quality) -end - -local function sort_by_recency(a, b) - local tracker = recently_used[uibs.building_type] - if not tracker then return sort_by_type(a, b) end - local recent_a, recent_b = tracker.set[a.search_key], tracker.set[b.search_key] - -- if they're both in the set, return the one with the greater index, - -- indicating more recent - if recent_a and recent_b then return recent_a > recent_b end - if recent_a and not recent_b then return true end - if not recent_a and recent_b then return false end - return sort_by_type(a, b) -end - -local function sort_by_name(a, b) - return a.search_key < b.search_key or - (a.search_key == b.search_key and sort_by_type(a, b)) -end - -local function sort_by_quantity(a, b) - local ad, bd = a.data, b.data - return ad.quantity > bd.quantity or - (ad.quantity == bd.quantity and sort_by_type(a, b)) -end - -ItemSelection = defclass(ItemSelection, widgets.Window) -ItemSelection.ATTRS{ - frame_title='Choose items', - frame={w=56, h=20, l=4, t=8}, - resizable=true, - index=DEFAULT_NIL, - quantity=DEFAULT_NIL, - on_submit=DEFAULT_NIL, - on_cancel=DEFAULT_NIL, -} - -function ItemSelection:init() - local filter = get_cur_filters()[self.index] - self.num_selected = 0 - self.selected_set = {} - local plural = self.quantity == 1 and '' or 's' - - self:addviews{ - widgets.Label{ - frame={t=0, l=0, r=10}, - text={ - get_desc(filter), - plural, - NEWLINE, - ('Select up to %d item%s ('):format(self.quantity, plural), - {text=function() return self.num_selected end}, - ' selected)', - }, - }, - widgets.Label{ - frame={r=0, w=9, t=0, h=3}, - text_pen=BUILD_TEXT_PEN, - text_hpen=BUILD_TEXT_HPEN, - text={ - ' ', NEWLINE, - ' Build ', NEWLINE, - ' ', - }, - on_click=self:callback('submit'), - }, - widgets.FilteredList{ - view_id='flist', - frame={t=3, l=0, r=0, b=4}, - case_sensitive=false, - choices=self:get_choices(sort_by_recency), - icon_width=2, - on_submit=self:callback('toggle_group'), - edit_on_char=function(ch) return ch:match('[%l -]') end, - }, - widgets.CycleHotkeyLabel{ - frame={l=0, b=2}, - key='CUSTOM_SHIFT_R', - label='Sort by:', - options={ - {label='Recently used', value=sort_by_recency}, - {label='Name', value=sort_by_name}, - {label='Amount', value=sort_by_quantity}, - }, - on_change=self:callback('on_sort'), - }, - widgets.HotkeyLabel{ - frame={l=0, b=1}, - key='SELECT', - label='Use all/none', - auto_width=true, - on_activate=function() self:toggle_group(self.subviews.flist.list:getSelected()) end, - }, - widgets.HotkeyLabel{ - frame={l=22, b=1}, - key='CUSTOM_SHIFT_B', - label='Build', - auto_width=true, - on_activate=self:callback('submit'), - }, - widgets.HotkeyLabel{ - frame={l=38, b=1}, - key='LEAVESCREEN', - label='Go back', - auto_width=true, - on_activate=self:callback('on_cancel'), - }, - widgets.HotkeyLabel{ - frame={l=0, b=0}, - key='KEYBOARD_CURSOR_RIGHT_FAST', - key_sep=' : ', - label='Use one', - auto_width=true, - on_activate=function() self:increment_group(self.subviews.flist.list:getSelected()) end, - }, - widgets.Label{ - frame={l=6, b=0, w=5}, - text_pen=COLOR_LIGHTGREEN, - text='Right', - }, - widgets.HotkeyLabel{ - frame={l=23, b=0}, - key='KEYBOARD_CURSOR_LEFT_FAST', - key_sep=' : ', - label='Use one fewer', - auto_width=true, - on_activate=function() self:decrement_group(self.subviews.flist.list:getSelected()) end, - }, - widgets.Label{ - frame={l=29, b=0, w=4}, - text_pen=COLOR_LIGHTGREEN, - text='Left', - }, - } -end - --- resort and restore selection -function ItemSelection:on_sort(sort_fn) - local flist = self.subviews.flist - local saved_filter = flist:getFilter() - flist:setFilter('') - flist:setChoices(self:get_choices(sort_fn), flist:getSelected()) - flist:setFilter(saved_filter) -end - -local function make_search_key(str) - local out = '' - for c in str:gmatch("[%w%s]") do - out = out .. c - end - return out -end - -function ItemSelection:get_choices(sort_fn) - local item_ids = getAvailableItems(uibs.building_type, - uibs.building_subtype, uibs.custom_type, self.index-1) - local buckets = {} - for _,item_id in ipairs(item_ids) do - local item = df.item.find(item_id) - if not item then goto continue end - local desc = dfhack.items.getDescription(item, 0, true) - if buckets[desc] then - local bucket = buckets[desc] - table.insert(bucket.data.item_ids, item_id) - bucket.data.quantity = bucket.data.quantity + 1 - else - local entry = { - search_key=make_search_key(desc), - icon=self:callback('get_entry_icon', item_id), - data={ - item_ids={item_id}, - item_type=item:getType(), - item_subtype=item:getSubtype(), - quantity=1, - quality=item:getQuality(), - selected=0, - }, - } - buckets[desc] = entry - end - ::continue:: - end - local choices = {} - for desc,choice in pairs(buckets) do - local data = choice.data - choice.text = { - {width=10, text=function() return ('[%d/%d]'):format(data.selected, data.quantity) end}, - {gap=2, text=desc}, - } - table.insert(choices, choice) - end - table.sort(choices, sort_fn) - return choices -end - -function ItemSelection:increment_group(idx, choice) - local data = choice.data - if self.quantity <= self.num_selected then return false end - if data.selected >= data.quantity then return false end - data.selected = data.selected + 1 - self.num_selected = self.num_selected + 1 - local item_id = data.item_ids[data.selected] - self.selected_set[item_id] = true - return true -end - -function ItemSelection:decrement_group(idx, choice) - local data = choice.data - if data.selected <= 0 then return false end - local item_id = data.item_ids[data.selected] - self.selected_set[item_id] = nil - self.num_selected = self.num_selected - 1 - data.selected = data.selected - 1 - return true -end - -function ItemSelection:toggle_group(idx, choice) - local data = choice.data - if data.selected > 0 then - while self:decrement_group(idx, choice) do end - else - while self:increment_group(idx, choice) do end - end -end - -function ItemSelection:get_entry_icon(item_id) - return self.selected_set[item_id] and get_selected_item_pen() or nil -end - -local function track_recently_used(choices) - -- use same set for all subtypes - local tracker = ensure_key(recently_used, uibs.building_type) - for _,choice in ipairs(choices) do - local data = choice.data - if data.selected <= 0 then goto continue end - local key = choice.search_key - local recent_set = ensure_key(tracker, 'set') - local recent_list = ensure_key(tracker, 'list') - if recent_set[key] then - if recent_list[#recent_list] ~= key then - for i,v in ipairs(recent_list) do - if v == key then - table.remove(recent_list, i) - table.insert(recent_list, key) - break - end - end - tracker.set = utils.invert(recent_list) - end - else - -- only keep most recent 10 - if #recent_list >= 10 then - -- remove least recently used from list and set - recent_set[table.remove(recent_list, 1)] = nil - end - table.insert(recent_list, key) - recent_set[key] = #recent_list - end - ::continue:: - end -end - -function ItemSelection:submit() - local selected_items = {} - for item_id in pairs(self.selected_set) do - table.insert(selected_items, item_id) - end - if #selected_items > 0 then - track_recently_used(self.subviews.flist:getChoices()) - end - self.on_submit(selected_items) -end - -function ItemSelection:onInput(keys) - if keys.LEAVESCREEN or keys._MOUSE_R_DOWN then - self.on_cancel() - return true - elseif keys._MOUSE_L_DOWN then - local list = self.subviews.flist.list - local idx = list:getIdxUnderMouse() - if idx then - list:setSelected(idx) - local modstate = dfhack.internal.getModstate() - if modstate & 2 > 0 then -- ctrl - local choice = list:getChoices()[idx] - if modstate & 1 > 0 then -- shift - self:decrement_group(idx, choice) - else - self:increment_group(idx, choice) - end - return true - end - end - end - return ItemSelection.super.onInput(self, keys) -end - -ItemSelectionScreen = defclass(ItemSelectionScreen, BuildingplanScreen) -ItemSelectionScreen.ATTRS { - focus_path='dwarfmode/Building/Placement/dfhack/lua/buildingplan/itemselection', - force_pause=true, - pass_pause=false, - index=DEFAULT_NIL, - quantity=DEFAULT_NIL, - on_submit=DEFAULT_NIL, - on_cancel=DEFAULT_NIL, -} - -function ItemSelectionScreen:init() - self:addviews{ - ItemSelection{ - index=self.index, - quantity=self.quantity, - on_submit=self.on_submit, - on_cancel=self.on_cancel, - } - } -end - --------------------------------- --- Slider --- - -Slider = defclass(Slider, widgets.Widget) -Slider.ATTRS{ - num_stops=DEFAULT_NIL, - get_left_idx_fn=DEFAULT_NIL, - get_right_idx_fn=DEFAULT_NIL, - on_left_change=DEFAULT_NIL, - on_right_change=DEFAULT_NIL, -} - -function Slider:preinit(init_table) - init_table.frame = init_table.frame or {} - init_table.frame.h = init_table.frame.h or 1 -end - -function Slider:init() - if self.num_stops < 2 then error('too few Slider stops') end - self.is_dragging_target = nil -- 'left', 'right', or 'both' - self.is_dragging_idx = nil -- offset from leftmost dragged tile -end - -local function slider_get_width_per_idx(self) - return math.max(5, (self.frame_body.width-7) // (self.num_stops-1)) -end - -function Slider:onInput(keys) - if not keys._MOUSE_L_DOWN then return false end - local x = self:getMousePos() - if not x then return false end - local left_idx, right_idx = self.get_left_idx_fn(), self.get_right_idx_fn() - local width_per_idx = slider_get_width_per_idx(self) - local left_pos = width_per_idx*(left_idx-1) - local right_pos = width_per_idx*(right_idx-1) + 4 - if x < left_pos then - self.on_left_change(self.get_left_idx_fn() - 1) - elseif x < left_pos+3 then - self.is_dragging_target = 'left' - self.is_dragging_idx = x - left_pos - elseif x < right_pos then - self.is_dragging_target = 'both' - self.is_dragging_idx = x - left_pos - elseif x < right_pos+3 then - self.is_dragging_target = 'right' - self.is_dragging_idx = x - right_pos - else - self.on_right_change(self.get_right_idx_fn() + 1) - end - return true -end - -local function slider_do_drag(self, width_per_idx) - local x = self.frame_body:localXY(dfhack.screen.getMousePos()) - local cur_pos = x - self.is_dragging_idx - cur_pos = math.max(0, cur_pos) - cur_pos = math.min(width_per_idx*(self.num_stops-1)+7, cur_pos) - local offset = self.is_dragging_target == 'right' and -2 or 1 - local new_idx = math.max(0, cur_pos+offset)//width_per_idx + 1 - local new_left_idx, new_right_idx - if self.is_dragging_target == 'right' then - new_right_idx = new_idx - else - new_left_idx = new_idx - if self.is_dragging_target == 'both' then - new_right_idx = new_left_idx + self.get_right_idx_fn() - self.get_left_idx_fn() - if new_right_idx > self.num_stops then - return - end - end - end - if new_left_idx and new_left_idx ~= self.get_left_idx_fn() then - self.on_left_change(new_left_idx) - end - if new_right_idx and new_right_idx ~= self.get_right_idx_fn() then - self.on_right_change(new_right_idx) - end -end - -local SLIDER_LEFT_END = to_pen{ch=198, fg=COLOR_GREY, bg=COLOR_BLACK} -local SLIDER_TRACK = to_pen{ch=205, fg=COLOR_GREY, bg=COLOR_BLACK} -local SLIDER_TRACK_SELECTED = to_pen{ch=205, fg=COLOR_LIGHTGREEN, bg=COLOR_BLACK} -local SLIDER_TRACK_STOP = to_pen{ch=216, fg=COLOR_GREY, bg=COLOR_BLACK} -local SLIDER_TRACK_STOP_SELECTED = to_pen{ch=216, fg=COLOR_LIGHTGREEN, bg=COLOR_BLACK} -local SLIDER_RIGHT_END = to_pen{ch=181, fg=COLOR_GREY, bg=COLOR_BLACK} -local SLIDER_TAB_LEFT = to_pen{ch=60, fg=COLOR_BLACK, bg=COLOR_YELLOW} -local SLIDER_TAB_CENTER = to_pen{ch=9, fg=COLOR_BLACK, bg=COLOR_YELLOW} -local SLIDER_TAB_RIGHT = to_pen{ch=62, fg=COLOR_BLACK, bg=COLOR_YELLOW} - -function Slider:onRenderBody(dc, rect) - local left_idx, right_idx = self.get_left_idx_fn(), self.get_right_idx_fn() - local width_per_idx = slider_get_width_per_idx(self) - -- draw track - dc:seek(1,0) - dc:char(nil, SLIDER_LEFT_END) - dc:char(nil, SLIDER_TRACK) - for stop_idx=1,self.num_stops-1 do - local track_stop_pen = SLIDER_TRACK_STOP_SELECTED - local track_pen = SLIDER_TRACK_SELECTED - if left_idx > stop_idx or right_idx < stop_idx then - track_stop_pen = SLIDER_TRACK_STOP - track_pen = SLIDER_TRACK - elseif right_idx == stop_idx then - track_pen = SLIDER_TRACK - end - dc:char(nil, track_stop_pen) - for i=2,width_per_idx do - dc:char(nil, track_pen) - end - end - if right_idx >= self.num_stops then - dc:char(nil, SLIDER_TRACK_STOP_SELECTED) - else - dc:char(nil, SLIDER_TRACK_STOP) - end - dc:char(nil, SLIDER_TRACK) - dc:char(nil, SLIDER_RIGHT_END) - -- draw tabs - dc:seek(width_per_idx*(left_idx-1)) - dc:char(nil, SLIDER_TAB_LEFT) - dc:char(nil, SLIDER_TAB_CENTER) - dc:char(nil, SLIDER_TAB_RIGHT) - dc:seek(width_per_idx*(right_idx-1)+4) - dc:char(nil, SLIDER_TAB_LEFT) - dc:char(nil, SLIDER_TAB_CENTER) - dc:char(nil, SLIDER_TAB_RIGHT) - -- manage dragging - if self.is_dragging_target then - slider_do_drag(self, width_per_idx) - end - if df.global.enabler.mouse_lbut == 0 then - self.is_dragging_target = nil - self.is_dragging_idx = nil - end -end - --------------------------------- --- QualityAndMaterialsPage --- - -QualityAndMaterialsPage = defclass(QualityAndMaterialsPage, widgets.Panel) -QualityAndMaterialsPage.ATTRS{ - frame={t=0, l=0}, - index=DEFAULT_NIL, -} - -local TYPE_COL_WIDTH = 20 -local HEADER_HEIGHT = 7 -local QUALITY_HEIGHT = 9 -local FOOTER_HEIGHT = 4 - --- returns whether the items matched by the specified filter can have a quality --- rating. This also conveniently indicates whether an item can be decorated. -local function can_be_improved(idx) - local filter = get_cur_filters()[idx] - if filter.flags2 and filter.flags2.building_material then - return false; - end - return filter.item_type ~= df.item_type.WOOD and - filter.item_type ~= df.item_type.BLOCKS and - filter.item_type ~= df.item_type.BAR and - filter.item_type ~= df.item_type.BOULDER -end - -local function mat_sort_by_name(a, b) - return a.name < b.name -end - -local function mat_sort_by_quantity(a, b) - return a.quantity > b.quantity or - (a.quantity == b.quantity and mat_sort_by_name(a, b)) -end - -function QualityAndMaterialsPage:init() - self.dirty = true - self.summary = '' - - local enable_item_quality = can_be_improved(self.index) - - self:addviews{ - widgets.Panel{ - view_id='header', - frame={l=0, t=0, h=HEADER_HEIGHT, r=0}, - frame_inset={l=1}, - subviews={ - widgets.Label{ - frame={l=0, t=0}, - text='Current filter:', - }, - widgets.WrappedLabel{ - frame={l=16, t=0, h=2, r=0}, - text_pen=COLOR_LIGHTCYAN, - text_to_wrap=function() return self.summary end, - auto_height=false, - }, - widgets.CycleHotkeyLabel{ - view_id='mat_sort', - frame={l=0, t=3, w=21}, - label='Sort by:', - key='CUSTOM_SHIFT_R', - options={ - {label='name', value=mat_sort_by_name}, - {label='available', value=mat_sort_by_quantity} - }, - on_change=function() self.dirty = true end, - }, - widgets.ToggleHotkeyLabel{ - view_id='hide_zero', - frame={l=0, t=4, w=24}, - label='Hide unavailable:', - key='CUSTOM_SHIFT_H', - initial_option=false, - on_change=function() self.dirty = true end, - }, - widgets.EditField{ - view_id='search', - frame={l=26, t=3}, - label_text='Search: ', - on_char=function(ch) return ch:match('[%l -]') end, - }, - widgets.Label{ - frame={l=1, b=0}, - text='Type', - text_pen=COLOR_LIGHTRED, - }, - widgets.Label{ - frame={l=TYPE_COL_WIDTH, b=0}, - text='Material', - text_pen=COLOR_LIGHTRED, - }, - }, - }, - widgets.Panel{ - view_id='materials_lists', - frame={l=0, t=HEADER_HEIGHT, r=0, b=FOOTER_HEIGHT+QUALITY_HEIGHT}, - frame_style=gui.INTERIOR_FRAME, - subviews={ - widgets.List{ - view_id='materials_categories', - frame={l=1, t=0, b=0, w=TYPE_COL_WIDTH-3}, - scroll_keys={}, - icon_width=2, - cursor_pen=COLOR_CYAN, - on_submit=self:callback('toggle_category'), - }, - widgets.FilteredList{ - view_id='materials_mats', - frame={l=TYPE_COL_WIDTH, t=0, r=0, b=0}, - icon_width=2, - on_submit=self:callback('toggle_material'), - }, - }, - }, - widgets.Panel{ - view_id='divider', - frame={l=TYPE_COL_WIDTH-1, t=HEADER_HEIGHT, b=FOOTER_HEIGHT+QUALITY_HEIGHT, w=1}, - on_render=self:callback('draw_divider'), - }, - widgets.Panel{ - view_id='quality_panel', - frame={l=0, r=0, h=QUALITY_HEIGHT, b=FOOTER_HEIGHT}, - frame_style=gui.INTERIOR_FRAME, - frame_title='Item quality', - subviews={ - widgets.CycleHotkeyLabel{ - view_id='decorated', - frame={l=0, t=1, w=23}, - key='CUSTOM_SHIFT_D', - label='Decorated only:', - options={ - {label='No', value=false}, - {label='Yes', value=true}, - }, - enabled=enable_item_quality, - on_change=self:callback('set_decorated'), - }, - widgets.CycleHotkeyLabel{ - view_id='min_quality', - frame={l=0, t=3, w=18}, - label='Min quality:', - label_below=true, - key_back='CUSTOM_SHIFT_Z', - key='CUSTOM_SHIFT_X', - options={ - {label='Ordinary', value=0}, - {label='Well Crafted', value=1}, - {label='Finely Crafted', value=2}, - {label='Superior', value=3}, - {label='Exceptional', value=4}, - {label='Masterful', value=5}, - {label='Artifact', value=6}, - }, - enabled=enable_item_quality, - on_change=function(val) self:set_min_quality(val+1) end, - }, - widgets.CycleHotkeyLabel{ - view_id='max_quality', - frame={r=1, t=3, w=18}, - label='Max quality:', - label_below=true, - key_back='CUSTOM_SHIFT_Q', - key='CUSTOM_SHIFT_W', - options={ - {label='Ordinary', value=0}, - {label='Well Crafted', value=1}, - {label='Finely Crafted', value=2}, - {label='Superior', value=3}, - {label='Exceptional', value=4}, - {label='Masterful', value=5}, - {label='Artifact', value=6}, - }, - enabled=enable_item_quality, - on_change=function(val) self:set_max_quality(val+1) end, - }, - Slider{ - frame={l=0, t=6}, - num_stops=7, - get_left_idx_fn=function() - return self.subviews.min_quality:getOptionValue() + 1 - end, - get_right_idx_fn=function() - return self.subviews.max_quality:getOptionValue() + 1 - end, - on_left_change=self:callback('set_min_quality'), - on_right_change=self:callback('set_max_quality'), - active=enable_item_quality, - }, - }, - }, - widgets.Panel{ - view_id='footer', - frame={l=0, r=0, b=0, h=FOOTER_HEIGHT}, - frame_inset={t=1, l=1}, - subviews={ - widgets.HotkeyLabel{ - frame={l=0, t=0}, - label='Toggle', - auto_width=true, - key='SELECT', - }, - widgets.HotkeyLabel{ - frame={l=0, t=2}, - label='Done', - auto_width=true, - key='LEAVESCREEN', - }, - widgets.HotkeyLabel{ - frame={l=30, t=0}, - label='Invert selection', - auto_width=true, - key='CUSTOM_SHIFT_I', - on_activate=self:callback('invert_materials'), - }, - widgets.HotkeyLabel{ - frame={l=30, t=2}, - label='Reset filter', - auto_width=true, - key='CUSTOM_SHIFT_X', - on_activate=self:callback('clear_filter'), - }, - }, - } - } - - -- replace the FilteredList's built-in EditField with our own - self.subviews.materials_mats.list.frame.t = 0 - self.subviews.materials_mats.edit.visible = false - self.subviews.materials_mats.edit = self.subviews.search - self.subviews.search.on_change = self.subviews.materials_mats:callback('onFilterChange') -end - -local MAT_ENABLED_PEN = to_pen{ch=string.char(251), fg=COLOR_LIGHTGREEN} -local MAT_DISABLED_PEN = to_pen{ch='x', fg=COLOR_RED} - -local function make_cat_choice(label, cat, key, cats) - local enabled = cats[cat] - local icon = nil - if not cats.unset then - icon = enabled and MAT_ENABLED_PEN or MAT_DISABLED_PEN - end - return { - text=label, - key=key, - enabled=enabled, - cat=cat, - icon=icon, - } -end - -local function make_mat_choice(name, props, enabled, cats) - local quantity = tonumber(props.count) - local text = ('%5d - %s'):format(quantity, name) - local icon = nil - if not cats.unset then - icon = enabled and MAT_ENABLED_PEN or MAT_DISABLED_PEN - end - return { - text=text, - enabled=enabled, - icon=icon, - name=name, - cat=props.category, - quantity=quantity, - } -end - -function QualityAndMaterialsPage:refresh() - local summary = get_desc(get_cur_filters()[self.index]) - local subviews = self.subviews - - local heat = getHeatSafetyFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type) - if heat >= 2 then summary = 'Magma safe ' .. summary - elseif heat == 1 then summary = 'Fire safe ' .. summary - else summary = 'Any ' .. summary - end - - local quality = getQualityFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) - subviews.decorated:setOption(quality.decorated ~= 0) - subviews.min_quality:setOption(quality.min_quality) - subviews.max_quality:setOption(quality.max_quality) - - local cats = getMaterialMaskFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) - local category_choices={ - make_cat_choice('Stone', 'stone', 'CUSTOM_SHIFT_S', cats), - make_cat_choice('Wood', 'wood', 'CUSTOM_SHIFT_O', cats), - make_cat_choice('Metal', 'metal', 'CUSTOM_SHIFT_M', cats), - make_cat_choice('Glass', 'glass', 'CUSTOM_SHIFT_G', cats), - } - self.subviews.materials_categories:setChoices(category_choices) - - local mats = getMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) - local mat_choices = {} - local hide_zero = self.subviews.hide_zero:getOptionValue() - local enabled_mat_names = {} - for name,props in pairs(mats) do - local enabled = props.enabled == 'true' and cats[props.category] - if not cats.unset and enabled then - table.insert(enabled_mat_names, name) - end - if not hide_zero or tonumber(props.count) > 0 then - table.insert(mat_choices, make_mat_choice(name, props, enabled, cats)) - end - end - table.sort(mat_choices, self.subviews.mat_sort:getOptionValue()) - - local prev_filter = self.subviews.search.text - self.subviews.materials_mats:setChoices(mat_choices) - self.subviews.materials_mats:setFilter(prev_filter) - - if #enabled_mat_names > 0 then - table.sort(enabled_mat_names) - summary = summary .. (' of %s'):format(table.concat(enabled_mat_names, ', ')) - end - - self.summary = summary - self.dirty = false - self:updateLayout() -end - -function QualityAndMaterialsPage:toggle_category(_, choice) - local cats = {} - if not choice.icon then - -- toggling from unset to something is set - table.insert(cats, choice.cat) - else - choice.enabled = not choice.enabled - for _,c in ipairs(self.subviews.materials_categories:getChoices()) do - if c.enabled then - table.insert(cats, c.cat) - end - end - end - setMaterialMaskFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, cats) - self.dirty = true -end - -function QualityAndMaterialsPage:toggle_material(_, choice) - local mats = {} - if not choice.icon then - -- toggling from unset to something is set - table.insert(mats, choice.name) - else - for _,c in ipairs(self.subviews.materials_mats:getChoices()) do - local enabled = c.enabled - if choice.name == c.name then - enabled = not c.enabled - end - if enabled then - table.insert(mats, c.name) - end - end - end - setMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, mats) - self.dirty = true -end - -function QualityAndMaterialsPage:invert_materials() - local mats = {} - for _,c in ipairs(self.subviews.materials_mats:getChoices()) do - if not c.icon then return end - if not c.enabled then - table.insert(mats, c.name) - end - end - setMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, mats) - self.dirty = true -end - -function QualityAndMaterialsPage:clear_filter() - clearFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) - self.dirty = true -end - -function QualityAndMaterialsPage:set_decorated(decorated) - local subviews = self.subviews - setQualityFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, - decorated and 1 or 0, subviews.min_quality:getOptionValue(), subviews.max_quality:getOptionValue()) - self.dirty = true -end - -function QualityAndMaterialsPage:set_min_quality(idx) - idx = math.min(6, math.max(0, idx-1)) - local subviews = self.subviews - subviews.min_quality:setOption(idx) - if subviews.max_quality:getOptionValue() < idx then - subviews.max_quality:setOption(idx) - end - setQualityFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, - subviews.decorated:getOptionValue() and 1 or 0, idx, subviews.max_quality:getOptionValue()) - self.dirty = true -end - -function QualityAndMaterialsPage:set_max_quality(idx) - idx = math.min(6, math.max(0, idx-1)) - local subviews = self.subviews - subviews.max_quality:setOption(idx) - if subviews.min_quality:getOptionValue() > idx then - subviews.min_quality:setOption(idx) - end - setQualityFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, - subviews.decorated:getOptionValue() and 1 or 0, subviews.min_quality:getOptionValue(), idx) - self.dirty = true -end - -local texpos = dfhack.textures.getThinBordersTexposStart() -local tp = function(offset) - if texpos == -1 then return nil end - return texpos + offset -end - -local TOP_PEN = to_pen{tile=tp(10), ch=194, fg=COLOR_GREY, bg=COLOR_BLACK} -local MID_PEN = to_pen{tile=tp(4), ch=192, fg=COLOR_GREY, bg=COLOR_BLACK} -local BOT_PEN = to_pen{tile=tp(11), ch=179, fg=COLOR_GREY, bg=COLOR_BLACK} - -function QualityAndMaterialsPage:draw_divider(dc) - local y2 = dc.height - 1 - for y=0,y2 do - dc:seek(0, y) - if y == 0 then - dc:char(nil, TOP_PEN) - elseif y == y2 then - dc:char(nil, BOT_PEN) - else - dc:char(nil, MID_PEN) - end - end -end - -function QualityAndMaterialsPage:onRenderFrame(dc, rect) - QualityAndMaterialsPage.super.onRenderFrame(self, dc, rect) - if self.dirty then - self:refresh() - end -end - --------------------------------- --- GlobalSettingsPage --- - -GlobalSettingsPage = defclass(GlobalSettingsPage, widgets.ResizingPanel) -GlobalSettingsPage.ATTRS{ - autoarrange_subviews=true, - frame={t=0, l=0}, - frame_style=gui.INTERIOR_FRAME, -} - -function GlobalSettingsPage:init() - self:addviews{ - widgets.WrappedLabel{ - frame={l=0}, - text_to_wrap='These options will affect the selection of "Generic Materials" for all future buildings.', - }, - widgets.Panel{ - frame={h=1}, - }, - widgets.ToggleHotkeyLabel{ - view_id='blocks', - frame={l=0}, - key='CUSTOM_B', - label='Blocks', - label_width=8, - on_change=self:callback('update_setting', 'blocks'), - }, - widgets.ToggleHotkeyLabel{ - view_id='logs', - frame={l=0}, - key='CUSTOM_L', - label='Logs', - label_width=8, - on_change=self:callback('update_setting', 'logs'), - }, - widgets.ToggleHotkeyLabel{ - view_id='boulders', - frame={l=0}, - key='CUSTOM_O', - label='Boulders', - label_width=8, - on_change=self:callback('update_setting', 'boulders'), - }, - widgets.ToggleHotkeyLabel{ - view_id='bars', - frame={l=0}, - key='CUSTOM_R', - label='Bars', - label_width=8, - on_change=self:callback('update_setting', 'bars'), - }, - } - - self:init_settings() -end - -function GlobalSettingsPage:init_settings() - local settings = getGlobalSettings() - local subviews = self.subviews - subviews.blocks:setOption(settings.blocks) - subviews.logs:setOption(settings.logs) - subviews.boulders:setOption(settings.boulders) - subviews.bars:setOption(settings.bars) -end - -function GlobalSettingsPage:update_setting(setting, val) - dfhack.run_command('buildingplan', 'set', setting, tostring(val)) - self:init_settings() -end - --------------------------------- --- FilterSelection --- - -FilterSelection = defclass(FilterSelection, widgets.Window) -FilterSelection.ATTRS{ - frame_title='Choose filters', - frame={w=55, h=53, l=30, t=8}, - frame_inset={t=1}, - resizable=true, - index=DEFAULT_NIL, - autoarrange_subviews=true, -} - -function FilterSelection:init() - self:addviews{ - widgets.TabBar{ - frame={t=0}, - labels={ - 'Quality and materials', - 'Global settings', - }, - on_select=function(idx) - self.subviews.pages:setSelected(idx) - self:updateLayout() - end, - get_cur_page=function() return self.subviews.pages:getSelected() end, - key='CUSTOM_CTRL_T', - }, - widgets.Widget{ - frame={h=1}, - }, - widgets.Pages{ - view_id='pages', - frame={t=5, l=0, b=0, r=0}, - subviews={ - QualityAndMaterialsPage{index=self.index}, - GlobalSettingsPage{}, - }, - }, - } -end - -FilterSelectionScreen = defclass(FilterSelectionScreen, BuildingplanScreen) -FilterSelectionScreen.ATTRS { - focus_path='dwarfmode/Building/Placement/dfhack/lua/buildingplan/filterselection', - index=DEFAULT_NIL, -} - -function FilterSelectionScreen:init() - self:addviews{ - FilterSelection{index=self.index} - } -end - -function FilterSelectionScreen:onShow() - -- don't let the building "shadow" follow the mouse cursor while this screen is open - df.global.game.main_interface.bottom_mode_selected = -1 -end - -function FilterSelectionScreen:onDismiss() - -- re-enable building shadow - df.global.game.main_interface.bottom_mode_selected = df.main_bottom_mode_type.BUILDING_PLACEMENT -end - --------------------------------- --- ItemLine --- - -local function cur_building_has_no_area() - if uibs.building_type == df.building_type.Construction then return false end - local filters = dfhack.buildings.getFiltersByType({}, - uibs.building_type, uibs.building_subtype, uibs.custom_type) - -- this works because all variable-size buildings have either no item - -- filters or a quantity of -1 for their first (and only) item - return filters and filters[1] and (not filters[1].quantity or filters[1].quantity > 0) -end - -local function is_plannable() - return get_cur_filters() and - not (uibs.building_type == df.building_type.Construction - and uibs.building_subtype == df.construction_type.TrackNSEW) -end - -local function is_construction() - return uibs.building_type == df.building_type.Construction -end - -local function is_stairs() - return is_construction() - and uibs.building_subtype == df.construction_type.UpDownStair -end - -local direction_panel_frame = {t=4, h=13, w=46, r=28} - -local direction_panel_types = utils.invert{ - df.building_type.Bridge, - df.building_type.ScrewPump, - df.building_type.WaterWheel, - df.building_type.AxleHorizontal, - df.building_type.Rollers, -} - -local function has_direction_panel() - return direction_panel_types[uibs.building_type] - or (uibs.building_type == df.building_type.Trap - and uibs.building_subtype == df.trap_type.TrackStop) -end - -local pressure_plate_panel_frame = {t=4, h=37, w=46, r=28} - -local function has_pressure_plate_panel() - return uibs.building_type == df.building_type.Trap - and uibs.building_subtype == df.trap_type.PressurePlate -end - -local function is_over_options_panel() - local frame = nil - if has_direction_panel() then - frame = direction_panel_frame - elseif has_pressure_plate_panel() then - frame = pressure_plate_panel_frame - else - return false - end - local v = widgets.Widget{frame=frame} - local rect = gui.mkdims_wh(0, 0, dfhack.screen.getWindowSize()) - v:updateLayout(gui.ViewRect{rect=rect}) - return v:getMousePos() -end - -ItemLine = defclass(ItemLine, widgets.Panel) -ItemLine.ATTRS{ - idx=DEFAULT_NIL, - is_selected_fn=DEFAULT_NIL, - is_hollow_fn=DEFAULT_NIL, - on_select=DEFAULT_NIL, - on_filter=DEFAULT_NIL, - on_clear_filter=DEFAULT_NIL, -} - -function ItemLine:init() - self.frame.h = 1 - self.visible = function() return #get_cur_filters() >= self.idx end - self:addviews{ - widgets.Label{ - frame={t=0, l=0}, - text='*', - auto_width=true, - visible=self.is_selected_fn, - }, - widgets.Label{ - frame={t=0, l=25}, - text={ - {tile=get_button_start_pen}, - {gap=6, tile=get_button_end_pen}, - }, - auto_width=true, - on_click=function() self.on_filter(self.idx) end, - }, - widgets.Label{ - frame={t=0, l=33}, - text={ - {tile=get_button_start_pen}, - {gap=1, tile=get_button_end_pen}, - }, - auto_width=true, - on_click=function() self.on_clear_filter(self.idx) end, - }, - widgets.Label{ - frame={t=0, l=2}, - text={ - {width=21, text=self:callback('get_item_line_text')}, - {gap=3, text='filter', pen=COLOR_GREEN}, - {gap=2, text='x', pen=self:callback('get_x_pen')}, - {gap=3, text=function() return self.note end, - pen=function() return self.note_pen end}, - }, - }, - } -end - -function ItemLine:reset() - self.desc = nil - self.available = nil -end - -function ItemLine:onInput(keys) - if keys._MOUSE_L_DOWN and self:getMousePos() then - self.on_select(self.idx) - end - return ItemLine.super.onInput(self, keys) -end - -function ItemLine:get_x_pen() - return hasFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.idx - 1) and - COLOR_GREEN or COLOR_GREY -end - -function ItemLine:get_item_line_text() - local idx = self.idx - local filter = get_cur_filters()[idx] - local quantity = get_quantity(filter, self.is_hollow_fn()) - - self.desc = self.desc or get_desc(filter) - - self.available = self.available or countAvailableItems(uibs.building_type, - uibs.building_subtype, uibs.custom_type, idx - 1) - if self.available >= quantity then - self.note_pen = COLOR_GREEN - self.note = 'Available now' - else - self.note_pen = COLOR_YELLOW - self.note = 'Will link later' - end - - return ('%d %s%s'):format(quantity, self.desc, quantity == 1 and '' or 's') -end - -function ItemLine:reduce_quantity(used_quantity) - if not self.available then return end - local filter = get_cur_filters()[self.idx] - used_quantity = used_quantity or get_quantity(filter, self.is_hollow_fn()) - self.available = math.max(0, self.available - used_quantity) -end - -local function get_placement_errors() - local out = '' - for _,str in ipairs(uibs.errors) do - if #out > 0 then out = out .. NEWLINE end - out = out .. str.value - end - return out -end - --------------------------------- --- PlannerOverlay --- - -PlannerOverlay = defclass(PlannerOverlay, overlay.OverlayWidget) -PlannerOverlay.ATTRS{ - default_pos={x=5,y=9}, - default_enabled=true, - viewscreens='dwarfmode/Building/Placement', - frame={w=56, h=20}, -} - -function PlannerOverlay:init() - self.selected = 1 - - local main_panel = widgets.Panel{ - view_id='main', - frame={t=0, l=0, r=0, h=14}, - frame_style=gui.MEDIUM_FRAME, - frame_background=gui.CLEAR_PEN, - } - - local function make_is_selected_fn(idx) - return function() return self.selected == idx end - end - - local function on_select_fn(idx) - self.selected = idx - end - - local function is_hollow_fn() - return self.subviews.hollow:getOptionValue() - end - - main_panel:addviews{ - widgets.Label{ - frame={}, - auto_width=true, - text='No items required.', - visible=function() return #get_cur_filters() == 0 end, - }, - ItemLine{view_id='item1', frame={t=0, l=0, r=0}, idx=1, - is_selected_fn=make_is_selected_fn(1), is_hollow_fn=is_hollow_fn, - on_select=on_select_fn, on_filter=self:callback('set_filter'), - on_clear_filter=self:callback('clear_filter')}, - ItemLine{view_id='item2', frame={t=2, l=0, r=0}, idx=2, - is_selected_fn=make_is_selected_fn(2), is_hollow_fn=is_hollow_fn, - on_select=on_select_fn, on_filter=self:callback('set_filter'), - on_clear_filter=self:callback('clear_filter')}, - ItemLine{view_id='item3', frame={t=4, l=0, r=0}, idx=3, - is_selected_fn=make_is_selected_fn(3), is_hollow_fn=is_hollow_fn, - on_select=on_select_fn, on_filter=self:callback('set_filter'), - on_clear_filter=self:callback('clear_filter')}, - ItemLine{view_id='item4', frame={t=6, l=0, r=0}, idx=4, - is_selected_fn=make_is_selected_fn(4), is_hollow_fn=is_hollow_fn, - on_select=on_select_fn, on_filter=self:callback('set_filter'), - on_clear_filter=self:callback('clear_filter')}, - widgets.CycleHotkeyLabel{ - view_id='hollow', - frame={t=3, l=4}, - key='CUSTOM_H', - label='Hollow area:', - visible=is_construction, - options={ - {label='No', value=false}, - {label='Yes', value=true}, - }, - }, - widgets.CycleHotkeyLabel{ - view_id='stairs_top_subtype', - frame={t=4, l=4}, - key='CUSTOM_R', - label='Top Stair Type: ', - visible=is_stairs, - options={ - {label='Auto', value='auto'}, - {label='UpDown', value=df.construction_type.UpDownStair}, - {label='Down', value=df.construction_type.DownStair}, - }, - }, - widgets.CycleHotkeyLabel { - view_id='stairs_bottom_subtype', - frame={t=5, l=4}, - key='CUSTOM_B', - label='Bottom Stair Type: ', - visible=is_stairs, - options={ - {label='Auto', value='auto'}, - {label='UpDown', value=df.construction_type.UpDownStair}, - {label='Up', value=df.construction_type.UpStair}, - }, - }, - widgets.Label{ - frame={b=3, l=17}, - text={ - 'Selected area: ', - {text=function() - return ('%dx%dx%d'):format(get_cur_area_dims(self.saved_placement)) - end - }, - }, - visible=function() - return not cur_building_has_no_area() and (self.saved_placement or is_choosing_area()) - end, - }, - widgets.Panel{ - visible=function() return #get_cur_filters() > 0 end, - subviews={ - widgets.HotkeyLabel{ - frame={b=1, l=0}, - key='STRING_A042', - auto_width=true, - enabled=function() return #get_cur_filters() > 1 end, - on_activate=function() self.selected = ((self.selected - 2) % #get_cur_filters()) + 1 end, - }, - widgets.HotkeyLabel{ - frame={b=1, l=1}, - key='STRING_A047', - label='Prev/next item', - auto_width=true, - enabled=function() return #get_cur_filters() > 1 end, - on_activate=function() self.selected = (self.selected % #get_cur_filters()) + 1 end, - }, - widgets.HotkeyLabel{ - frame={b=1, l=21}, - key='CUSTOM_F', - label='Set filter', - auto_width=true, - on_activate=function() self:set_filter(self.selected) end, - }, - widgets.HotkeyLabel{ - frame={b=1, l=37}, - key='CUSTOM_X', - label='Clear filter', - auto_width=true, - on_activate=function() self:clear_filter(self.selected) end, - enabled=function() - return hasFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.selected - 1) - end - }, - widgets.CycleHotkeyLabel{ - view_id='choose', - frame={b=0, l=0, w=25}, - key='CUSTOM_I', - label='Choose from items:', - options={{label='Yes', value=true}, - {label='No', value=false}}, - initial_option=false, - enabled=function() - for idx = 1,4 do - if (self.subviews['item'..idx].available or 0) > 0 then - return true - end - end - end, - }, - widgets.CycleHotkeyLabel{ - view_id='safety', - frame={b=0, l=29, w=25}, - key='CUSTOM_G', - label='Building safety:', - options={ - {label='Any', value=0}, - {label='Magma', value=2, pen=COLOR_RED}, - {label='Fire', value=1, pen=COLOR_LIGHTRED}, - }, - initial_option=0, - on_change=function(heat) - setHeatSafetyFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, heat) - end, - }, - }, - }, - } - - local error_panel = widgets.ResizingPanel{ - view_id='errors', - frame={t=14, l=0, r=0}, - frame_style=gui.MEDIUM_FRAME, - frame_background=gui.CLEAR_PEN, - } - - error_panel:addviews{ - widgets.WrappedLabel{ - frame={t=0, l=0, r=0}, - text_pen=COLOR_LIGHTRED, - text_to_wrap=get_placement_errors, - visible=function() return #uibs.errors > 0 end, - }, - widgets.Label{ - frame={t=0, l=0, r=0}, - text_pen=COLOR_GREEN, - text='OK to build', - visible=function() return #uibs.errors == 0 end, - }, - } - - self:addviews{ - main_panel, - error_panel, - } -end - -function PlannerOverlay:reset() - self.subviews.item1:reset() - self.subviews.item2:reset() - self.subviews.item3:reset() - self.subviews.item4:reset() - reset_counts_flag = false -end - -function PlannerOverlay:set_filter(idx) - FilterSelectionScreen{index=idx}:show() -end - -function PlannerOverlay:clear_filter(idx) - clearFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, idx-1) -end - -local function get_placement_data() - local pos = uibs.pos - local direction = uibs.direction - local width, height, depth = get_cur_area_dims() - local _, adjusted_width, adjusted_height = dfhack.buildings.getCorrectSize( - width, height, uibs.building_type, uibs.building_subtype, - uibs.custom_type, direction) - -- get the upper-left corner of the building/area at min z-level - local has_selection = is_choosing_area() - local start_pos = xyz2pos( - has_selection and math.min(uibs.selection_pos.x, pos.x) or pos.x - adjusted_width//2, - has_selection and math.min(uibs.selection_pos.y, pos.y) or pos.y - adjusted_height//2, - has_selection and math.min(uibs.selection_pos.z, pos.z) or pos.z - ) - if uibs.building_type == df.building_type.ScrewPump then - if direction == df.screw_pump_direction.FromSouth then - start_pos.y = start_pos.y + 1 - elseif direction == df.screw_pump_direction.FromEast then - start_pos.x = start_pos.x + 1 - end - end - local min_x, max_x = start_pos.x, start_pos.x - local min_y, max_y = start_pos.y, start_pos.y - local min_z, max_z = start_pos.z, start_pos.z - if adjusted_width == 1 and adjusted_height == 1 - and (width > 1 or height > 1 or depth > 1) then - max_x = min_x + width - 1 - max_y = min_y + height - 1 - max_z = math.max(uibs.selection_pos.z, pos.z) - end - return { - p1=xyz2pos(min_x, min_y, min_z), - p2=xyz2pos(max_x, max_y, max_z), - width=adjusted_width, - height=adjusted_height - } -end - -function PlannerOverlay:save_placement() - self.saved_placement = get_placement_data() - if (uibs.selection_pos:isValid()) then - self.saved_selection_pos_valid = true - self.saved_selection_pos = copyall(uibs.selection_pos) - self.saved_pos = copyall(uibs.pos) - uibs.selection_pos:clear() - else - self.saved_selection_pos = copyall(self.saved_placement.p1) - self.saved_pos = copyall(self.saved_placement.p2) - self.saved_pos.x = self.saved_pos.x + self.saved_placement.width - 1 - self.saved_pos.y = self.saved_pos.y + self.saved_placement.height - 1 - end -end - -function PlannerOverlay:restore_placement() - if self.saved_selection_pos_valid then - uibs.selection_pos = self.saved_selection_pos - self.saved_selection_pos_valid = nil - else - uibs.selection_pos:clear() - end - self.saved_selection_pos = nil - self.saved_pos = nil - local placement_data = self.saved_placement - self.saved_placement = nil - return placement_data -end - -function PlannerOverlay:onInput(keys) - if not is_plannable() then return false end - if keys.LEAVESCREEN or keys._MOUSE_R_DOWN then - if uibs.selection_pos:isValid() then - uibs.selection_pos:clear() - return true - end - self.selected = 1 - self.subviews.hollow:setOption(false) - self.subviews.choose:setOption(false) - self:reset() - reset_counts_flag = true - return false - end - if PlannerOverlay.super.onInput(self, keys) then - return true - end - if keys._MOUSE_L_DOWN then - if is_over_options_panel() then return false end - local detect_rect = copyall(self.frame_rect) - detect_rect.height = self.subviews.main.frame_rect.height + - self.subviews.errors.frame_rect.height - detect_rect.y2 = detect_rect.y1 + detect_rect.height - 1 - if self.subviews.main:getMousePos(gui.ViewRect{rect=detect_rect}) - or self.subviews.errors:getMousePos() then - return true - end - if not is_construction() and #uibs.errors > 0 then return true end - if dfhack.gui.getMousePos() then - if is_choosing_area() or cur_building_has_no_area() then - local filters = get_cur_filters() - local num_filters = #filters - local choose = self.subviews.choose - if choose.enabled() and choose:getOptionValue() then - self:save_placement() - local is_hollow = self.subviews.hollow:getOptionValue() - local chosen_items, active_screens = {}, {} - local pending = num_filters - df.global.game.main_interface.bottom_mode_selected = -1 - for idx = num_filters,1,-1 do - chosen_items[idx] = {} - if (self.subviews['item'..idx].available or 0) > 0 then - active_screens[idx] = ItemSelectionScreen{ - index=idx, - quantity=get_quantity(filters[idx], is_hollow, - self.saved_placement), - on_submit=function(items) - chosen_items[idx] = items - active_screens[idx]:dismiss() - active_screens[idx] = nil - pending = pending - 1 - if pending == 0 then - df.global.game.main_interface.bottom_mode_selected = df.main_bottom_mode_type.BUILDING_PLACEMENT - self:place_building(self:restore_placement(), chosen_items) - end - end, - on_cancel=function() - for i,scr in pairs(active_screens) do - scr:dismiss() - end - df.global.game.main_interface.bottom_mode_selected = df.main_bottom_mode_type.BUILDING_PLACEMENT - self:restore_placement() - end, - }:show() - else - pending = pending - 1 - end - end - else - self:place_building(get_placement_data()) - end - return true - elseif not is_choosing_area() then - return false - end - end - end - return keys._MOUSE_L or keys.SELECT -end -function PlannerOverlay:render(dc) - if not is_plannable() then return end - self.subviews.errors:updateLayout() - PlannerOverlay.super.render(self, dc) -end - -local GOOD_PEN, BAD_PEN -function reload_cursors() - GOOD_PEN = to_pen{ch='o', fg=COLOR_GREEN, tile=dfhack.screen.findGraphicsTile('CURSORS', 1, 2)} - BAD_PEN = to_pen{ch='X', fg=COLOR_RED, tile=dfhack.screen.findGraphicsTile('CURSORS', 3, 0)} -end -reload_cursors() - -local ONE_BY_ONE = xy2pos(1, 1) - -function PlannerOverlay:onRenderFrame(dc, rect) - PlannerOverlay.super.onRenderFrame(self, dc, rect) - - if reset_counts_flag then - self:reset() - self.subviews.safety:setOption(getHeatSafetyFilter( - uibs.building_type, uibs.building_subtype, uibs.custom_type)) - end - - local selection_pos = self.saved_selection_pos or uibs.selection_pos - if not selection_pos or selection_pos.x < 0 then return end - - local pos = self.saved_pos or uibs.pos - local bounds = { - x1 = math.max(0, math.min(selection_pos.x, pos.x)), - x2 = math.min(df.global.world.map.x_count-1, math.max(selection_pos.x, pos.x)), - y1 = math.max(0, math.min(selection_pos.y, pos.y)), - y2 = math.min(df.global.world.map.y_count-1, math.max(selection_pos.y, pos.y)), - } - - local hollow = self.subviews.hollow:getOptionValue() - local default_pen = (self.saved_selection_pos or #uibs.errors == 0) and GOOD_PEN or BAD_PEN - - local get_pen_fn = is_construction() and function(pos) - return dfhack.buildings.checkFreeTiles(pos, ONE_BY_ONE) and GOOD_PEN or BAD_PEN - end or function() - return default_pen - end - - local function get_overlay_pen(pos) - if not hollow then return get_pen_fn(pos) end - if pos.x == bounds.x1 or pos.x == bounds.x2 or - pos.y == bounds.y1 or pos.y == bounds.y2 then - return get_pen_fn(pos) - end - return gui.TRANSPARENT_PEN - end - - guidm.renderMapOverlay(get_overlay_pen, bounds) -end - -function PlannerOverlay:get_stairs_subtype(pos, corner1, corner2) - local subtype = uibs.building_subtype - if pos.z == corner1.z then - local opt = self.subviews.stairs_bottom_subtype:getOptionValue() - if opt == 'auto' then - local tt = dfhack.maps.getTileType(pos) - local shape = df.tiletype.attrs[tt].shape - if shape ~= df.tiletype_shape.STAIR_DOWN then - subtype = df.construction_type.UpStair - end - else - subtype = opt - end - elseif pos.z == corner2.z then - local opt = self.subviews.stairs_top_subtype:getOptionValue() - if opt == 'auto' then - local tt = dfhack.maps.getTileType(pos) - local shape = df.tiletype.attrs[tt].shape - if shape ~= df.tiletype_shape.STAIR_UP then - subtype = df.construction_type.DownStair - end - else - subtype = opt - end - end - return subtype -end - -function PlannerOverlay:place_building(placement_data, chosen_items) - local p1, p2 = placement_data.p1, placement_data.p2 - local blds = {} - local hollow = self.subviews.hollow:getOptionValue() - local subtype = uibs.building_subtype - for z=p1.z,p2.z do for y=p1.y,p2.y do for x=p1.x,p2.x do - if hollow and x ~= p1.x and x ~= p2.x and y ~= p1.y and y ~= p2.y then - goto continue - end - local pos = xyz2pos(x, y, z) - if is_stairs() then - subtype = self:get_stairs_subtype(pos, p1, p2) - end - local bld, err = dfhack.buildings.constructBuilding{pos=pos, - type=uibs.building_type, subtype=subtype, custom=uibs.custom_type, - width=placement_data.width, height=placement_data.height, - direction=uibs.direction} - if err then - -- it's ok if some buildings fail to build - goto continue - end - -- assign fields for the types that need them. we can't pass them all in - -- to the call to constructBuilding since attempting to assign unrelated - -- fields to building types that don't support them causes errors. - for k,v in pairs(bld) do - if k == 'friction' then bld.friction = uibs.friction end - if k == 'use_dump' then bld.use_dump = uibs.use_dump end - if k == 'dump_x_shift' then bld.dump_x_shift = uibs.dump_x_shift end - if k == 'dump_y_shift' then bld.dump_y_shift = uibs.dump_y_shift end - if k == 'speed' then bld.speed = uibs.speed end - end - table.insert(blds, bld) - ::continue:: - end end end - local used_quantity = is_construction() and #blds or false - self.subviews.item1:reduce_quantity(used_quantity) - self.subviews.item2:reduce_quantity(used_quantity) - self.subviews.item3:reduce_quantity(used_quantity) - self.subviews.item4:reduce_quantity(used_quantity) - for _,bld in ipairs(blds) do - -- attach chosen items and reduce job_item quantity - if chosen_items then - local job = bld.jobs[0] - local jitems = job.job_items - for idx=1,#get_cur_filters() do - local item_ids = chosen_items[idx] - while jitems[idx-1].quantity > 0 and #item_ids > 0 do - local item_id = item_ids[#item_ids] - local item = df.item.find(item_id) - if not item then - dfhack.printerr(('item no longer available: %d'):format(item_id)) - break - end - if not dfhack.job.attachJobItem(job, item, df.job_item_ref.T_role.Hauled, idx-1, -1) then - dfhack.printerr(('cannot attach item: %d'):format(item_id)) - break - end - jitems[idx-1].quantity = jitems[idx-1].quantity - 1 - item_ids[#item_ids] = nil - end - end - end - addPlannedBuilding(bld) - end - scheduleCycle() - uibs.selection_pos:clear() -end - --------------------------------- --- InspectorLine --- - -local function get_building_filters() - local bld = dfhack.gui.getSelectedBuilding() - return dfhack.buildings.getFiltersByType({}, - bld:getType(), bld:getSubtype(), bld:getCustomType()) -end - -InspectorLine = defclass(InspectorLine, widgets.Panel) -InspectorLine.ATTRS{ - idx=DEFAULT_NIL, -} - -function InspectorLine:init() - self.frame.h = 2 - self.visible = function() return #get_building_filters() >= self.idx end - self:addviews{ - widgets.Label{ - frame={t=0, l=0}, - text={{text=self:callback('get_desc_string')}}, - }, - widgets.Label{ - frame={t=1, l=2}, - text={{text=self:callback('get_status_line')}}, - }, - } -end - -function InspectorLine:get_desc_string() - if self.desc then return self.desc end - self.desc = getDescString(dfhack.gui.getSelectedBuilding(), self.idx-1) - return self.desc -end - -function InspectorLine:get_status_line() - if self.status then return self.status end - local queue_pos = getQueuePosition(dfhack.gui.getSelectedBuilding(), self.idx-1) - if queue_pos <= 0 then - return 'Item attached' - end - self.status = ('Position in line: %d'):format(queue_pos) - return self.status -end - -function InspectorLine:reset() - self.desc = nil - self.status = nil -end - --------------------------------- --- InspectorOverlay --- - -InspectorOverlay = defclass(InspectorOverlay, overlay.OverlayWidget) -InspectorOverlay.ATTRS{ - default_pos={x=-41,y=14}, - default_enabled=true, - viewscreens='dwarfmode/ViewSheets/BUILDING', - frame={w=30, h=15}, - frame_style=gui.MEDIUM_FRAME, - frame_background=gui.CLEAR_PEN, -} - -function InspectorOverlay:init() - self:addviews{ - widgets.Label{ - frame={t=0, l=0}, - text='Waiting for items:', - }, - InspectorLine{view_id='item1', frame={t=2, l=0}, idx=1}, - InspectorLine{view_id='item2', frame={t=4, l=0}, idx=2}, - InspectorLine{view_id='item3', frame={t=6, l=0}, idx=3}, - InspectorLine{view_id='item4', frame={t=8, l=0}, idx=4}, - widgets.HotkeyLabel{ - frame={t=11, l=0}, - label='adjust filters', - key='CUSTOM_CTRL_F', - visible=false, -- until implemented - }, - widgets.HotkeyLabel{ - frame={t=12, l=0}, - label='make top priority', - key='CUSTOM_CTRL_T', - on_activate=self:callback('make_top_priority'), - }, - } -end - -function InspectorOverlay:reset() - self.subviews.item1:reset() - self.subviews.item2:reset() - self.subviews.item3:reset() - self.subviews.item4:reset() - reset_inspector_flag = false -end - -function InspectorOverlay:make_top_priority() - makeTopPriority(dfhack.gui.getSelectedBuilding()) - self:reset() + return desc end -local RESUME_BUTTON_FRAME = {t=15, h=3, r=73, w=25} - -local function mouse_is_over_resume_button(rect) - local x,y = dfhack.screen.getMousePos() - if not x then return false end - if y < RESUME_BUTTON_FRAME.t or y > RESUME_BUTTON_FRAME.t + RESUME_BUTTON_FRAME.h - 1 then - return false - end - if x > rect.x2 - RESUME_BUTTON_FRAME.r + 1 or x < rect.x2 - RESUME_BUTTON_FRAME.r - RESUME_BUTTON_FRAME.w + 2 then - return false - end - return true +function reload_pens() + pens.reload_pens() end -function InspectorOverlay:onInput(keys) - if not isPlannedBuilding(dfhack.gui.getSelectedBuilding()) then - return false - end - if keys._MOUSE_L_DOWN and mouse_is_over_resume_button(self.frame_parent_rect) then - return true - elseif keys._MOUSE_L_DOWN or keys._MOUSE_R_DOWN or keys.LEAVESCREEN then - self:reset() - end - return InspectorOverlay.super.onInput(self, keys) +function signal_reset() + planner.reset_counts_flag = true + inspector.reset_inspector_flag = true end -function InspectorOverlay:render(dc) - if not isPlannedBuilding(dfhack.gui.getSelectedBuilding()) then - return - end - if reset_inspector_flag then - self:reset() - end - InspectorOverlay.super.render(self, dc) +-- for use during development to reload all buildingplan modules +function reload_modules() + -- ensure circular deps are refreshed + reload('plugins.buildingplan.pens') + reload('plugins.buildingplan') + reload('plugins.buildingplan.filterselection') + reload('plugins.buildingplan.itemselection') + reload('plugins.buildingplan.planneroverlay') + reload('plugins.buildingplan.inspectoroverlay') + reload('plugins.buildingplan') end OVERLAY_WIDGETS = { - planner=PlannerOverlay, - inspector=InspectorOverlay, + planner=planner.PlannerOverlay, + inspector=inspector.InspectorOverlay, } return _ENV diff --git a/plugins/lua/buildingplan/filterselection.lua b/plugins/lua/buildingplan/filterselection.lua new file mode 100644 index 000000000..d043406f3 --- /dev/null +++ b/plugins/lua/buildingplan/filterselection.lua @@ -0,0 +1,731 @@ +local _ENV = mkmodule('plugins.buildingplan.filterselection') + +local gui = require('gui') +local pens = require('plugins.buildingplan.pens') +local widgets = require('gui.widgets') + +local uibs = df.global.buildreq +local to_pen = dfhack.pen.parse + +local function get_cur_filters() + return dfhack.buildings.getFiltersByType({}, uibs.building_type, + uibs.building_subtype, uibs.custom_type) +end + +-------------------------------- +-- Slider +-- + +Slider = defclass(Slider, widgets.Widget) +Slider.ATTRS{ + num_stops=DEFAULT_NIL, + get_left_idx_fn=DEFAULT_NIL, + get_right_idx_fn=DEFAULT_NIL, + on_left_change=DEFAULT_NIL, + on_right_change=DEFAULT_NIL, +} + +function Slider:preinit(init_table) + init_table.frame = init_table.frame or {} + init_table.frame.h = init_table.frame.h or 1 +end + +function Slider:init() + if self.num_stops < 2 then error('too few Slider stops') end + self.is_dragging_target = nil -- 'left', 'right', or 'both' + self.is_dragging_idx = nil -- offset from leftmost dragged tile +end + +local function slider_get_width_per_idx(self) + return math.max(5, (self.frame_body.width-7) // (self.num_stops-1)) +end + +function Slider:onInput(keys) + if not keys._MOUSE_L_DOWN then return false end + local x = self:getMousePos() + if not x then return false end + local left_idx, right_idx = self.get_left_idx_fn(), self.get_right_idx_fn() + local width_per_idx = slider_get_width_per_idx(self) + local left_pos = width_per_idx*(left_idx-1) + local right_pos = width_per_idx*(right_idx-1) + 4 + if x < left_pos then + self.on_left_change(self.get_left_idx_fn() - 1) + elseif x < left_pos+3 then + self.is_dragging_target = 'left' + self.is_dragging_idx = x - left_pos + elseif x < right_pos then + self.is_dragging_target = 'both' + self.is_dragging_idx = x - left_pos + elseif x < right_pos+3 then + self.is_dragging_target = 'right' + self.is_dragging_idx = x - right_pos + else + self.on_right_change(self.get_right_idx_fn() + 1) + end + return true +end + +local function slider_do_drag(self, width_per_idx) + local x = self.frame_body:localXY(dfhack.screen.getMousePos()) + local cur_pos = x - self.is_dragging_idx + cur_pos = math.max(0, cur_pos) + cur_pos = math.min(width_per_idx*(self.num_stops-1)+7, cur_pos) + local offset = self.is_dragging_target == 'right' and -2 or 1 + local new_idx = math.max(0, cur_pos+offset)//width_per_idx + 1 + local new_left_idx, new_right_idx + if self.is_dragging_target == 'right' then + new_right_idx = new_idx + else + new_left_idx = new_idx + if self.is_dragging_target == 'both' then + new_right_idx = new_left_idx + self.get_right_idx_fn() - self.get_left_idx_fn() + if new_right_idx > self.num_stops then + return + end + end + end + if new_left_idx and new_left_idx ~= self.get_left_idx_fn() then + self.on_left_change(new_left_idx) + end + if new_right_idx and new_right_idx ~= self.get_right_idx_fn() then + self.on_right_change(new_right_idx) + end +end + +local SLIDER_LEFT_END = to_pen{ch=198, fg=COLOR_GREY, bg=COLOR_BLACK} +local SLIDER_TRACK = to_pen{ch=205, fg=COLOR_GREY, bg=COLOR_BLACK} +local SLIDER_TRACK_SELECTED = to_pen{ch=205, fg=COLOR_LIGHTGREEN, bg=COLOR_BLACK} +local SLIDER_TRACK_STOP = to_pen{ch=216, fg=COLOR_GREY, bg=COLOR_BLACK} +local SLIDER_TRACK_STOP_SELECTED = to_pen{ch=216, fg=COLOR_LIGHTGREEN, bg=COLOR_BLACK} +local SLIDER_RIGHT_END = to_pen{ch=181, fg=COLOR_GREY, bg=COLOR_BLACK} +local SLIDER_TAB_LEFT = to_pen{ch=60, fg=COLOR_BLACK, bg=COLOR_YELLOW} +local SLIDER_TAB_CENTER = to_pen{ch=9, fg=COLOR_BLACK, bg=COLOR_YELLOW} +local SLIDER_TAB_RIGHT = to_pen{ch=62, fg=COLOR_BLACK, bg=COLOR_YELLOW} + +function Slider:onRenderBody(dc, rect) + local left_idx, right_idx = self.get_left_idx_fn(), self.get_right_idx_fn() + local width_per_idx = slider_get_width_per_idx(self) + -- draw track + dc:seek(1,0) + dc:char(nil, SLIDER_LEFT_END) + dc:char(nil, SLIDER_TRACK) + for stop_idx=1,self.num_stops-1 do + local track_stop_pen = SLIDER_TRACK_STOP_SELECTED + local track_pen = SLIDER_TRACK_SELECTED + if left_idx > stop_idx or right_idx < stop_idx then + track_stop_pen = SLIDER_TRACK_STOP + track_pen = SLIDER_TRACK + elseif right_idx == stop_idx then + track_pen = SLIDER_TRACK + end + dc:char(nil, track_stop_pen) + for i=2,width_per_idx do + dc:char(nil, track_pen) + end + end + if right_idx >= self.num_stops then + dc:char(nil, SLIDER_TRACK_STOP_SELECTED) + else + dc:char(nil, SLIDER_TRACK_STOP) + end + dc:char(nil, SLIDER_TRACK) + dc:char(nil, SLIDER_RIGHT_END) + -- draw tabs + dc:seek(width_per_idx*(left_idx-1)) + dc:char(nil, SLIDER_TAB_LEFT) + dc:char(nil, SLIDER_TAB_CENTER) + dc:char(nil, SLIDER_TAB_RIGHT) + dc:seek(width_per_idx*(right_idx-1)+4) + dc:char(nil, SLIDER_TAB_LEFT) + dc:char(nil, SLIDER_TAB_CENTER) + dc:char(nil, SLIDER_TAB_RIGHT) + -- manage dragging + if self.is_dragging_target then + slider_do_drag(self, width_per_idx) + end + if df.global.enabler.mouse_lbut == 0 then + self.is_dragging_target = nil + self.is_dragging_idx = nil + end +end + +-------------------------------- +-- QualityAndMaterialsPage +-- + +QualityAndMaterialsPage = defclass(QualityAndMaterialsPage, widgets.Panel) +QualityAndMaterialsPage.ATTRS{ + frame={t=0, l=0}, + index=DEFAULT_NIL, + desc=DEFAULT_NIL, +} + +local TYPE_COL_WIDTH = 20 +local HEADER_HEIGHT = 7 +local QUALITY_HEIGHT = 9 +local FOOTER_HEIGHT = 4 + +-- returns whether the items matched by the specified filter can have a quality +-- rating. This also conveniently indicates whether an item can be decorated. +local function can_be_improved(idx) + local filter = get_cur_filters()[idx] + if filter.flags2 and filter.flags2.building_material then + return false; + end + return filter.item_type ~= df.item_type.WOOD and + filter.item_type ~= df.item_type.BLOCKS and + filter.item_type ~= df.item_type.BAR and + filter.item_type ~= df.item_type.BOULDER +end + +local function mat_sort_by_name(a, b) + return a.name < b.name +end + +local function mat_sort_by_quantity(a, b) + return a.quantity > b.quantity or + (a.quantity == b.quantity and mat_sort_by_name(a, b)) +end + +function QualityAndMaterialsPage:init() + self.dirty = true + self.summary = '' + + local enable_item_quality = can_be_improved(self.index) + + self:addviews{ + widgets.Panel{ + view_id='header', + frame={l=0, t=0, h=HEADER_HEIGHT, r=0}, + frame_inset={l=1}, + subviews={ + widgets.Label{ + frame={l=0, t=0}, + text='Current filter:', + }, + widgets.WrappedLabel{ + frame={l=16, t=0, h=2, r=0}, + text_pen=COLOR_LIGHTCYAN, + text_to_wrap=function() return self.summary end, + auto_height=false, + }, + widgets.CycleHotkeyLabel{ + view_id='mat_sort', + frame={l=0, t=3, w=21}, + label='Sort by:', + key='CUSTOM_SHIFT_R', + options={ + {label='name', value=mat_sort_by_name}, + {label='available', value=mat_sort_by_quantity} + }, + on_change=function() self.dirty = true end, + }, + widgets.ToggleHotkeyLabel{ + view_id='hide_zero', + frame={l=0, t=4, w=24}, + label='Hide unavailable:', + key='CUSTOM_SHIFT_H', + initial_option=false, + on_change=function() self.dirty = true end, + }, + widgets.EditField{ + view_id='search', + frame={l=26, t=3}, + label_text='Search: ', + on_char=function(ch) return ch:match('[%l -]') end, + }, + widgets.Label{ + frame={l=1, b=0}, + text='Type', + text_pen=COLOR_LIGHTRED, + }, + widgets.Label{ + frame={l=TYPE_COL_WIDTH, b=0}, + text='Material', + text_pen=COLOR_LIGHTRED, + }, + }, + }, + widgets.Panel{ + view_id='materials_lists', + frame={l=0, t=HEADER_HEIGHT, r=0, b=FOOTER_HEIGHT+QUALITY_HEIGHT}, + frame_style=gui.INTERIOR_FRAME, + subviews={ + widgets.List{ + view_id='materials_categories', + frame={l=1, t=0, b=0, w=TYPE_COL_WIDTH-3}, + scroll_keys={}, + icon_width=2, + cursor_pen=COLOR_CYAN, + on_submit=self:callback('toggle_category'), + }, + widgets.FilteredList{ + view_id='materials_mats', + frame={l=TYPE_COL_WIDTH, t=0, r=0, b=0}, + icon_width=2, + on_submit=self:callback('toggle_material'), + }, + }, + }, + widgets.Panel{ + view_id='divider', + frame={l=TYPE_COL_WIDTH-1, t=HEADER_HEIGHT, b=FOOTER_HEIGHT+QUALITY_HEIGHT, w=1}, + on_render=self:callback('draw_divider'), + }, + widgets.Panel{ + view_id='quality_panel', + frame={l=0, r=0, h=QUALITY_HEIGHT, b=FOOTER_HEIGHT}, + frame_style=gui.INTERIOR_FRAME, + frame_title='Item quality', + subviews={ + widgets.CycleHotkeyLabel{ + view_id='decorated', + frame={l=0, t=1, w=23}, + key='CUSTOM_SHIFT_D', + label='Decorated only:', + options={ + {label='No', value=false}, + {label='Yes', value=true}, + }, + enabled=enable_item_quality, + on_change=self:callback('set_decorated'), + }, + widgets.CycleHotkeyLabel{ + view_id='min_quality', + frame={l=0, t=3, w=18}, + label='Min quality:', + label_below=true, + key_back='CUSTOM_SHIFT_Z', + key='CUSTOM_SHIFT_X', + options={ + {label='Ordinary', value=0}, + {label='Well Crafted', value=1}, + {label='Finely Crafted', value=2}, + {label='Superior', value=3}, + {label='Exceptional', value=4}, + {label='Masterful', value=5}, + {label='Artifact', value=6}, + }, + enabled=enable_item_quality, + on_change=function(val) self:set_min_quality(val+1) end, + }, + widgets.CycleHotkeyLabel{ + view_id='max_quality', + frame={r=1, t=3, w=18}, + label='Max quality:', + label_below=true, + key_back='CUSTOM_SHIFT_Q', + key='CUSTOM_SHIFT_W', + options={ + {label='Ordinary', value=0}, + {label='Well Crafted', value=1}, + {label='Finely Crafted', value=2}, + {label='Superior', value=3}, + {label='Exceptional', value=4}, + {label='Masterful', value=5}, + {label='Artifact', value=6}, + }, + enabled=enable_item_quality, + on_change=function(val) self:set_max_quality(val+1) end, + }, + Slider{ + frame={l=0, t=6}, + num_stops=7, + get_left_idx_fn=function() + return self.subviews.min_quality:getOptionValue() + 1 + end, + get_right_idx_fn=function() + return self.subviews.max_quality:getOptionValue() + 1 + end, + on_left_change=self:callback('set_min_quality'), + on_right_change=self:callback('set_max_quality'), + active=enable_item_quality, + }, + }, + }, + widgets.Panel{ + view_id='footer', + frame={l=0, r=0, b=0, h=FOOTER_HEIGHT}, + frame_inset={t=1, l=1}, + subviews={ + widgets.HotkeyLabel{ + frame={l=0, t=0}, + label='Toggle', + auto_width=true, + key='SELECT', + }, + widgets.HotkeyLabel{ + frame={l=0, t=2}, + label='Done', + auto_width=true, + key='LEAVESCREEN', + }, + widgets.HotkeyLabel{ + frame={l=30, t=0}, + label='Invert selection', + auto_width=true, + key='CUSTOM_SHIFT_I', + on_activate=self:callback('invert_materials'), + }, + widgets.HotkeyLabel{ + frame={l=30, t=2}, + label='Reset filter', + auto_width=true, + key='CUSTOM_SHIFT_X', + on_activate=self:callback('clear_filter'), + }, + }, + } + } + + -- replace the FilteredList's built-in EditField with our own + self.subviews.materials_mats.list.frame.t = 0 + self.subviews.materials_mats.edit.visible = false + self.subviews.materials_mats.edit = self.subviews.search + self.subviews.search.on_change = self.subviews.materials_mats:callback('onFilterChange') +end + +local MAT_ENABLED_PEN = to_pen{ch=string.char(251), fg=COLOR_LIGHTGREEN} +local MAT_DISABLED_PEN = to_pen{ch='x', fg=COLOR_RED} + +local function make_cat_choice(label, cat, key, cats) + local enabled = cats[cat] + local icon = nil + if not cats.unset then + icon = enabled and MAT_ENABLED_PEN or MAT_DISABLED_PEN + end + return { + text=label, + key=key, + enabled=enabled, + cat=cat, + icon=icon, + } +end + +local function make_mat_choice(name, props, enabled, cats) + local quantity = tonumber(props.count) + local text = ('%5d - %s'):format(quantity, name) + local icon = nil + if not cats.unset then + icon = enabled and MAT_ENABLED_PEN or MAT_DISABLED_PEN + end + return { + text=text, + enabled=enabled, + icon=icon, + name=name, + cat=props.category, + quantity=quantity, + } +end + +function QualityAndMaterialsPage:refresh() + local summary = self.desc + local subviews = self.subviews + + local buildingplan = require('plugins.buildingplan') + + local heat = buildingplan.getHeatSafetyFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type) + if heat >= 2 then summary = 'Magma safe ' .. summary + elseif heat == 1 then summary = 'Fire safe ' .. summary + else summary = 'Any ' .. summary + end + + local quality = buildingplan.getQualityFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) + subviews.decorated:setOption(quality.decorated ~= 0) + subviews.min_quality:setOption(quality.min_quality) + subviews.max_quality:setOption(quality.max_quality) + + local cats = buildingplan.getMaterialMaskFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) + local category_choices={ + make_cat_choice('Stone', 'stone', 'CUSTOM_SHIFT_S', cats), + make_cat_choice('Wood', 'wood', 'CUSTOM_SHIFT_O', cats), + make_cat_choice('Metal', 'metal', 'CUSTOM_SHIFT_M', cats), + make_cat_choice('Glass', 'glass', 'CUSTOM_SHIFT_G', cats), + } + self.subviews.materials_categories:setChoices(category_choices) + + local mats = buildingplan.getMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) + local mat_choices = {} + local hide_zero = self.subviews.hide_zero:getOptionValue() + local enabled_mat_names = {} + for name,props in pairs(mats) do + local enabled = props.enabled == 'true' and cats[props.category] + if not cats.unset and enabled then + table.insert(enabled_mat_names, name) + end + if not hide_zero or tonumber(props.count) > 0 then + table.insert(mat_choices, make_mat_choice(name, props, enabled, cats)) + end + end + table.sort(mat_choices, self.subviews.mat_sort:getOptionValue()) + + local prev_filter = self.subviews.search.text + self.subviews.materials_mats:setChoices(mat_choices) + self.subviews.materials_mats:setFilter(prev_filter) + + if #enabled_mat_names > 0 then + table.sort(enabled_mat_names) + summary = summary .. (' of %s'):format(table.concat(enabled_mat_names, ', ')) + end + + self.summary = summary + self.dirty = false + self:updateLayout() +end + +function QualityAndMaterialsPage:toggle_category(_, choice) + local cats = {} + if not choice.icon then + -- toggling from unset to something is set + table.insert(cats, choice.cat) + else + choice.enabled = not choice.enabled + for _,c in ipairs(self.subviews.materials_categories:getChoices()) do + if c.enabled then + table.insert(cats, c.cat) + end + end + end + require('plugins.buildingplan').setMaterialMaskFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, cats) + self.dirty = true +end + +function QualityAndMaterialsPage:toggle_material(_, choice) + local mats = {} + if not choice.icon then + -- toggling from unset to something is set + table.insert(mats, choice.name) + else + for _,c in ipairs(self.subviews.materials_mats:getChoices()) do + local enabled = c.enabled + if choice.name == c.name then + enabled = not c.enabled + end + if enabled then + table.insert(mats, c.name) + end + end + end + require('plugins.buildingplan').setMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, mats) + self.dirty = true +end + +function QualityAndMaterialsPage:invert_materials() + local mats = {} + for _,c in ipairs(self.subviews.materials_mats:getChoices()) do + if not c.icon then return end + if not c.enabled then + table.insert(mats, c.name) + end + end + require('plugins.buildingplan').setMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, mats) + self.dirty = true +end + +function QualityAndMaterialsPage:clear_filter() + require('plugins.buildingplan').clearFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) + self.dirty = true +end + +function QualityAndMaterialsPage:set_decorated(decorated) + local subviews = self.subviews + require('plugins.buildingplan').setQualityFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, + decorated and 1 or 0, subviews.min_quality:getOptionValue(), subviews.max_quality:getOptionValue()) + self.dirty = true +end + +function QualityAndMaterialsPage:set_min_quality(idx) + idx = math.min(6, math.max(0, idx-1)) + local subviews = self.subviews + subviews.min_quality:setOption(idx) + if subviews.max_quality:getOptionValue() < idx then + subviews.max_quality:setOption(idx) + end + require('plugins.buildingplan').setQualityFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, + subviews.decorated:getOptionValue() and 1 or 0, idx, subviews.max_quality:getOptionValue()) + self.dirty = true +end + +function QualityAndMaterialsPage:set_max_quality(idx) + idx = math.min(6, math.max(0, idx-1)) + local subviews = self.subviews + subviews.max_quality:setOption(idx) + if subviews.min_quality:getOptionValue() > idx then + subviews.min_quality:setOption(idx) + end + require('plugins.buildingplan').setQualityFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, + subviews.decorated:getOptionValue() and 1 or 0, subviews.min_quality:getOptionValue(), idx) + self.dirty = true +end + +function QualityAndMaterialsPage:draw_divider(dc) + local y2 = dc.height - 1 + for y=0,y2 do + dc:seek(0, y) + if y == 0 then + dc:char(nil, pens.VERT_TOP_PEN) + elseif y == y2 then + dc:char(nil, pens.VERT_BOT_PEN) + else + dc:char(nil, pens.VERT_MID_PEN) + end + end +end + +function QualityAndMaterialsPage:onRenderFrame(dc, rect) + QualityAndMaterialsPage.super.onRenderFrame(self, dc, rect) + if self.dirty then + self:refresh() + end +end + +-------------------------------- +-- GlobalSettingsPage +-- + +GlobalSettingsPage = defclass(GlobalSettingsPage, widgets.ResizingPanel) +GlobalSettingsPage.ATTRS{ + autoarrange_subviews=true, + frame={t=0, l=0}, + frame_style=gui.INTERIOR_FRAME, +} + +function GlobalSettingsPage:init() + self:addviews{ + widgets.WrappedLabel{ + frame={l=0}, + text_to_wrap='These options will affect the selection of "Generic Materials" for all future buildings.', + }, + widgets.Panel{ + frame={h=1}, + }, + widgets.ToggleHotkeyLabel{ + view_id='blocks', + frame={l=0}, + key='CUSTOM_B', + label='Blocks', + label_width=8, + on_change=self:callback('update_setting', 'blocks'), + }, + widgets.ToggleHotkeyLabel{ + view_id='logs', + frame={l=0}, + key='CUSTOM_L', + label='Logs', + label_width=8, + on_change=self:callback('update_setting', 'logs'), + }, + widgets.ToggleHotkeyLabel{ + view_id='boulders', + frame={l=0}, + key='CUSTOM_O', + label='Boulders', + label_width=8, + on_change=self:callback('update_setting', 'boulders'), + }, + widgets.ToggleHotkeyLabel{ + view_id='bars', + frame={l=0}, + key='CUSTOM_R', + label='Bars', + label_width=8, + on_change=self:callback('update_setting', 'bars'), + }, + } + + self:init_settings() +end + +function GlobalSettingsPage:init_settings() + local settings = require('plugins.buildingplan').getGlobalSettings() + local subviews = self.subviews + subviews.blocks:setOption(settings.blocks) + subviews.logs:setOption(settings.logs) + subviews.boulders:setOption(settings.boulders) + subviews.bars:setOption(settings.bars) +end + +function GlobalSettingsPage:update_setting(setting, val) + dfhack.run_command('buildingplan', 'set', setting, tostring(val)) + self:init_settings() +end + +-------------------------------- +-- FilterSelection +-- + +FilterSelection = defclass(FilterSelection, widgets.Window) +FilterSelection.ATTRS{ + frame_title='Choose filters', + frame={w=55, h=53, l=30, t=8}, + frame_inset={t=1}, + resizable=true, + index=DEFAULT_NIL, + desc=DEFAULT_NIL, + autoarrange_subviews=true, +} + +function FilterSelection:init() + self:addviews{ + widgets.TabBar{ + frame={t=0}, + labels={ + 'Quality and materials', + 'Global settings', + }, + on_select=function(idx) + self.subviews.pages:setSelected(idx) + self:updateLayout() + end, + get_cur_page=function() return self.subviews.pages:getSelected() end, + key='CUSTOM_CTRL_T', + }, + widgets.Widget{ + frame={h=1}, + }, + widgets.Pages{ + view_id='pages', + frame={t=5, l=0, b=0, r=0}, + subviews={ + QualityAndMaterialsPage{ + index=self.index, + desc=self.desc + }, + GlobalSettingsPage{}, + }, + }, + } +end + +FilterSelectionScreen = defclass(FilterSelectionScreen, gui.ZScreen) +FilterSelectionScreen.ATTRS { + focus_path='dwarfmode/Building/Placement/dfhack/lua/buildingplan/filterselection', + pass_movement_keys=true, + pass_mouse_clicks=false, + defocusable=false, + index=DEFAULT_NIL, + desc=DEFAULT_NIL, +} + +function FilterSelectionScreen:init() + self:addviews{ + FilterSelection{ + index=self.index, + desc=self.desc + } + } +end + +function FilterSelectionScreen:onShow() + -- don't let the building "shadow" follow the mouse cursor while this screen is open + df.global.game.main_interface.bottom_mode_selected = -1 +end + +function FilterSelectionScreen:onDismiss() + -- re-enable building shadow + df.global.game.main_interface.bottom_mode_selected = df.main_bottom_mode_type.BUILDING_PLACEMENT +end + +return _ENV diff --git a/plugins/lua/buildingplan/inspectoroverlay.lua b/plugins/lua/buildingplan/inspectoroverlay.lua new file mode 100644 index 000000000..5262eccc8 --- /dev/null +++ b/plugins/lua/buildingplan/inspectoroverlay.lua @@ -0,0 +1,148 @@ +local _ENV = mkmodule('plugins.buildingplan.inspectoroverlay') + +local gui = require('gui') +local overlay = require('plugins.overlay') +local widgets = require('gui.widgets') + +reset_inspector_flag = false + +local function get_building_filters() + local bld = dfhack.gui.getSelectedBuilding() + return dfhack.buildings.getFiltersByType({}, + bld:getType(), bld:getSubtype(), bld:getCustomType()) +end + +-------------------------------- +-- InspectorLine +-- + +InspectorLine = defclass(InspectorLine, widgets.Panel) +InspectorLine.ATTRS{ + idx=DEFAULT_NIL, +} + +function InspectorLine:init() + self.frame.h = 2 + self.visible = function() return #get_building_filters() >= self.idx end + self:addviews{ + widgets.Label{ + frame={t=0, l=0}, + text={{text=self:callback('get_desc_string')}}, + }, + widgets.Label{ + frame={t=1, l=2}, + text={{text=self:callback('get_status_line')}}, + }, + } +end + +function InspectorLine:get_desc_string() + if self.desc then return self.desc end + self.desc = require('plugins.buildingplan').getDescString(dfhack.gui.getSelectedBuilding(), self.idx-1) + return self.desc +end + +function InspectorLine:get_status_line() + if self.status then return self.status end + local queue_pos = require('plugins.buildingplan').getQueuePosition(dfhack.gui.getSelectedBuilding(), self.idx-1) + if queue_pos <= 0 then + return 'Item attached' + end + self.status = ('Position in line: %d'):format(queue_pos) + return self.status +end + +function InspectorLine:reset() + self.desc = nil + self.status = nil +end + +-------------------------------- +-- InspectorOverlay +-- + +InspectorOverlay = defclass(InspectorOverlay, overlay.OverlayWidget) +InspectorOverlay.ATTRS{ + default_pos={x=-41,y=14}, + default_enabled=true, + viewscreens='dwarfmode/ViewSheets/BUILDING', + frame={w=30, h=15}, + frame_style=gui.MEDIUM_FRAME, + frame_background=gui.CLEAR_PEN, +} + +function InspectorOverlay:init() + self:addviews{ + widgets.Label{ + frame={t=0, l=0}, + text='Waiting for items:', + }, + InspectorLine{view_id='item1', frame={t=2, l=0}, idx=1}, + InspectorLine{view_id='item2', frame={t=4, l=0}, idx=2}, + InspectorLine{view_id='item3', frame={t=6, l=0}, idx=3}, + InspectorLine{view_id='item4', frame={t=8, l=0}, idx=4}, + widgets.HotkeyLabel{ + frame={t=11, l=0}, + label='adjust filters', + key='CUSTOM_CTRL_F', + visible=false, -- until implemented + }, + widgets.HotkeyLabel{ + frame={t=12, l=0}, + label='make top priority', + key='CUSTOM_CTRL_T', + on_activate=self:callback('make_top_priority'), + }, + } +end + +function InspectorOverlay:reset() + self.subviews.item1:reset() + self.subviews.item2:reset() + self.subviews.item3:reset() + self.subviews.item4:reset() + reset_inspector_flag = false +end + +function InspectorOverlay:make_top_priority() + require('plugins.buildingplan').makeTopPriority(dfhack.gui.getSelectedBuilding()) + self:reset() +end + +local RESUME_BUTTON_FRAME = {t=15, h=3, r=73, w=25} + +local function mouse_is_over_resume_button(rect) + local x,y = dfhack.screen.getMousePos() + if not x then return false end + if y < RESUME_BUTTON_FRAME.t or y > RESUME_BUTTON_FRAME.t + RESUME_BUTTON_FRAME.h - 1 then + return false + end + if x > rect.x2 - RESUME_BUTTON_FRAME.r + 1 or x < rect.x2 - RESUME_BUTTON_FRAME.r - RESUME_BUTTON_FRAME.w + 2 then + return false + end + return true +end + +function InspectorOverlay:onInput(keys) + if not require('plugins.buildingplan').isPlannedBuilding(dfhack.gui.getSelectedBuilding()) then + return false + end + if keys._MOUSE_L_DOWN and mouse_is_over_resume_button(self.frame_parent_rect) then + return true + elseif keys._MOUSE_L_DOWN or keys._MOUSE_R_DOWN or keys.LEAVESCREEN then + self:reset() + end + return InspectorOverlay.super.onInput(self, keys) +end + +function InspectorOverlay:render(dc) + if not require('plugins.buildingplan').isPlannedBuilding(dfhack.gui.getSelectedBuilding()) then + return + end + if reset_inspector_flag then + self:reset() + end + InspectorOverlay.super.render(self, dc) +end + +return _ENV diff --git a/plugins/lua/buildingplan/itemselection.lua b/plugins/lua/buildingplan/itemselection.lua new file mode 100644 index 000000000..7e6567d04 --- /dev/null +++ b/plugins/lua/buildingplan/itemselection.lua @@ -0,0 +1,347 @@ +local _ENV = mkmodule('plugins.buildingplan.itemselection') + +local gui = require('gui') +local pens = require('plugins.buildingplan.pens') +local utils = require('utils') +local widgets = require('gui.widgets') + +local uibs = df.global.buildreq +local to_pen = dfhack.pen.parse + +local BUILD_TEXT_PEN = to_pen{fg=COLOR_BLACK, bg=COLOR_GREEN, keep_lower=true} +local BUILD_TEXT_HPEN = to_pen{fg=COLOR_WHITE, bg=COLOR_GREEN, keep_lower=true} + +-- map of building type -> {set=set of recently used, list=list of recently used} +-- most recent entries are at the *end* of the list +local recently_used = {} + +local function sort_by_type(a, b) + local ad, bd = a.data, b.data + return ad.item_type < bd.item_type or + (ad.item_type == bd.item_type and ad.item_subtype < bd.item_subtype) or + (ad.item_type == bd.item_type and ad.item_subtype == bd.item_subtype and a.search_key < b.search_key) or + (ad.item_type == bd.item_type and ad.item_subtype == bd.item_subtype and a.search_key == b.search_key and ad.quality > bd.quality) +end + +local function sort_by_recency(a, b) + local tracker = recently_used[uibs.building_type] + if not tracker then return sort_by_type(a, b) end + local recent_a, recent_b = tracker.set[a.search_key], tracker.set[b.search_key] + -- if they're both in the set, return the one with the greater index, + -- indicating more recent + if recent_a and recent_b then return recent_a > recent_b end + if recent_a and not recent_b then return true end + if not recent_a and recent_b then return false end + return sort_by_type(a, b) +end + +local function sort_by_name(a, b) + return a.search_key < b.search_key or + (a.search_key == b.search_key and sort_by_type(a, b)) +end + +local function sort_by_quantity(a, b) + local ad, bd = a.data, b.data + return ad.quantity > bd.quantity or + (ad.quantity == bd.quantity and sort_by_type(a, b)) +end + +ItemSelection = defclass(ItemSelection, widgets.Window) +ItemSelection.ATTRS{ + frame_title='Choose items', + frame={w=56, h=20, l=4, t=8}, + resizable=true, + index=DEFAULT_NIL, + desc=DEFAULT_NIL, + quantity=DEFAULT_NIL, + on_submit=DEFAULT_NIL, + on_cancel=DEFAULT_NIL, +} + +function ItemSelection:init() + self.num_selected = 0 + self.selected_set = {} + local plural = self.quantity == 1 and '' or 's' + + self:addviews{ + widgets.Label{ + frame={t=0, l=0, r=10}, + text={ + self.desc, + plural, + NEWLINE, + ('Select up to %d item%s ('):format(self.quantity, plural), + {text=function() return self.num_selected end}, + ' selected)', + }, + }, + widgets.Label{ + frame={r=0, w=9, t=0, h=3}, + text_pen=BUILD_TEXT_PEN, + text_hpen=BUILD_TEXT_HPEN, + text={ + ' ', NEWLINE, + ' Build ', NEWLINE, + ' ', + }, + on_click=self:callback('submit'), + }, + widgets.FilteredList{ + view_id='flist', + frame={t=3, l=0, r=0, b=4}, + case_sensitive=false, + choices=self:get_choices(sort_by_recency), + icon_width=2, + on_submit=self:callback('toggle_group'), + edit_on_char=function(ch) return ch:match('[%l -]') end, + }, + widgets.CycleHotkeyLabel{ + frame={l=0, b=2}, + key='CUSTOM_SHIFT_R', + label='Sort by:', + options={ + {label='Recently used', value=sort_by_recency}, + {label='Name', value=sort_by_name}, + {label='Amount', value=sort_by_quantity}, + }, + on_change=self:callback('on_sort'), + }, + widgets.HotkeyLabel{ + frame={l=0, b=1}, + key='SELECT', + label='Use all/none', + auto_width=true, + on_activate=function() self:toggle_group(self.subviews.flist.list:getSelected()) end, + }, + widgets.HotkeyLabel{ + frame={l=22, b=1}, + key='CUSTOM_SHIFT_B', + label='Build', + auto_width=true, + on_activate=self:callback('submit'), + }, + widgets.HotkeyLabel{ + frame={l=38, b=1}, + key='LEAVESCREEN', + label='Go back', + auto_width=true, + on_activate=self:callback('on_cancel'), + }, + widgets.HotkeyLabel{ + frame={l=0, b=0}, + key='KEYBOARD_CURSOR_RIGHT_FAST', + key_sep=' : ', + label='Use one', + auto_width=true, + on_activate=function() self:increment_group(self.subviews.flist.list:getSelected()) end, + }, + widgets.Label{ + frame={l=6, b=0, w=5}, + text_pen=COLOR_LIGHTGREEN, + text='Right', + }, + widgets.HotkeyLabel{ + frame={l=23, b=0}, + key='KEYBOARD_CURSOR_LEFT_FAST', + key_sep=' : ', + label='Use one fewer', + auto_width=true, + on_activate=function() self:decrement_group(self.subviews.flist.list:getSelected()) end, + }, + widgets.Label{ + frame={l=29, b=0, w=4}, + text_pen=COLOR_LIGHTGREEN, + text='Left', + }, + } +end + +-- resort and restore selection +function ItemSelection:on_sort(sort_fn) + local flist = self.subviews.flist + local saved_filter = flist:getFilter() + flist:setFilter('') + flist:setChoices(self:get_choices(sort_fn), flist:getSelected()) + flist:setFilter(saved_filter) +end + +local function make_search_key(str) + local out = '' + for c in str:gmatch("[%w%s]") do + out = out .. c + end + return out +end + +function ItemSelection:get_choices(sort_fn) + local item_ids = require('plugins.buildingplan').getAvailableItems(uibs.building_type, + uibs.building_subtype, uibs.custom_type, self.index-1) + local buckets = {} + for _,item_id in ipairs(item_ids) do + local item = df.item.find(item_id) + if not item then goto continue end + local desc = dfhack.items.getDescription(item, 0, true) + if buckets[desc] then + local bucket = buckets[desc] + table.insert(bucket.data.item_ids, item_id) + bucket.data.quantity = bucket.data.quantity + 1 + else + local entry = { + search_key=make_search_key(desc), + icon=self:callback('get_entry_icon', item_id), + data={ + item_ids={item_id}, + item_type=item:getType(), + item_subtype=item:getSubtype(), + quantity=1, + quality=item:getQuality(), + selected=0, + }, + } + buckets[desc] = entry + end + ::continue:: + end + local choices = {} + for desc,choice in pairs(buckets) do + local data = choice.data + choice.text = { + {width=10, text=function() return ('[%d/%d]'):format(data.selected, data.quantity) end}, + {gap=2, text=desc}, + } + table.insert(choices, choice) + end + table.sort(choices, sort_fn) + return choices +end + +function ItemSelection:increment_group(idx, choice) + local data = choice.data + if self.quantity <= self.num_selected then return false end + if data.selected >= data.quantity then return false end + data.selected = data.selected + 1 + self.num_selected = self.num_selected + 1 + local item_id = data.item_ids[data.selected] + self.selected_set[item_id] = true + return true +end + +function ItemSelection:decrement_group(idx, choice) + local data = choice.data + if data.selected <= 0 then return false end + local item_id = data.item_ids[data.selected] + self.selected_set[item_id] = nil + self.num_selected = self.num_selected - 1 + data.selected = data.selected - 1 + return true +end + +function ItemSelection:toggle_group(idx, choice) + local data = choice.data + if data.selected > 0 then + while self:decrement_group(idx, choice) do end + else + while self:increment_group(idx, choice) do end + end +end + +function ItemSelection:get_entry_icon(item_id) + return self.selected_set[item_id] and pens.SELECTED_ITEM_PEN or nil +end + +local function track_recently_used(choices) + -- use same set for all subtypes + local tracker = ensure_key(recently_used, uibs.building_type) + for _,choice in ipairs(choices) do + local data = choice.data + if data.selected <= 0 then goto continue end + local key = choice.search_key + local recent_set = ensure_key(tracker, 'set') + local recent_list = ensure_key(tracker, 'list') + if recent_set[key] then + if recent_list[#recent_list] ~= key then + for i,v in ipairs(recent_list) do + if v == key then + table.remove(recent_list, i) + table.insert(recent_list, key) + break + end + end + tracker.set = utils.invert(recent_list) + end + else + -- only keep most recent 10 + if #recent_list >= 10 then + -- remove least recently used from list and set + recent_set[table.remove(recent_list, 1)] = nil + end + table.insert(recent_list, key) + recent_set[key] = #recent_list + end + ::continue:: + end +end + +function ItemSelection:submit() + local selected_items = {} + for item_id in pairs(self.selected_set) do + table.insert(selected_items, item_id) + end + if #selected_items > 0 then + track_recently_used(self.subviews.flist:getChoices()) + end + self.on_submit(selected_items) +end + +function ItemSelection:onInput(keys) + if keys.LEAVESCREEN or keys._MOUSE_R_DOWN then + self.on_cancel() + return true + elseif keys._MOUSE_L_DOWN then + local list = self.subviews.flist.list + local idx = list:getIdxUnderMouse() + if idx then + list:setSelected(idx) + local modstate = dfhack.internal.getModstate() + if modstate & 2 > 0 then -- ctrl + local choice = list:getChoices()[idx] + if modstate & 1 > 0 then -- shift + self:decrement_group(idx, choice) + else + self:increment_group(idx, choice) + end + return true + end + end + end + return ItemSelection.super.onInput(self, keys) +end + +ItemSelectionScreen = defclass(ItemSelectionScreen, gui.ZScreen) +ItemSelectionScreen.ATTRS { + focus_path='dwarfmode/Building/Placement/dfhack/lua/buildingplan/itemselection', + force_pause=true, + pass_movement_keys=true, + pass_pause=false, + pass_mouse_clicks=false, + defocusable=false, + index=DEFAULT_NIL, + desc=DEFAULT_NIL, + quantity=DEFAULT_NIL, + on_submit=DEFAULT_NIL, + on_cancel=DEFAULT_NIL, +} + +function ItemSelectionScreen:init() + self:addviews{ + ItemSelection{ + index=self.index, + desc=self.desc, + quantity=self.quantity, + on_submit=self.on_submit, + on_cancel=self.on_cancel, + } + } +end + +return _ENV diff --git a/plugins/lua/buildingplan/pens.lua b/plugins/lua/buildingplan/pens.lua new file mode 100644 index 000000000..d2198706f --- /dev/null +++ b/plugins/lua/buildingplan/pens.lua @@ -0,0 +1,31 @@ +local _ENV = mkmodule('plugins.buildingplan.pens') + +GOOD_TILE_PEN, BAD_TILE_PEN = nil, nil +VERT_TOP_PEN, VERT_MID_PEN, VERT_BOT_PEN = nil, nil, nil +BUTTON_START_PEN, BUTTON_END_PEN = nil, nil +SELECTED_ITEM_PEN = nil + +local to_pen = dfhack.pen.parse + +local tp = function(base, offset) + if base == -1 then return nil end + return base + offset +end + +function reload_pens() + GOOD_TILE_PEN = to_pen{ch='o', fg=COLOR_GREEN, tile=dfhack.screen.findGraphicsTile('CURSORS', 1, 2)} + BAD_TILE_PEN = to_pen{ch='X', fg=COLOR_RED, tile=dfhack.screen.findGraphicsTile('CURSORS', 3, 0)} + + local tb_texpos = dfhack.textures.getThinBordersTexposStart() + VERT_TOP_PEN = to_pen{tile=tp(tb_texpos, 10), ch=194, fg=COLOR_GREY, bg=COLOR_BLACK} + VERT_MID_PEN = to_pen{tile=tp(tb_texpos, 4), ch=192, fg=COLOR_GREY, bg=COLOR_BLACK} + VERT_BOT_PEN = to_pen{tile=tp(tb_texpos, 11), ch=179, fg=COLOR_GREY, bg=COLOR_BLACK} + + local cp_texpos = dfhack.textures.getControlPanelTexposStart() + BUTTON_START_PEN = to_pen{tile=tp(cp_texpos, 13), ch='[', fg=COLOR_YELLOW} + BUTTON_END_PEN = to_pen{tile=tp(cp_texpos, 15), ch=']', fg=COLOR_YELLOW} + SELECTED_ITEM_PEN = to_pen{tile=tp(cp_texpos, 9), ch=string.char(251), fg=COLOR_YELLOW} +end +reload_pens() + +return _ENV diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua new file mode 100644 index 000000000..864516436 --- /dev/null +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -0,0 +1,734 @@ +local _ENV = mkmodule('plugins.buildingplan.planneroverlay') + +local itemselection = require('plugins.buildingplan.itemselection') +local filterselection = require('plugins.buildingplan.filterselection') +local gui = require('gui') +local guidm = require('gui.dwarfmode') +local overlay = require('plugins.overlay') +local pens = require('plugins.buildingplan.pens') +local utils = require('utils') +local widgets = require('gui.widgets') +require('dfhack.buildings') + +local uibs = df.global.buildreq + +reset_counts_flag = false + +local function get_cur_filters() + return dfhack.buildings.getFiltersByType({}, uibs.building_type, + uibs.building_subtype, uibs.custom_type) +end + +local function is_choosing_area() + return uibs.selection_pos.x >= 0 +end + +local function get_cur_area_dims(placement_data) + if not placement_data and not is_choosing_area() then return 1, 1, 1 end + local selection_pos = placement_data and placement_data.p1 or uibs.selection_pos + local pos = placement_data and placement_data.p2 or uibs.pos + return math.abs(selection_pos.x - pos.x) + 1, + math.abs(selection_pos.y - pos.y) + 1, + math.abs(selection_pos.z - pos.z) + 1 +end + +local function get_quantity(filter, hollow, placement_data) + local quantity = filter.quantity or 1 + local dimx, dimy, dimz = get_cur_area_dims(placement_data) + if quantity < 1 then + return (((dimx * dimy) // 4) + 1) * dimz + end + if hollow and dimx > 2 and dimy > 2 then + return quantity * (2*dimx + 2*dimy - 4) * dimz + end + return quantity * dimx * dimy * dimz +end + +local function cur_building_has_no_area() + if uibs.building_type == df.building_type.Construction then return false end + local filters = dfhack.buildings.getFiltersByType({}, + uibs.building_type, uibs.building_subtype, uibs.custom_type) + -- this works because all variable-size buildings have either no item + -- filters or a quantity of -1 for their first (and only) item + return filters and filters[1] and (not filters[1].quantity or filters[1].quantity > 0) +end + +local function is_plannable() + return get_cur_filters() and + not (uibs.building_type == df.building_type.Construction + and uibs.building_subtype == df.construction_type.TrackNSEW) +end + +local function is_construction() + return uibs.building_type == df.building_type.Construction +end + +local function is_stairs() + return is_construction() + and uibs.building_subtype == df.construction_type.UpDownStair +end + +local direction_panel_frame = {t=4, h=13, w=46, r=28} + +local direction_panel_types = utils.invert{ + df.building_type.Bridge, + df.building_type.ScrewPump, + df.building_type.WaterWheel, + df.building_type.AxleHorizontal, + df.building_type.Rollers, +} + +local function has_direction_panel() + return direction_panel_types[uibs.building_type] + or (uibs.building_type == df.building_type.Trap + and uibs.building_subtype == df.trap_type.TrackStop) +end + +local pressure_plate_panel_frame = {t=4, h=37, w=46, r=28} + +local function has_pressure_plate_panel() + return uibs.building_type == df.building_type.Trap + and uibs.building_subtype == df.trap_type.PressurePlate +end + +local function is_over_options_panel() + local frame = nil + if has_direction_panel() then + frame = direction_panel_frame + elseif has_pressure_plate_panel() then + frame = pressure_plate_panel_frame + else + return false + end + local v = widgets.Widget{frame=frame} + local rect = gui.mkdims_wh(0, 0, dfhack.screen.getWindowSize()) + v:updateLayout(gui.ViewRect{rect=rect}) + return v:getMousePos() +end + +-------------------------------- +-- ItemLine +-- + +ItemLine = defclass(ItemLine, widgets.Panel) +ItemLine.ATTRS{ + idx=DEFAULT_NIL, + is_selected_fn=DEFAULT_NIL, + is_hollow_fn=DEFAULT_NIL, + on_select=DEFAULT_NIL, + on_filter=DEFAULT_NIL, + on_clear_filter=DEFAULT_NIL, +} + +function ItemLine:init() + self.frame.h = 1 + self.visible = function() return #get_cur_filters() >= self.idx end + self:addviews{ + widgets.Label{ + frame={t=0, l=0}, + text='*', + auto_width=true, + visible=self.is_selected_fn, + }, + widgets.Label{ + frame={t=0, l=25}, + text={ + {tile=pens.BUTTON_START_PEN}, + {gap=6, tile=pens.BUTTON_END_PEN}, + }, + auto_width=true, + on_click=function() self.on_filter(self.idx) end, + }, + widgets.Label{ + frame={t=0, l=33}, + text={ + {tile=pens.BUTTON_START_PEN}, + {gap=1, tile=pens.BUTTON_END_PEN}, + }, + auto_width=true, + on_click=function() self.on_clear_filter(self.idx) end, + }, + widgets.Label{ + frame={t=0, l=2}, + text={ + {width=21, text=self:callback('get_item_line_text')}, + {gap=3, text='filter', pen=COLOR_GREEN}, + {gap=2, text='x', pen=self:callback('get_x_pen')}, + {gap=3, text=function() return self.note end, + pen=function() return self.note_pen end}, + }, + }, + } +end + +function ItemLine:reset() + self.desc = nil + self.available = nil +end + +function ItemLine:onInput(keys) + if keys._MOUSE_L_DOWN and self:getMousePos() then + self.on_select(self.idx) + end + return ItemLine.super.onInput(self, keys) +end + +function ItemLine:get_x_pen() + return require('plugins.buildingplan').hasFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.idx-1) and + COLOR_GREEN or COLOR_GREY +end + +function ItemLine:get_item_line_text() + local idx = self.idx + local filter = get_cur_filters()[idx] + local quantity = get_quantity(filter, self.is_hollow_fn()) + + local buildingplan = require('plugins.buildingplan') + self.desc = self.desc or buildingplan.get_desc(filter) + + self.available = self.available or buildingplan.countAvailableItems( + uibs.building_type, uibs.building_subtype, uibs.custom_type, idx - 1) + if self.available >= quantity then + self.note_pen = COLOR_GREEN + self.note = 'Available now' + else + self.note_pen = COLOR_YELLOW + self.note = 'Will link later' + end + + return ('%d %s%s'):format(quantity, self.desc, quantity == 1 and '' or 's') +end + +function ItemLine:reduce_quantity(used_quantity) + if not self.available then return end + local filter = get_cur_filters()[self.idx] + used_quantity = used_quantity or get_quantity(filter, self.is_hollow_fn()) + self.available = math.max(0, self.available - used_quantity) +end + +local function get_placement_errors() + local out = '' + for _,str in ipairs(uibs.errors) do + if #out > 0 then out = out .. NEWLINE end + out = out .. str.value + end + return out +end + +-------------------------------- +-- PlannerOverlay +-- + +PlannerOverlay = defclass(PlannerOverlay, overlay.OverlayWidget) +PlannerOverlay.ATTRS{ + default_pos={x=5,y=9}, + default_enabled=true, + viewscreens='dwarfmode/Building/Placement', + frame={w=56, h=20}, +} + +function PlannerOverlay:init() + self.selected = 1 + + local main_panel = widgets.Panel{ + view_id='main', + frame={t=0, l=0, r=0, h=14}, + frame_style=gui.MEDIUM_FRAME, + frame_background=gui.CLEAR_PEN, + } + + local function make_is_selected_fn(idx) + return function() return self.selected == idx end + end + + local function on_select_fn(idx) + self.selected = idx + end + + local function is_hollow_fn() + return self.subviews.hollow:getOptionValue() + end + + local buildingplan = require('plugins.buildingplan') + + main_panel:addviews{ + widgets.Label{ + frame={}, + auto_width=true, + text='No items required.', + visible=function() return #get_cur_filters() == 0 end, + }, + ItemLine{view_id='item1', frame={t=0, l=0, r=0}, idx=1, + is_selected_fn=make_is_selected_fn(1), is_hollow_fn=is_hollow_fn, + on_select=on_select_fn, on_filter=self:callback('set_filter'), + on_clear_filter=self:callback('clear_filter')}, + ItemLine{view_id='item2', frame={t=2, l=0, r=0}, idx=2, + is_selected_fn=make_is_selected_fn(2), is_hollow_fn=is_hollow_fn, + on_select=on_select_fn, on_filter=self:callback('set_filter'), + on_clear_filter=self:callback('clear_filter')}, + ItemLine{view_id='item3', frame={t=4, l=0, r=0}, idx=3, + is_selected_fn=make_is_selected_fn(3), is_hollow_fn=is_hollow_fn, + on_select=on_select_fn, on_filter=self:callback('set_filter'), + on_clear_filter=self:callback('clear_filter')}, + ItemLine{view_id='item4', frame={t=6, l=0, r=0}, idx=4, + is_selected_fn=make_is_selected_fn(4), is_hollow_fn=is_hollow_fn, + on_select=on_select_fn, on_filter=self:callback('set_filter'), + on_clear_filter=self:callback('clear_filter')}, + widgets.CycleHotkeyLabel{ + view_id='hollow', + frame={t=3, l=4}, + key='CUSTOM_H', + label='Hollow area:', + visible=is_construction, + options={ + {label='No', value=false}, + {label='Yes', value=true}, + }, + }, + widgets.CycleHotkeyLabel{ + view_id='stairs_top_subtype', + frame={t=4, l=4}, + key='CUSTOM_R', + label='Top Stair Type: ', + visible=is_stairs, + options={ + {label='Auto', value='auto'}, + {label='UpDown', value=df.construction_type.UpDownStair}, + {label='Down', value=df.construction_type.DownStair}, + }, + }, + widgets.CycleHotkeyLabel { + view_id='stairs_bottom_subtype', + frame={t=5, l=4}, + key='CUSTOM_B', + label='Bottom Stair Type: ', + visible=is_stairs, + options={ + {label='Auto', value='auto'}, + {label='UpDown', value=df.construction_type.UpDownStair}, + {label='Up', value=df.construction_type.UpStair}, + }, + }, + widgets.Label{ + frame={b=3, l=17}, + text={ + 'Selected area: ', + {text=function() + return ('%dx%dx%d'):format(get_cur_area_dims(self.saved_placement)) + end + }, + }, + visible=function() + return not cur_building_has_no_area() and (self.saved_placement or is_choosing_area()) + end, + }, + widgets.Panel{ + visible=function() return #get_cur_filters() > 0 end, + subviews={ + widgets.HotkeyLabel{ + frame={b=1, l=0}, + key='STRING_A042', + auto_width=true, + enabled=function() return #get_cur_filters() > 1 end, + on_activate=function() self.selected = ((self.selected - 2) % #get_cur_filters()) + 1 end, + }, + widgets.HotkeyLabel{ + frame={b=1, l=1}, + key='STRING_A047', + label='Prev/next item', + auto_width=true, + enabled=function() return #get_cur_filters() > 1 end, + on_activate=function() self.selected = (self.selected % #get_cur_filters()) + 1 end, + }, + widgets.HotkeyLabel{ + frame={b=1, l=21}, + key='CUSTOM_F', + label='Set filter', + auto_width=true, + on_activate=function() self:set_filter(self.selected) end, + }, + widgets.HotkeyLabel{ + frame={b=1, l=37}, + key='CUSTOM_X', + label='Clear filter', + auto_width=true, + on_activate=function() self:clear_filter(self.selected) end, + enabled=function() + return buildingplan.hasFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.selected - 1) + end + }, + widgets.CycleHotkeyLabel{ + view_id='choose', + frame={b=0, l=0, w=25}, + key='CUSTOM_I', + label='Choose from items:', + options={{label='Yes', value=true}, + {label='No', value=false}}, + initial_option=false, + enabled=function() + for idx = 1,4 do + if (self.subviews['item'..idx].available or 0) > 0 then + return true + end + end + end, + }, + widgets.CycleHotkeyLabel{ + view_id='safety', + frame={b=0, l=29, w=25}, + key='CUSTOM_G', + label='Building safety:', + options={ + {label='Any', value=0}, + {label='Magma', value=2, pen=COLOR_RED}, + {label='Fire', value=1, pen=COLOR_LIGHTRED}, + }, + initial_option=0, + on_change=function(heat) + buildingplan.setHeatSafetyFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, heat) + end, + }, + }, + }, + } + + local error_panel = widgets.ResizingPanel{ + view_id='errors', + frame={t=14, l=0, r=0}, + frame_style=gui.MEDIUM_FRAME, + frame_background=gui.CLEAR_PEN, + } + + error_panel:addviews{ + widgets.WrappedLabel{ + frame={t=0, l=0, r=0}, + text_pen=COLOR_LIGHTRED, + text_to_wrap=get_placement_errors, + visible=function() return #uibs.errors > 0 end, + }, + widgets.Label{ + frame={t=0, l=0, r=0}, + text_pen=COLOR_GREEN, + text='OK to build', + visible=function() return #uibs.errors == 0 end, + }, + } + + self:addviews{ + main_panel, + error_panel, + } +end + +function PlannerOverlay:reset() + self.subviews.item1:reset() + self.subviews.item2:reset() + self.subviews.item3:reset() + self.subviews.item4:reset() + reset_counts_flag = false +end + +function PlannerOverlay:set_filter(idx) + filterselection.FilterSelectionScreen{index=idx, desc=require('plugins.buildingplan').get_desc(get_cur_filters()[idx])}:show() +end + +function PlannerOverlay:clear_filter(idx) + clearFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, idx-1) +end + +local function get_placement_data() + local pos = uibs.pos + local direction = uibs.direction + local width, height, depth = get_cur_area_dims() + local _, adjusted_width, adjusted_height = dfhack.buildings.getCorrectSize( + width, height, uibs.building_type, uibs.building_subtype, + uibs.custom_type, direction) + -- get the upper-left corner of the building/area at min z-level + local has_selection = is_choosing_area() + local start_pos = xyz2pos( + has_selection and math.min(uibs.selection_pos.x, pos.x) or pos.x - adjusted_width//2, + has_selection and math.min(uibs.selection_pos.y, pos.y) or pos.y - adjusted_height//2, + has_selection and math.min(uibs.selection_pos.z, pos.z) or pos.z + ) + if uibs.building_type == df.building_type.ScrewPump then + if direction == df.screw_pump_direction.FromSouth then + start_pos.y = start_pos.y + 1 + elseif direction == df.screw_pump_direction.FromEast then + start_pos.x = start_pos.x + 1 + end + end + local min_x, max_x = start_pos.x, start_pos.x + local min_y, max_y = start_pos.y, start_pos.y + local min_z, max_z = start_pos.z, start_pos.z + if adjusted_width == 1 and adjusted_height == 1 + and (width > 1 or height > 1 or depth > 1) then + max_x = min_x + width - 1 + max_y = min_y + height - 1 + max_z = math.max(uibs.selection_pos.z, pos.z) + end + return { + p1=xyz2pos(min_x, min_y, min_z), + p2=xyz2pos(max_x, max_y, max_z), + width=adjusted_width, + height=adjusted_height + } +end + +function PlannerOverlay:save_placement() + self.saved_placement = get_placement_data() + if (uibs.selection_pos:isValid()) then + self.saved_selection_pos_valid = true + self.saved_selection_pos = copyall(uibs.selection_pos) + self.saved_pos = copyall(uibs.pos) + uibs.selection_pos:clear() + else + self.saved_selection_pos = copyall(self.saved_placement.p1) + self.saved_pos = copyall(self.saved_placement.p2) + self.saved_pos.x = self.saved_pos.x + self.saved_placement.width - 1 + self.saved_pos.y = self.saved_pos.y + self.saved_placement.height - 1 + end +end + +function PlannerOverlay:restore_placement() + if self.saved_selection_pos_valid then + uibs.selection_pos = self.saved_selection_pos + self.saved_selection_pos_valid = nil + else + uibs.selection_pos:clear() + end + self.saved_selection_pos = nil + self.saved_pos = nil + local placement_data = self.saved_placement + self.saved_placement = nil + return placement_data +end + +function PlannerOverlay:onInput(keys) + if not is_plannable() then return false end + if keys.LEAVESCREEN or keys._MOUSE_R_DOWN then + if uibs.selection_pos:isValid() then + uibs.selection_pos:clear() + return true + end + self.selected = 1 + self.subviews.hollow:setOption(false) + self.subviews.choose:setOption(false) + self:reset() + reset_counts_flag = true + return false + end + if PlannerOverlay.super.onInput(self, keys) then + return true + end + if keys._MOUSE_L_DOWN then + if is_over_options_panel() then return false end + local detect_rect = copyall(self.frame_rect) + detect_rect.height = self.subviews.main.frame_rect.height + + self.subviews.errors.frame_rect.height + detect_rect.y2 = detect_rect.y1 + detect_rect.height - 1 + if self.subviews.main:getMousePos(gui.ViewRect{rect=detect_rect}) + or self.subviews.errors:getMousePos() then + return true + end + if not is_construction() and #uibs.errors > 0 then return true end + if dfhack.gui.getMousePos() then + if is_choosing_area() or cur_building_has_no_area() then + local filters = get_cur_filters() + local num_filters = #filters + local choose = self.subviews.choose + if choose.enabled() and choose:getOptionValue() then + self:save_placement() + local is_hollow = self.subviews.hollow:getOptionValue() + local chosen_items, active_screens = {}, {} + local pending = num_filters + df.global.game.main_interface.bottom_mode_selected = -1 + for idx = num_filters,1,-1 do + chosen_items[idx] = {} + if (self.subviews['item'..idx].available or 0) > 0 then + local filter = filters[idx] + active_screens[idx] = itemselection.ItemSelectionScreen{ + index=idx, + desc=require('plugins.buildingplan').get_desc(filter), + quantity=get_quantity(filter, is_hollow, + self.saved_placement), + on_submit=function(items) + chosen_items[idx] = items + active_screens[idx]:dismiss() + active_screens[idx] = nil + pending = pending - 1 + if pending == 0 then + df.global.game.main_interface.bottom_mode_selected = df.main_bottom_mode_type.BUILDING_PLACEMENT + self:place_building(self:restore_placement(), chosen_items) + end + end, + on_cancel=function() + for i,scr in pairs(active_screens) do + scr:dismiss() + end + df.global.game.main_interface.bottom_mode_selected = df.main_bottom_mode_type.BUILDING_PLACEMENT + self:restore_placement() + end, + }:show() + else + pending = pending - 1 + end + end + else + self:place_building(get_placement_data()) + end + return true + elseif not is_choosing_area() then + return false + end + end + end + return keys._MOUSE_L or keys.SELECT +end + +function PlannerOverlay:render(dc) + if not is_plannable() then return end + self.subviews.errors:updateLayout() + PlannerOverlay.super.render(self, dc) +end + +local ONE_BY_ONE = xy2pos(1, 1) + +function PlannerOverlay:onRenderFrame(dc, rect) + PlannerOverlay.super.onRenderFrame(self, dc, rect) + + if reset_counts_flag then + self:reset() + self.subviews.safety:setOption(require('plugins.buildingplan').getHeatSafetyFilter( + uibs.building_type, uibs.building_subtype, uibs.custom_type)) + end + + local selection_pos = self.saved_selection_pos or uibs.selection_pos + if not selection_pos or selection_pos.x < 0 then return end + + local pos = self.saved_pos or uibs.pos + local bounds = { + x1 = math.max(0, math.min(selection_pos.x, pos.x)), + x2 = math.min(df.global.world.map.x_count-1, math.max(selection_pos.x, pos.x)), + y1 = math.max(0, math.min(selection_pos.y, pos.y)), + y2 = math.min(df.global.world.map.y_count-1, math.max(selection_pos.y, pos.y)), + } + + local hollow = self.subviews.hollow:getOptionValue() + local default_pen = (self.saved_selection_pos or #uibs.errors == 0) and GOOD_PEN or BAD_PEN + + local get_pen_fn = is_construction() and function(pos) + return dfhack.buildings.checkFreeTiles(pos, ONE_BY_ONE) and GOOD_PEN or BAD_PEN + end or function() + return default_pen + end + + local function get_overlay_pen(pos) + if not hollow then return get_pen_fn(pos) end + if pos.x == bounds.x1 or pos.x == bounds.x2 or + pos.y == bounds.y1 or pos.y == bounds.y2 then + return get_pen_fn(pos) + end + return gui.TRANSPARENT_PEN + end + + guidm.renderMapOverlay(get_overlay_pen, bounds) +end + +function PlannerOverlay:get_stairs_subtype(pos, corner1, corner2) + local subtype = uibs.building_subtype + if pos.z == corner1.z then + local opt = self.subviews.stairs_bottom_subtype:getOptionValue() + if opt == 'auto' then + local tt = dfhack.maps.getTileType(pos) + local shape = df.tiletype.attrs[tt].shape + if shape ~= df.tiletype_shape.STAIR_DOWN then + subtype = df.construction_type.UpStair + end + else + subtype = opt + end + elseif pos.z == corner2.z then + local opt = self.subviews.stairs_top_subtype:getOptionValue() + if opt == 'auto' then + local tt = dfhack.maps.getTileType(pos) + local shape = df.tiletype.attrs[tt].shape + if shape ~= df.tiletype_shape.STAIR_UP then + subtype = df.construction_type.DownStair + end + else + subtype = opt + end + end + return subtype +end + +function PlannerOverlay:place_building(placement_data, chosen_items) + local p1, p2 = placement_data.p1, placement_data.p2 + local blds = {} + local hollow = self.subviews.hollow:getOptionValue() + local subtype = uibs.building_subtype + for z=p1.z,p2.z do for y=p1.y,p2.y do for x=p1.x,p2.x do + if hollow and x ~= p1.x and x ~= p2.x and y ~= p1.y and y ~= p2.y then + goto continue + end + local pos = xyz2pos(x, y, z) + if is_stairs() then + subtype = self:get_stairs_subtype(pos, p1, p2) + end + local bld, err = dfhack.buildings.constructBuilding{pos=pos, + type=uibs.building_type, subtype=subtype, custom=uibs.custom_type, + width=placement_data.width, height=placement_data.height, + direction=uibs.direction} + if err then + -- it's ok if some buildings fail to build + goto continue + end + -- assign fields for the types that need them. we can't pass them all in + -- to the call to constructBuilding since attempting to assign unrelated + -- fields to building types that don't support them causes errors. + for k,v in pairs(bld) do + if k == 'friction' then bld.friction = uibs.friction end + if k == 'use_dump' then bld.use_dump = uibs.use_dump end + if k == 'dump_x_shift' then bld.dump_x_shift = uibs.dump_x_shift end + if k == 'dump_y_shift' then bld.dump_y_shift = uibs.dump_y_shift end + if k == 'speed' then bld.speed = uibs.speed end + end + table.insert(blds, bld) + ::continue:: + end end end + local used_quantity = is_construction() and #blds or false + self.subviews.item1:reduce_quantity(used_quantity) + self.subviews.item2:reduce_quantity(used_quantity) + self.subviews.item3:reduce_quantity(used_quantity) + self.subviews.item4:reduce_quantity(used_quantity) + local buildingplan = require('plugins.buildingplan') + for _,bld in ipairs(blds) do + -- attach chosen items and reduce job_item quantity + if chosen_items then + local job = bld.jobs[0] + local jitems = job.job_items + for idx=1,#get_cur_filters() do + local item_ids = chosen_items[idx] + while jitems[idx-1].quantity > 0 and #item_ids > 0 do + local item_id = item_ids[#item_ids] + local item = df.item.find(item_id) + if not item then + dfhack.printerr(('item no longer available: %d'):format(item_id)) + break + end + if not dfhack.job.attachJobItem(job, item, df.job_item_ref.T_role.Hauled, idx-1, -1) then + dfhack.printerr(('cannot attach item: %d'):format(item_id)) + break + end + jitems[idx-1].quantity = jitems[idx-1].quantity - 1 + item_ids[#item_ids] = nil + end + end + end + buildingplan.addPlannedBuilding(bld) + end + buildingplan.scheduleCycle() + uibs.selection_pos:clear() +end + +return _ENV From 2818f2334972867c8a8ca9efa9a3291f116b4378 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 10 Mar 2023 07:15:52 +0000 Subject: [PATCH 20/28] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 09ddae3c2..267eb20f7 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 09ddae3c2851f207c7257b4767182f4c6c938f18 +Subproject commit 267eb20f75e891fc0ade166e2d316498f454d913 diff --git a/scripts b/scripts index c8ceb198b..89a345d66 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit c8ceb198b4fc62155b31a5554b9c96cc6bcb8d55 +Subproject commit 89a345d66d70eb02293bd2e46dcfad65f0229ee6 From 44fb91056bc59f89b7eb5a3cb0f5dc5a4f6b1f42 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 9 Mar 2023 23:41:13 -0800 Subject: [PATCH 21/28] ensure pressure plate config gets copied to the planned building --- plugins/lua/buildingplan/planneroverlay.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index 864516436..d90285141 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -692,6 +692,7 @@ function PlannerOverlay:place_building(placement_data, chosen_items) if k == 'dump_x_shift' then bld.dump_x_shift = uibs.dump_x_shift end if k == 'dump_y_shift' then bld.dump_y_shift = uibs.dump_y_shift end if k == 'speed' then bld.speed = uibs.speed end + if k == 'plate_info' then utils.assign(bld.plate_info, uibs.plate_info) end end table.insert(blds, bld) ::continue:: From 2f0dc4bd256fc5d03ae40e3020917ea5f2d15c8b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 10 Mar 2023 00:09:12 -0800 Subject: [PATCH 22/28] require correct number of mechanisms when building pressure plates --- plugins/lua/buildingplan/planneroverlay.lua | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index d90285141..41da7a429 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -32,7 +32,17 @@ local function get_cur_area_dims(placement_data) math.abs(selection_pos.z - pos.z) + 1 end +local function is_pressure_plate() + return uibs.building_type == df.building_type.Trap + and uibs.building_subtype == df.trap_type.PressurePlate +end + local function get_quantity(filter, hollow, placement_data) + if is_pressure_plate() then + local flags = uibs.plate_info.flags + return (flags.units and 1 or 0) + (flags.water and 1 or 0) + + (flags.magma and 1 or 0) + (flags.track and 1 or 0) + end local quantity = filter.quantity or 1 local dimx, dimy, dimz = get_cur_area_dims(placement_data) if quantity < 1 then @@ -87,8 +97,7 @@ end local pressure_plate_panel_frame = {t=4, h=37, w=46, r=28} local function has_pressure_plate_panel() - return uibs.building_type == df.building_type.Trap - and uibs.building_subtype == df.trap_type.PressurePlate + return is_pressure_plate() end local function is_over_options_panel() @@ -667,6 +676,10 @@ function PlannerOverlay:place_building(placement_data, chosen_items) local blds = {} local hollow = self.subviews.hollow:getOptionValue() local subtype = uibs.building_subtype + local filters = get_cur_filters() + if is_pressure_plate() then + filters[1].quantity = get_quantity() + end for z=p1.z,p2.z do for y=p1.y,p2.y do for x=p1.x,p2.x do if hollow and x ~= p1.x and x ~= p2.x and y ~= p1.y and y ~= p2.y then goto continue @@ -678,7 +691,7 @@ function PlannerOverlay:place_building(placement_data, chosen_items) local bld, err = dfhack.buildings.constructBuilding{pos=pos, type=uibs.building_type, subtype=subtype, custom=uibs.custom_type, width=placement_data.width, height=placement_data.height, - direction=uibs.direction} + direction=uibs.direction, filters=filters} if err then -- it's ok if some buildings fail to build goto continue From a3f8be3c012bd98a30d6878e6f273a8feeb07f2e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 10 Mar 2023 00:12:56 -0800 Subject: [PATCH 23/28] fix pens reference --- plugins/lua/buildingplan/planneroverlay.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index 864516436..de25af1f9 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -614,10 +614,10 @@ function PlannerOverlay:onRenderFrame(dc, rect) } local hollow = self.subviews.hollow:getOptionValue() - local default_pen = (self.saved_selection_pos or #uibs.errors == 0) and GOOD_PEN or BAD_PEN + local default_pen = (self.saved_selection_pos or #uibs.errors == 0) and pens.GOOD_TILE_PEN or pens.BAD_TILE_PEN local get_pen_fn = is_construction() and function(pos) - return dfhack.buildings.checkFreeTiles(pos, ONE_BY_ONE) and GOOD_PEN or BAD_PEN + return dfhack.buildings.checkFreeTiles(pos, ONE_BY_ONE) and pens.GOOD_TILE_PEN or pens.BAD_TILE_PEN end or function() return default_pen end From b884fab7b6f533c21fab125cb7f044ddeadad995 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 10 Mar 2023 01:04:23 -0800 Subject: [PATCH 24/28] fix freeze when printing status and there are defunct planned buildings --- plugins/buildingplan/buildingplan.cpp | 12 ++++++++---- plugins/buildingplan/plannedbuilding.cpp | 5 +++-- plugins/buildingplan/plannedbuilding.h | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 310d03a5e..2b67c2353 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -516,16 +516,20 @@ static void printStatus(color_ostream &out) { out.print(" use bars: %s\n", get_config_bool(config, CONFIG_BARS) ? "yes" : "no"); out.print("\n"); + size_t bld_count = 0; map counts; int32_t total = 0; for (auto &entry : planned_buildings) { auto &pb = entry.second; - auto bld = pb.getBuildingIfValidOrRemoveIfNot(out); + // don't actually remove bad buildings from the list while we're + // actively iterating through that list + auto bld = pb.getBuildingIfValidOrRemoveIfNot(out, true); if (!bld || bld->jobs.size() != 1) continue; auto &job_items = bld->jobs[0]->job_items; if (job_items.size() != pb.vector_ids.size()) continue; + ++bld_count; int job_item_idx = 0; for (auto &vec_ids : pb.vector_ids) { auto &jitem = job_items[job_item_idx++]; @@ -537,9 +541,9 @@ static void printStatus(color_ostream &out) { } } - if (planned_buildings.size()) { + if (bld_count) { out.print("Waiting for %d item(s) to be produced for %zd building(s):\n", - total, planned_buildings.size()); + total, bld_count); for (auto &count : counts) out.print(" %3d %s%s\n", count.second, count.first.c_str(), count.second == 1 ? "" : "s"); } else { @@ -949,7 +953,7 @@ static string getDescString(color_ostream &out, df::building *bld, int index) { PlannedBuilding &pb = planned_buildings.at(bld->id); auto &jitem = bld->jobs[0]->job_items[index]; - return get_desc_string(out, jitem, pb.vector_ids[index]); + return int_to_string(jitem->quantity) + " " + get_desc_string(out, jitem, pb.vector_ids[index]); } static int getQueuePosition(color_ostream &out, df::building *bld, int index) { diff --git a/plugins/buildingplan/plannedbuilding.cpp b/plugins/buildingplan/plannedbuilding.cpp index a693f6fcf..e678c850f 100644 --- a/plugins/buildingplan/plannedbuilding.cpp +++ b/plugins/buildingplan/plannedbuilding.cpp @@ -99,11 +99,12 @@ PlannedBuilding::PlannedBuilding(color_ostream &out, PersistentDataItem &bld_con // Ensure the building still exists and is in a valid state. It can disappear // for lots of reasons, such as running the game with the buildingplan plugin // disabled, manually removing the building, modifying it via the API, etc. -df::building * PlannedBuilding::getBuildingIfValidOrRemoveIfNot(color_ostream &out) { +df::building * PlannedBuilding::getBuildingIfValidOrRemoveIfNot(color_ostream &out, bool skip_remove) { auto bld = df::building::find(id); bool valid = bld && bld->getBuildStage() == 0; if (!valid) { - remove(out); + if (!skip_remove) + remove(out); return NULL; } return bld; diff --git a/plugins/buildingplan/plannedbuilding.h b/plugins/buildingplan/plannedbuilding.h index 5bd09ba5a..59dc24a79 100644 --- a/plugins/buildingplan/plannedbuilding.h +++ b/plugins/buildingplan/plannedbuilding.h @@ -29,7 +29,7 @@ public: // Ensure the building still exists and is in a valid state. It can disappear // for lots of reasons, such as running the game with the buildingplan plugin // disabled, manually removing the building, modifying it via the API, etc. - df::building * getBuildingIfValidOrRemoveIfNot(DFHack::color_ostream &out); + df::building * getBuildingIfValidOrRemoveIfNot(DFHack::color_ostream &out, bool skip_remove = false); private: DFHack::PersistentDataItem bld_config; From 43b423cd313a5ebc1a28a6e562b3629a92e84f2a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 10 Mar 2023 01:44:33 -0800 Subject: [PATCH 25/28] make number of weapons in trap configurable --- plugins/lua/buildingplan/planneroverlay.lua | 36 +++++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index fe3cebd83..924080ec3 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -37,11 +37,21 @@ local function is_pressure_plate() and uibs.building_subtype == df.trap_type.PressurePlate end +local function is_weapon_trap() + return uibs.building_type == df.building_type.Trap + and uibs.building_subtype == df.trap_type.WeaponTrap +end + +-- adjusted from CycleHotkeyLabel on the planner panel +local weapon_quantity = 1 + local function get_quantity(filter, hollow, placement_data) if is_pressure_plate() then local flags = uibs.plate_info.flags return (flags.units and 1 or 0) + (flags.water and 1 or 0) + (flags.magma and 1 or 0) + (flags.track and 1 or 0) + elseif is_weapon_trap() and filter.vector_id == df.job_item_vector_id.ANY_WEAPON then + return weapon_quantity end local quantity = filter.quantity or 1 local dimx, dimy, dimz = get_cur_area_dims(placement_data) @@ -298,7 +308,7 @@ function PlannerOverlay:init() view_id='stairs_top_subtype', frame={t=4, l=4}, key='CUSTOM_R', - label='Top Stair Type: ', + label='Top Stair Type: ', visible=is_stairs, options={ {label='Auto', value='auto'}, @@ -310,7 +320,7 @@ function PlannerOverlay:init() view_id='stairs_bottom_subtype', frame={t=5, l=4}, key='CUSTOM_B', - label='Bottom Stair Type: ', + label='Bottom Stair Type:', visible=is_stairs, options={ {label='Auto', value='auto'}, @@ -318,6 +328,16 @@ function PlannerOverlay:init() {label='Up', value=df.construction_type.UpStair}, }, }, + widgets.CycleHotkeyLabel { + view_id='weapons', + frame={t=5, l=4}, + key='CUSTOM_T', + key_back='CUSTOM_SHIFT_T', + label='Num weapons:', + visible=is_weapon_trap, + options={1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + on_change=function(val) weapon_quantity = val end, + }, widgets.Label{ frame={b=3, l=17}, text={ @@ -678,7 +698,9 @@ function PlannerOverlay:place_building(placement_data, chosen_items) local subtype = uibs.building_subtype local filters = get_cur_filters() if is_pressure_plate() then - filters[1].quantity = get_quantity() + filters[1].quantity = get_quantity(filters[1]) + elseif is_weapon_trap() then + filters[2].quantity = get_quantity(filters[2]) end for z=p1.z,p2.z do for y=p1.y,p2.y do for x=p1.x,p2.x do if hollow and x ~= p1.x and x ~= p2.x and y ~= p1.y and y ~= p2.y then @@ -721,9 +743,11 @@ function PlannerOverlay:place_building(placement_data, chosen_items) if chosen_items then local job = bld.jobs[0] local jitems = job.job_items - for idx=1,#get_cur_filters() do + local num_filters = #get_cur_filters() + for idx=1,num_filters do local item_ids = chosen_items[idx] - while jitems[idx-1].quantity > 0 and #item_ids > 0 do + local jitem = jitems[num_filters-idx] + while jitem.quantity > 0 and #item_ids > 0 do local item_id = item_ids[#item_ids] local item = df.item.find(item_id) if not item then @@ -734,7 +758,7 @@ function PlannerOverlay:place_building(placement_data, chosen_items) dfhack.printerr(('cannot attach item: %d'):format(item_id)) break end - jitems[idx-1].quantity = jitems[idx-1].quantity - 1 + jitem.quantity = jitem.quantity - 1 item_ids[#item_ids] = nil end end From 6ad2922aca5897991ac164ff37ce5669d963b8ab Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 10 Mar 2023 02:34:05 -0800 Subject: [PATCH 26/28] filter displayed materials if building heat safety is set --- plugins/buildingplan/buildingplan.cpp | 18 ++++++++++++++++-- plugins/buildingplan/buildingplan.h | 1 + plugins/buildingplan/buildingplan_cycle.cpp | 17 +++++++++++------ 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 2b67c2353..abc3c6ebd 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -847,11 +847,25 @@ static int getMaterialFilter(lua_State *L) { const auto &mat_filter = filters[index].getMaterials(); map counts; scanAvailableItems(*out, type, subtype, custom, index, NULL, &counts); - // name -> {count=int, enabled=bool, category=string} + HeatSafety heat = get_heat_safety_filter(key); + df::job_item jitem_cur_heat = getJobItemWithHeatSafety( + get_job_items(*out, key)[index], heat); + df::job_item jitem_fire = getJobItemWithHeatSafety( + get_job_items(*out, key)[index], HEAT_SAFETY_FIRE); + df::job_item jitem_magma = getJobItemWithHeatSafety( + get_job_items(*out, key)[index], HEAT_SAFETY_MAGMA); + // name -> {count=int, enabled=bool, category=string, heat=string} map> ret; for (auto & entry : mat_cache) { - auto &name = entry.first; auto &mat = entry.second.first; + if (!mat.matches(jitem_cur_heat)) + continue; + string heat_safety = ""; + if (mat.matches(jitem_magma)) + heat_safety = "magma-safe"; + else if (mat.matches(jitem_fire)) + heat_safety = "fire-safe"; + auto &name = entry.first; auto &cat = entry.second.second; map props; string count = "0"; diff --git a/plugins/buildingplan/buildingplan.h b/plugins/buildingplan/buildingplan.h index eef9808e6..0d0d5b45f 100644 --- a/plugins/buildingplan/buildingplan.h +++ b/plugins/buildingplan/buildingplan.h @@ -47,6 +47,7 @@ void set_config_bool(DFHack::PersistentDataItem &c, int index, bool value); std::vector getVectorIds(DFHack::color_ostream &out, const df::job_item *job_item); bool itemPassesScreen(df::item * item); +df::job_item getJobItemWithHeatSafety(const df::job_item *job_item, HeatSafety heat); bool matchesFilters(df::item * item, const df::job_item * job_item, HeatSafety heat, const ItemFilter &item_filter); bool isJobReady(DFHack::color_ostream &out, const std::vector &jitems); void finalizeBuilding(DFHack::color_ostream &out, df::building *bld); diff --git a/plugins/buildingplan/buildingplan_cycle.cpp b/plugins/buildingplan/buildingplan_cycle.cpp index f401c90a8..c08324e8a 100644 --- a/plugins/buildingplan/buildingplan_cycle.cpp +++ b/plugins/buildingplan/buildingplan_cycle.cpp @@ -48,6 +48,16 @@ bool itemPassesScreen(df::item * item) { && !item->isAssignedToStockpile(); } +df::job_item getJobItemWithHeatSafety(const df::job_item *job_item, HeatSafety heat) { + df::job_item jitem = *job_item; + if (heat >= HEAT_SAFETY_MAGMA) { + jitem.flags2.bits.magma_safe = true; + jitem.flags2.bits.fire_safe = false; + } else if (heat == HEAT_SAFETY_FIRE && !jitem.flags2.bits.magma_safe) + jitem.flags2.bits.fire_safe = true; + return jitem; +} + bool matchesFilters(df::item * item, const df::job_item * job_item, HeatSafety heat, const ItemFilter &item_filter) { // check the properties that are not checked by Job::isSuitableItem() if (job_item->item_type > -1 && job_item->item_type != item->getType()) @@ -67,12 +77,7 @@ bool matchesFilters(df::item * item, const df::job_item * job_item, HeatSafety h && !item->hasToolUse(job_item->has_tool_use)) return false; - df::job_item jitem = *job_item; - if (heat == HEAT_SAFETY_MAGMA) { - jitem.flags2.bits.magma_safe = true; - jitem.flags2.bits.fire_safe = false; - } else if (heat == HEAT_SAFETY_FIRE && !jitem.flags2.bits.magma_safe) - jitem.flags2.bits.fire_safe = true; + df::job_item jitem = getJobItemWithHeatSafety(job_item, heat); return Job::isSuitableItem( &jitem, item->getType(), item->getSubtype()) From c56e24803453e267b2e6cda0f984f099de370a16 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 10 Mar 2023 14:08:31 -0800 Subject: [PATCH 27/28] cleanup stockpiles plugin and don't bork on empty type vectors --- plugins/stockpiles/OrganicMatLookup.cpp | 164 +- plugins/stockpiles/OrganicMatLookup.h | 34 +- plugins/stockpiles/StockpileSerializer.cpp | 2322 ++++++++------------ plugins/stockpiles/StockpileSerializer.h | 528 ++--- plugins/stockpiles/StockpileUtils.h | 46 +- plugins/stockpiles/stockpiles.cpp | 121 +- 6 files changed, 1328 insertions(+), 1887 deletions(-) diff --git a/plugins/stockpiles/OrganicMatLookup.cpp b/plugins/stockpiles/OrganicMatLookup.cpp index 5167ece4a..2367eddb5 100644 --- a/plugins/stockpiles/OrganicMatLookup.cpp +++ b/plugins/stockpiles/OrganicMatLookup.cpp @@ -1,154 +1,126 @@ #include "OrganicMatLookup.h" - #include "StockpileUtils.h" -#include "modules/Materials.h" -#include "MiscUtils.h" - -#include "df/world.h" -#include "df/world_data.h" +#include "Debug.h" #include "df/creature_raw.h" #include "df/caste_raw.h" -#include "df/material.h" +#include "df/world.h" using namespace DFHack; using namespace df::enums; using df::global::world; -using std::endl; +namespace DFHack { + DBG_EXTERN(stockpiles, log); +} /** * Helper class for mapping the various organic mats between their material indices * and their index in the stockpile_settings structures. */ -void OrganicMatLookup::food_mat_by_idx ( std::ostream &out, organic_mat_category::organic_mat_category mat_category, std::vector::size_type food_idx, FoodMat & food_mat ) -{ - out << "food_lookup: food_idx(" << food_idx << ") "; - df::world_raws &raws = world->raws; +void OrganicMatLookup::food_mat_by_idx(organic_mat_category::organic_mat_category mat_category, std::vector::size_type food_idx, FoodMat& food_mat) { + DEBUG(log).print("food_lookup: food_idx(%zd) ", food_idx); + df::world_raws& raws = world->raws; df::special_mat_table table = raws.mat_table; int32_t main_idx = table.organic_indexes[mat_category][food_idx]; int16_t type = table.organic_types[mat_category][food_idx]; - if ( mat_category == organic_mat_category::Fish || - mat_category == organic_mat_category::UnpreparedFish || - mat_category == organic_mat_category::Eggs ) - { + if (mat_category == organic_mat_category::Fish || + mat_category == organic_mat_category::UnpreparedFish || + mat_category == organic_mat_category::Eggs) { food_mat.creature = raws.creatures.all[type]; food_mat.caste = food_mat.creature->caste[main_idx]; - out << " special creature type(" << type << ") caste("<< main_idx <<")" <::size_type idx ) -{ +std::string OrganicMatLookup::food_token_by_idx(organic_mat_category::organic_mat_category mat_category, std::vector::size_type idx) { FoodMat food_mat; - food_mat_by_idx ( out, mat_category, idx, food_mat ); - if ( food_mat.material.isValid() ) - { + food_mat_by_idx(mat_category, idx, food_mat); + if (food_mat.material.isValid()) { return food_mat.material.getToken(); } - else if ( food_mat.creature ) - { + else if (food_mat.creature) { return food_mat.creature->creature_id + ":" + food_mat.caste->caste_id; } return std::string(); } -size_t OrganicMatLookup::food_max_size ( organic_mat_category::organic_mat_category mat_category ) -{ +size_t OrganicMatLookup::food_max_size(organic_mat_category::organic_mat_category mat_category) { return world->raws.mat_table.organic_types[mat_category].size(); } -void OrganicMatLookup::food_build_map ( std::ostream &out ) -{ - if ( index_built ) +void OrganicMatLookup::food_build_map() { + if (index_built) return; - df::world_raws &raws = world->raws; + df::world_raws& raws = world->raws; df::special_mat_table table = raws.mat_table; using df::enums::organic_mat_category::organic_mat_category; using traits = df::enum_traits; - for ( int32_t mat_category = traits::first_item_value; mat_category <= traits::last_item_value; ++mat_category ) - { - for ( size_t i = 0; i < table.organic_indexes[mat_category].size(); ++i ) - { - int16_t type = table.organic_types[mat_category].at ( i ); - int32_t index = table.organic_indexes[mat_category].at ( i ); - food_index[mat_category].insert ( std::make_pair ( std::make_pair ( type,index ), i ) ); // wtf.. only in c++ + for (int32_t mat_category = traits::first_item_value; mat_category <= traits::last_item_value; ++mat_category) { + for (size_t i = 0; i < table.organic_indexes[mat_category].size(); ++i) { + int16_t type = table.organic_types[mat_category].at(i); + int32_t index = table.organic_indexes[mat_category].at(i); + food_index[mat_category].insert(std::make_pair(std::make_pair(type, index), i)); // wtf.. only in c++ } } index_built = true; } -int16_t OrganicMatLookup::food_idx_by_token ( std::ostream &out, organic_mat_category::organic_mat_category mat_category, const std::string & token ) -{ - int16_t food_idx = -1; - df::world_raws &raws = world->raws; +int16_t OrganicMatLookup::food_idx_by_token(organic_mat_category::organic_mat_category mat_category, const std::string& token) { + df::world_raws& raws = world->raws; df::special_mat_table table = raws.mat_table; - out << "food_idx_by_token: "; - if ( mat_category == organic_mat_category::Fish || - mat_category == organic_mat_category::UnpreparedFish || - mat_category == organic_mat_category::Eggs ) - { + DEBUG(log).print("food_idx_by_token: "); + if (mat_category == organic_mat_category::Fish || + mat_category == organic_mat_category::UnpreparedFish || + mat_category == organic_mat_category::Eggs) { std::vector tokens; - split_string ( &tokens, token, ":" ); - if ( tokens.size() != 2 ) - { - out << "creature " << "invalid CREATURE:CASTE token: " << token << endl; + split_string(&tokens, token, ":"); + if (tokens.size() != 2) { + WARN(log).print("creature invalid CREATURE:CASTE token: %s\n", token.c_str()); + return -1; } - else - { - int16_t creature_idx = find_creature ( tokens[0] ); - if ( creature_idx < 0 ) - { - out << " creature invalid token " << tokens[0]; - } - else - { - food_idx = linear_index ( table.organic_types[mat_category], creature_idx ); - if ( tokens[1] == "MALE" ) - food_idx += 1; - if ( table.organic_types[mat_category][food_idx] == creature_idx ) - out << "creature " << token << " caste " << tokens[1] << " creature_idx(" << creature_idx << ") food_idx("<< food_idx << ")" << endl; - else - { - out << "ERROR creature caste not found: " << token << " caste " << tokens[1] << " creature_idx(" << creature_idx << ") food_idx("<< food_idx << ")" << endl; - food_idx = -1; - } - } + int16_t creature_idx = find_creature(tokens[0]); + if (creature_idx < 0) { + WARN(log).print("creature invalid token %s\n", tokens[0].c_str()); + return -1; } - } - else - { - if ( !index_built ) - food_build_map ( out ); - MaterialInfo mat_info = food_mat_by_token ( out, token ); - int16_t type = mat_info.type; - int32_t index = mat_info.index; - auto it = food_index[mat_category].find ( std::make_pair ( type, index ) ); - if ( it != food_index[mat_category].end() ) - { - out << "matinfo: " << token << " type(" << mat_info.type << ") idx(" << mat_info.index << ") food_idx(" << it->second << ")" << endl; - food_idx = it->second; - } - else - { - out << "matinfo: " << token << " type(" << mat_info.type << ") idx(" << mat_info.index << ") food_idx not found :(" << endl; + int16_t food_idx = linear_index(table.organic_types[mat_category], creature_idx); + if (tokens[1] == "MALE") + food_idx += 1; + if (table.organic_types[mat_category][food_idx] == creature_idx) { + DEBUG(log).print("creature %s caste %s creature_idx(%d) food_idx(%d)\n", token.c_str(), tokens[1].c_str(), creature_idx, food_idx); + return food_idx; } + WARN(log).print("creature caste not found: %s caste %s creature_idx(%d) food_idx(%d)\n", token.c_str(), tokens[1].c_str(), creature_idx, food_idx); + return -1; } - return food_idx; + + if (!index_built) + food_build_map(); + MaterialInfo mat_info = food_mat_by_token(token); + int16_t type = mat_info.type; + int32_t index = mat_info.index; + auto it = food_index[mat_category].find(std::make_pair(type, index)); + if (it != food_index[mat_category].end()) { + DEBUG(log).print("matinfo: %s type(%d) idx(%d) food_idx(%zd)\n", token.c_str(), mat_info.type, mat_info.index, it->second); + return it->second; + + } + + WARN(log).print("matinfo: %s type(%d) idx(%d) food_idx not found :(\n", token.c_str(), mat_info.type, mat_info.index); + return -1; } -MaterialInfo OrganicMatLookup::food_mat_by_token ( std::ostream &out, const std::string & token ) -{ +MaterialInfo OrganicMatLookup::food_mat_by_token(const std::string& token) { MaterialInfo mat_info; - mat_info.find ( token ); + mat_info.find(token); return mat_info; } bool OrganicMatLookup::index_built = false; -std::vector OrganicMatLookup::food_index = std::vector ( df::enum_traits< df::organic_mat_category >::last_item_value + 1 ); +std::vector OrganicMatLookup::food_index = std::vector(df::enum_traits< df::organic_mat_category >::last_item_value + 1); diff --git a/plugins/stockpiles/OrganicMatLookup.h b/plugins/stockpiles/OrganicMatLookup.h index ef49f3f3c..7ae65b17e 100644 --- a/plugins/stockpiles/OrganicMatLookup.h +++ b/plugins/stockpiles/OrganicMatLookup.h @@ -5,42 +5,42 @@ #include "df/organic_mat_category.h" namespace df { -struct creature_raw; -struct caste_raw; + struct creature_raw; + struct caste_raw; } /** * Helper class for mapping the various organic mats between their material indices * and their index in the stockpile_settings structures. */ -class OrganicMatLookup -{ +class OrganicMatLookup { -// pair of material type and index + // pair of material type and index typedef std::pair FoodMatPair; -// map for using type,index pairs to find the food index + // map for using type,index pairs to find the food index typedef std::map FoodMatMap; public: - struct FoodMat - { + struct FoodMat { DFHack::MaterialInfo material; - df::creature_raw *creature; - df::caste_raw * caste; - FoodMat() : material ( -1 ), creature ( 0 ), caste ( 0 ) {} + df::creature_raw* creature; + df::caste_raw* caste; + FoodMat(): material(-1), creature(0), caste(0) { } }; - static void food_mat_by_idx ( std::ostream &out, df::enums::organic_mat_category::organic_mat_category mat_category, std::vector::size_type food_idx, FoodMat & food_mat ); - static std::string food_token_by_idx ( std::ostream &out, df::enums::organic_mat_category::organic_mat_category mat_category, std::vector::size_type idx ); - static size_t food_max_size ( df::enums::organic_mat_category::organic_mat_category mat_category ); - static void food_build_map ( std::ostream &out ); + static void food_mat_by_idx(df::enums::organic_mat_category::organic_mat_category mat_category, std::vector::size_type food_idx, FoodMat& food_mat); + static std::string food_token_by_idx(df::enums::organic_mat_category::organic_mat_category mat_category, std::vector::size_type idx); - static int16_t food_idx_by_token ( std::ostream &out, df::enums::organic_mat_category::organic_mat_category mat_category, const std::string & token ); + static size_t food_max_size(df::enums::organic_mat_category::organic_mat_category mat_category); + static void food_build_map(); - static DFHack::MaterialInfo food_mat_by_token ( std::ostream &out, const std::string & token ); + static int16_t food_idx_by_token(df::enums::organic_mat_category::organic_mat_category mat_category, const std::string& token); + + static DFHack::MaterialInfo food_mat_by_token(const std::string& token); static bool index_built; static std::vector food_index; + private: OrganicMatLookup(); }; diff --git a/plugins/stockpiles/StockpileSerializer.cpp b/plugins/stockpiles/StockpileSerializer.cpp index c413613f6..65feb2711 100644 --- a/plugins/stockpiles/StockpileSerializer.cpp +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -1,38 +1,34 @@ -#include "StockpileSerializer.h" - // stockpiles plugin #include "OrganicMatLookup.h" +#include "StockpileSerializer.h" #include "StockpileUtils.h" // dfhack +#include "Debug.h" #include "MiscUtils.h" +#include "modules/Items.h" // df #include "df/building_stockpilest.h" #include "df/inorganic_raw.h" -#include "df/creature_raw.h" -#include "df/caste_raw.h" -#include "df/material.h" -#include "df/inorganic_raw.h" -#include "df/plant_raw.h" -#include "df/stockpile_group_set.h" +#include "df/item_quality.h" #include -#include -#include -#include #include -#include -#include #include +#include #include #include +#include +#include +#include +#include "df/furniture_type.h" +#include "df/material.h" +#include "df/plant_raw.h" +#include "df/stockpile_group_set.h" // protobuf #include -#include - -using std::endl; using std::string; using std::vector; @@ -44,21 +40,12 @@ using df::global::world; using std::placeholders::_1; - - -int NullBuffer::overflow ( int c ) -{ - return c; +namespace DFHack { + DBG_EXTERN(stockpiles, log); } -NullStream::NullStream() : std::ostream ( &m_sb ) {} - -StockpileSerializer::StockpileSerializer ( df::building_stockpilest * stockpile ) - : mDebug ( false ) - , mOut ( 0 ) - , mNull() - , mPile ( stockpile ) -{ +StockpileSerializer::StockpileSerializer(df::building_stockpilest* stockpile) + : mPile(stockpile) { // build other_mats indices furniture_setup_other_mats(); @@ -67,106 +54,87 @@ StockpileSerializer::StockpileSerializer ( df::building_stockpilest * stockpile weapons_armor_setup_other_mats(); } -StockpileSerializer::~StockpileSerializer() {} +StockpileSerializer::~StockpileSerializer() { } -void StockpileSerializer::enable_debug ( std::ostream&out ) -{ - mDebug = true; - mOut = &out; -} - -bool StockpileSerializer::serialize_to_ostream ( std::ostream* output ) -{ - if ( output->fail( ) ) return false; +bool StockpileSerializer::serialize_to_ostream(std::ostream* output) { + if (output->fail()) + return false; mBuffer.Clear(); write(); { - io::OstreamOutputStream zero_copy_output ( output ); - if ( !mBuffer.SerializeToZeroCopyStream ( &zero_copy_output ) ) return false; + io::OstreamOutputStream zero_copy_output(output); + if (!mBuffer.SerializeToZeroCopyStream(&zero_copy_output)) + return false; } return output->good(); } -bool StockpileSerializer::serialize_to_file ( const std::string & file ) -{ - std::fstream output ( file, std::ios::out | std::ios::binary | std::ios::trunc ); - if ( output.fail() ) - { - debug() << "ERROR: failed to open file for writing: " << file << endl; +bool StockpileSerializer::serialize_to_file(const std::string& file) { + std::fstream output(file, std::ios::out | std::ios::binary | std::ios::trunc); + if (output.fail()) { + WARN(log).print("ERROR: failed to open file for writing: '%s'\n", file.c_str()); return false; } - return serialize_to_ostream ( &output ); + return serialize_to_ostream(&output); } -bool StockpileSerializer::parse_from_istream ( std::istream* input ) -{ - if ( input->fail( ) ) return false; +bool StockpileSerializer::parse_from_istream(std::istream* input) { + if (input->fail()) + return false; mBuffer.Clear(); - io::IstreamInputStream zero_copy_input ( input ); - const bool res = mBuffer.ParseFromZeroCopyStream ( &zero_copy_input ) && input->eof(); - if ( res ) read(); + io::IstreamInputStream zero_copy_input(input); + const bool res = mBuffer.ParseFromZeroCopyStream(&zero_copy_input) && input->eof(); + if (res) + read(); return res; } - -bool StockpileSerializer::unserialize_from_file ( const std::string & file ) -{ - std::fstream input ( file, std::ios::in | std::ios::binary ); - if ( input.fail() ) - { - debug() << "ERROR: failed to open file for reading: " << file << endl; +bool StockpileSerializer::unserialize_from_file(const std::string& file) { + std::fstream input(file, std::ios::in | std::ios::binary); + if (input.fail()) { + WARN(log).print("failed to open file for reading: '%s'\n", file.c_str()); return false; } - return parse_from_istream ( &input ); + return parse_from_istream(&input); } -std::ostream & StockpileSerializer::debug() -{ - if ( mDebug ) - return *mOut; - return mNull; -} - - -void StockpileSerializer::write() -{ - // debug() << "GROUP SET " << bitfield_to_string(mPile->settings.flags) << endl; +void StockpileSerializer::write() { + DEBUG(log).print("GROUP SET %s\n", bitfield_to_string(mPile->settings.flags).c_str()); write_general(); - if ( mPile->settings.flags.bits.animals ) + if (mPile->settings.flags.bits.animals) write_animals(); - if ( mPile->settings.flags.bits.food ) + if (mPile->settings.flags.bits.food) write_food(); - if ( mPile->settings.flags.bits.furniture ) + if (mPile->settings.flags.bits.furniture) write_furniture(); - if ( mPile->settings.flags.bits.refuse ) + if (mPile->settings.flags.bits.refuse) write_refuse(); - if ( mPile->settings.flags.bits.stone ) + if (mPile->settings.flags.bits.stone) write_stone(); - if ( mPile->settings.flags.bits.ammo ) + if (mPile->settings.flags.bits.ammo) write_ammo(); - if ( mPile->settings.flags.bits.coins ) + if (mPile->settings.flags.bits.coins) write_coins(); - if ( mPile->settings.flags.bits.bars_blocks ) + if (mPile->settings.flags.bits.bars_blocks) write_bars_blocks(); - if ( mPile->settings.flags.bits.gems ) + if (mPile->settings.flags.bits.gems) write_gems(); - if ( mPile->settings.flags.bits.finished_goods ) + if (mPile->settings.flags.bits.finished_goods) write_finished_goods(); - if ( mPile->settings.flags.bits.leather ) + if (mPile->settings.flags.bits.leather) write_leather(); - if ( mPile->settings.flags.bits.cloth ) + if (mPile->settings.flags.bits.cloth) write_cloth(); - if ( mPile->settings.flags.bits.wood ) + if (mPile->settings.flags.bits.wood) write_wood(); - if ( mPile->settings.flags.bits.weapons ) + if (mPile->settings.flags.bits.weapons) write_weapons(); - if ( mPile->settings.flags.bits.armor ) + if (mPile->settings.flags.bits.armor) write_armor(); } -void StockpileSerializer::read () -{ - debug() << endl << "==READ==" << endl; +void StockpileSerializer::read() { + DEBUG(log).print("==READ==\n"); read_general(); read_animals(); read_food(); @@ -185,361 +153,297 @@ void StockpileSerializer::read () read_armor(); } - -void StockpileSerializer::serialize_list_organic_mat ( FuncWriteExport add_value, const std::vector * list, organic_mat_category::organic_mat_category cat ) -{ - if ( !list ) - { - debug() << "serialize_list_organic_mat: list null" << endl; +void StockpileSerializer::serialize_list_organic_mat(FuncWriteExport add_value, const std::vector* list, organic_mat_category::organic_mat_category cat) { + if (!list) { + DEBUG(log).print("serialize_list_organic_mat: list null\n"); + return; } - for ( size_t i = 0; i < list->size(); ++i ) - { - if ( list->at ( i ) ) - { - std::string token = OrganicMatLookup::food_token_by_idx ( debug(), cat, i ); - if ( !token.empty() ) - { - add_value ( token ); - debug() << " organic_material " << i << " is " << token << endl; - } - else - { - debug() << "food mat invalid :(" << endl; + for (size_t i = 0; i < list->size(); ++i) { + if (list->at(i)) { + std::string token = OrganicMatLookup::food_token_by_idx(cat, i); + if (token.empty()) { + DEBUG(log).print("food mat invalid :(\n"); + continue; } + DEBUG(log).print("organic_material %zd is %s\n", i, token.c_str()); + add_value(token); } } } -void StockpileSerializer::unserialize_list_organic_mat ( FuncReadImport get_value, size_t list_size, std::vector *pile_list, organic_mat_category::organic_mat_category cat ) -{ +void StockpileSerializer::unserialize_list_organic_mat(FuncReadImport get_value, size_t list_size, std::vector* pile_list, organic_mat_category::organic_mat_category cat) { pile_list->clear(); - pile_list->resize ( OrganicMatLookup::food_max_size ( cat ), '\0' ); - for ( size_t i = 0; i < list_size; ++i ) - { - std::string token = get_value ( i ); - int16_t idx = OrganicMatLookup::food_idx_by_token ( debug(), cat, token ); - debug() << " organic_material " << idx << " is " << token << endl; - if ( size_t(idx) >= pile_list->size() ) - { - debug() << "error organic mat index too large! idx[" << idx << "] max_size[" << pile_list->size() << "]" << endl; + pile_list->resize(OrganicMatLookup::food_max_size(cat), '\0'); + for (size_t i = 0; i < list_size; ++i) { + std::string token = get_value(i); + int16_t idx = OrganicMatLookup::food_idx_by_token(cat, token); + DEBUG(log).print(" organic_material %d is %s\n", idx, token.c_str()); + if (size_t(idx) >= pile_list->size()) { + WARN(log).print("organic mat index too large! idx[%d] max_size[%zd]\n", idx, pile_list->size()); continue; } - pile_list->at ( idx ) = 1; + pile_list->at(idx) = 1; } } - -void StockpileSerializer::serialize_list_item_type ( FuncItemAllowed is_allowed, FuncWriteExport add_value, const std::vector &list ) -{ +void StockpileSerializer::serialize_list_item_type(FuncItemAllowed is_allowed, FuncWriteExport add_value, const std::vector& list) { using df::enums::item_type::item_type; using type_traits = df::enum_traits; - debug() << "item_type size = " << list.size() << " size limit = " << type_traits::last_item_value << " typecasted: " << ( size_t ) type_traits::last_item_value << endl; - for ( size_t i = 0; i <= ( size_t ) type_traits::last_item_value; ++i ) - { - if ( list.at ( i ) ) - { - const item_type type = ( item_type ) ( ( df::enum_traits::base_type ) i ); - std::string r_type ( type_traits::key_table[i+1] ); - if ( !is_allowed ( type ) ) continue; - add_value ( r_type ); - debug() << "item_type key_table[" << i+1 << "] type[" << ( int16_t ) type << "] is " << r_type <::base_type)i); + std::string r_type(type_traits::key_table[i + 1]); + if (!is_allowed(type)) + continue; + add_value(r_type); + DEBUG(log).print("item_type key_table[%zd] type[%d] is %s\n", i + 1, (int16_t)type, r_type.c_str()); } } - -void StockpileSerializer::unserialize_list_item_type ( FuncItemAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector *pile_list ) -{ +void StockpileSerializer::unserialize_list_item_type(FuncItemAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector* pile_list) { pile_list->clear(); - pile_list->resize ( 112, '\0' ); // TODO remove hardcoded list size value - for ( size_t i = 0; i < pile_list->size(); ++i ) - { - pile_list->at ( i ) = is_allowed ( ( item_type::item_type ) i ) ? 0 : 1; + pile_list->resize(112, '\0'); // TODO remove hardcoded list size value + for (size_t i = 0; i < pile_list->size(); ++i) { + pile_list->at(i) = is_allowed((item_type::item_type)i) ? 0 : 1; } using df::enums::item_type::item_type; df::enum_traits type_traits; - for ( int32_t i = 0; i < list_size; ++i ) - { - const std::string token = read_value ( i ); + for (int32_t i = 0; i < list_size; ++i) { + const std::string token = read_value(i); // subtract one because item_type starts at -1 - const df::enum_traits::base_type idx = linear_index ( debug(), type_traits, token ) - 1; - const item_type type = ( item_type ) idx; - if ( !is_allowed ( type ) ) continue; - debug() << " item_type " << idx << " is " << token << endl; - if ( size_t(idx) >= pile_list->size() ) - { - debug() << "error item_type index too large! idx[" << idx << "] max_size[" << pile_list->size() << "]" << endl; + const df::enum_traits::base_type idx = linear_index(type_traits, token) - 1; + const item_type type = (item_type)idx; + if (!is_allowed(type)) + continue; + DEBUG(log).print("item_type %d is %s\n", idx, token.c_str()); + if (size_t(idx) >= pile_list->size()) { + WARN(log).print("error item_type index too large! idx[%d] max_size[%zd]\n", idx, pile_list->size()); continue; } - pile_list->at ( idx ) = 1; + pile_list->at(idx) = 1; } } - -void StockpileSerializer::serialize_list_material ( FuncMaterialAllowed is_allowed, FuncWriteExport add_value, const std::vector &list ) -{ +void StockpileSerializer::serialize_list_material(FuncMaterialAllowed is_allowed, FuncWriteExport add_value, const std::vector& list) { MaterialInfo mi; - for ( size_t i = 0; i < list.size(); ++i ) - { - if ( list.at ( i ) ) - { - mi.decode ( 0, i ); - if ( !is_allowed ( mi ) ) continue; - debug() << " material " << i << " is " << mi.getToken() << endl; - add_value ( mi.getToken() ); + for (size_t i = 0; i < list.size(); ++i) { + if (list.at(i)) { + mi.decode(0, i); + if (!is_allowed(mi)) + continue; + DEBUG(log).print("material %zd is %s\n", i, mi.getToken().c_str()); + add_value(mi.getToken()); } } } - -void StockpileSerializer::unserialize_list_material ( FuncMaterialAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector *pile_list ) -{ +void StockpileSerializer::unserialize_list_material(FuncMaterialAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector* pile_list) { // we initialize all possible (allowed) values to 0, // then all other not-allowed values to 1 // why? because that's how the memory is in DF before // we muck with it. std::set idx_set; pile_list->clear(); - pile_list->resize ( world->raws.inorganics.size(), 0 ); - for ( size_t i = 0; i < pile_list->size(); ++i ) - { - MaterialInfo mi ( 0, i ); - pile_list->at ( i ) = is_allowed ( mi ) ? 0 : 1; + pile_list->resize(world->raws.inorganics.size(), 0); + for (size_t i = 0; i < pile_list->size(); ++i) { + MaterialInfo mi(0, i); + pile_list->at(i) = is_allowed(mi) ? 0 : 1; } - for ( int i = 0; i < list_size; ++i ) - { - const std::string token = read_value ( i ); + for (int i = 0; i < list_size; ++i) { + const std::string token = read_value(i); MaterialInfo mi; - mi.find ( token ); - if ( !is_allowed ( mi ) ) continue; - debug() << " material " << mi.index << " is " << token << endl; - if ( size_t(mi.index) >= pile_list->size() ) - { - debug() << "error material index too large! idx[" << mi.index << "] max_size[" << pile_list->size() << "]" << endl; + mi.find(token); + if (!is_allowed(mi)) + continue; + DEBUG(log).print("material %d is %s\n", mi.index, token.c_str()); + if (size_t(mi.index) >= pile_list->size()) { + WARN(log).print("material index too large! idx[%d] max_size[%zd]\n", mi.index, pile_list->size()); continue; } - pile_list->at ( mi.index ) = 1; + pile_list->at(mi.index) = 1; } } - -void StockpileSerializer::serialize_list_quality ( FuncWriteExport add_value, const bool ( &quality_list ) [7] ) -{ +void StockpileSerializer::serialize_list_quality(FuncWriteExport add_value, const bool(&quality_list)[7]) { using df::enums::item_quality::item_quality; using quality_traits = df::enum_traits; - for ( size_t i = 0; i < 7; ++i ) - { - if ( quality_list[i] ) - { - const std::string f_type ( quality_traits::key_table[i] ); - add_value ( f_type ); - debug() << " quality: " << i << " is " << f_type < 0 && list_size <= 7 ) - { +void StockpileSerializer::unserialize_list_quality(FuncReadImport read_value, int32_t list_size, bool(&pile_list)[7]) { + quality_clear(pile_list); + if (list_size > 0 && list_size <= 7) { using df::enums::item_quality::item_quality; df::enum_traits quality_traits; - for ( int i = 0; i < list_size; ++i ) - { - const std::string quality = read_value ( i ); - df::enum_traits::base_type idx = linear_index ( debug(), quality_traits, quality ); - if ( idx < 0 ) - { - debug() << " invalid quality token " << quality << endl; + for (int i = 0; i < list_size; ++i) { + const std::string quality = read_value(i); + df::enum_traits::base_type idx = linear_index(quality_traits, quality); + if (idx < 0) { + WARN(log).print("invalid quality token: %s\n", quality.c_str()); continue; } - debug() << " quality: " << idx << " is " << quality << endl; + DEBUG(log).print("quality: %d is %s\n", idx, quality.c_str()); pile_list[idx] = true; } } } - -void StockpileSerializer::serialize_list_other_mats ( const std::map other_mats, FuncWriteExport add_value, std::vector list ) -{ - for ( size_t i = 0; i < list.size(); ++i ) - { - if ( list.at ( i ) ) - { - const std::string token = other_mats_index ( other_mats, i ); - if ( token.empty() ) - { - debug() << " invalid other material with index " << i << endl; +void StockpileSerializer::serialize_list_other_mats(const std::map other_mats, FuncWriteExport add_value, std::vector list) { + for (size_t i = 0; i < list.size(); ++i) { + if (list.at(i)) { + const std::string token = other_mats_index(other_mats, i); + if (token.empty()) { + WARN(log).print("invalid other material with index %zd\n", i); continue; } - add_value ( token ); - debug() << " other mats " << i << " is " << token << endl; + add_value(token); + DEBUG(log).print("other mats %zd is %s\n", i, token.c_str()); } } } - -void StockpileSerializer::unserialize_list_other_mats ( const std::map other_mats, FuncReadImport read_value, int32_t list_size, std::vector *pile_list ) -{ +void StockpileSerializer::unserialize_list_other_mats(const std::map other_mats, FuncReadImport read_value, int32_t list_size, std::vector* pile_list) { pile_list->clear(); - pile_list->resize ( other_mats.size(), '\0' ); - for ( int i = 0; i < list_size; ++i ) - { - const std::string token = read_value ( i ); - size_t idx = other_mats_token ( other_mats, token ); - if ( idx < 0 ) - { - debug() << "invalid other mat with token " << token; + pile_list->resize(other_mats.size(), '\0'); + for (int i = 0; i < list_size; ++i) { + const std::string token = read_value(i); + size_t idx = other_mats_token(other_mats, token); + if (idx < 0) { + WARN(log).print("invalid other mat with token %s\n", token.c_str()); continue; } - debug() << " other_mats " << idx << " is " << token << endl; - if ( idx >= pile_list->size() ) - { - debug() << "error other_mats index too large! idx[" << idx << "] max_size[" << pile_list->size() << "]" << endl; + DEBUG(log).print("other_mats %zd is %s\n", idx, token.c_str()); + if (idx >= pile_list->size()) { + WARN(log).print("other_mats index too large! idx[%zd] max_size[%zd]\n", idx, pile_list->size()); continue; } - pile_list->at ( idx ) = 1; + pile_list->at(idx) = 1; } } - - -void StockpileSerializer::serialize_list_itemdef ( FuncWriteExport add_value, std::vector list, std::vector items, item_type::item_type type ) -{ - for ( size_t i = 0; i < list.size(); ++i ) - { - if ( list.at ( i ) ) - { - const df::itemdef *a = items.at ( i ); +void StockpileSerializer::serialize_list_itemdef(FuncWriteExport add_value, std::vector list, std::vector items, item_type::item_type type) { + for (size_t i = 0; i < list.size(); ++i) { + if (list.at(i)) { + const df::itemdef* a = items.at(i); // skip procedurally generated items - if ( a->base_flags.is_set ( df::itemdef_flags::GENERATED ) ) continue; + if (a->base_flags.is_set(df::itemdef_flags::GENERATED)) + continue; ItemTypeInfo ii; - if ( !ii.decode ( type, i ) ) continue; - add_value ( ii.getToken() ); - debug() << " itemdef type" << i << " is " << ii.getToken() << endl; + if (!ii.decode(type, i)) + continue; + add_value(ii.getToken()); + DEBUG(log).print("itemdef type %zd is %s\n", i, ii.getToken().c_str()); } } } - -void StockpileSerializer::unserialize_list_itemdef ( FuncReadImport read_value, int32_t list_size, std::vector *pile_list, item_type::item_type type ) -{ +void StockpileSerializer::unserialize_list_itemdef(FuncReadImport read_value, int32_t list_size, std::vector* pile_list, item_type::item_type type) { pile_list->clear(); - pile_list->resize ( Items::getSubtypeCount ( type ), '\0' ); - for ( int i = 0; i < list_size; ++i ) - { - std::string token = read_value ( i ); + pile_list->resize(Items::getSubtypeCount(type), '\0'); + for (int i = 0; i < list_size; ++i) { + std::string token = read_value(i); ItemTypeInfo ii; - if ( !ii.find ( token ) ) continue; - debug() << " itemdef " << ii.subtype << " is " << token << endl; - if ( size_t(ii.subtype) >= pile_list->size() ) - { - debug() << "error itemdef index too large! idx[" << ii.subtype << "] max_size[" << pile_list->size() << "]" << endl; + if (!ii.find(token)) + continue; + DEBUG(log).print("itemdef %d is %s\n", ii.subtype, token.c_str()); + if (size_t(ii.subtype) >= pile_list->size()) { + WARN(log).print("itemdef index too large! idx[%d] max_size[%zd]\n", ii.subtype, pile_list->size()); continue; } - pile_list->at ( ii.subtype ) = 1; + pile_list->at(ii.subtype) = 1; } } - -std::string StockpileSerializer::other_mats_index ( const std::map other_mats, int idx ) -{ - auto it = other_mats.find ( idx ); - if ( it == other_mats.end() ) +std::string StockpileSerializer::other_mats_index(const std::map other_mats, int idx) { + auto it = other_mats.find(idx); + if (it == other_mats.end()) return std::string(); return it->second; } -int StockpileSerializer::other_mats_token ( const std::map other_mats, const std::string & token ) -{ - for ( auto it = other_mats.begin(); it != other_mats.end(); ++it ) - { - if ( it->second == token ) +int StockpileSerializer::other_mats_token(const std::map other_mats, const std::string& token) { + for (auto it = other_mats.begin(); it != other_mats.end(); ++it) { + if (it->second == token) return it->first; } return -1; } -void StockpileSerializer::write_general() -{ - mBuffer.set_max_bins ( mPile->max_bins ); - mBuffer.set_max_wheelbarrows ( mPile->max_wheelbarrows ); - mBuffer.set_max_barrels ( mPile->max_barrels ); - mBuffer.set_use_links_only ( mPile->use_links_only ); - mBuffer.set_allow_inorganic ( mPile->settings.allow_inorganic ); - mBuffer.set_allow_organic ( mPile->settings.allow_organic ); - mBuffer.set_corpses ( mPile->settings.flags.bits.corpses ); +void StockpileSerializer::write_general() { + mBuffer.set_max_bins(mPile->max_bins); + mBuffer.set_max_wheelbarrows(mPile->max_wheelbarrows); + mBuffer.set_max_barrels(mPile->max_barrels); + mBuffer.set_use_links_only(mPile->use_links_only); + mBuffer.set_allow_inorganic(mPile->settings.allow_inorganic); + mBuffer.set_allow_organic(mPile->settings.allow_organic); + mBuffer.set_corpses(mPile->settings.flags.bits.corpses); } -void StockpileSerializer::read_general() -{ - if ( mBuffer.has_max_bins() ) +void StockpileSerializer::read_general() { + if (mBuffer.has_max_bins()) mPile->max_bins = mBuffer.max_bins(); - if ( mBuffer.has_max_wheelbarrows() ) + if (mBuffer.has_max_wheelbarrows()) mPile->max_wheelbarrows = mBuffer.max_wheelbarrows(); - if ( mBuffer.has_max_barrels() ) + if (mBuffer.has_max_barrels()) mPile->max_barrels = mBuffer.max_barrels(); - if ( mBuffer.has_use_links_only() ) + if (mBuffer.has_use_links_only()) mPile->use_links_only = mBuffer.use_links_only(); - if ( mBuffer.has_allow_inorganic() ) + if (mBuffer.has_allow_inorganic()) mPile->settings.allow_inorganic = mBuffer.allow_inorganic(); - if ( mBuffer.has_allow_organic() ) + if (mBuffer.has_allow_organic()) mPile->settings.allow_organic = mBuffer.allow_organic(); - if ( mBuffer.has_corpses() ) + if (mBuffer.has_corpses()) mPile->settings.flags.bits.corpses = mBuffer.corpses(); } -void StockpileSerializer::write_animals() -{ - StockpileSettings::AnimalsSet *animals= mBuffer.mutable_animals(); - animals->set_empty_cages ( mPile->settings.animals.empty_cages ); - animals->set_empty_traps ( mPile->settings.animals.empty_traps ); - for ( size_t i = 0; i < mPile->settings.animals.enabled.size(); ++i ) - { - if ( mPile->settings.animals.enabled.at ( i ) == 1 ) - { - df::creature_raw* r = find_creature ( i ); - debug() << "creature "<< r->creature_id << " " << i << endl; - animals->add_enabled ( r->creature_id ); +void StockpileSerializer::write_animals() { + StockpileSettings::AnimalsSet* animals = mBuffer.mutable_animals(); + animals->set_empty_cages(mPile->settings.animals.empty_cages); + animals->set_empty_traps(mPile->settings.animals.empty_traps); + for (size_t i = 0; i < mPile->settings.animals.enabled.size(); ++i) { + if (mPile->settings.animals.enabled.at(i) == 1) { + df::creature_raw* r = find_creature(i); + DEBUG(log).print("creature %s %zd\n", r->creature_id.c_str(), i); + animals->add_enabled(r->creature_id); } } } -void StockpileSerializer::read_animals() -{ - if ( mBuffer.has_animals() ) - { +void StockpileSerializer::read_animals() { + if (mBuffer.has_animals()) { mPile->settings.flags.bits.animals = 1; - debug() << "animals:" << endl; + DEBUG(log).print("animals:\n"); mPile->settings.animals.empty_cages = mBuffer.animals().empty_cages(); mPile->settings.animals.empty_traps = mBuffer.animals().empty_traps(); mPile->settings.animals.enabled.clear(); - mPile->settings.animals.enabled.resize ( world->raws.creatures.all.size(), '\0' ); - debug() << " pile has " << mPile->settings.animals.enabled.size() << endl; - for ( auto i = 0; i < mBuffer.animals().enabled_size(); ++i ) - { - std::string id = mBuffer.animals().enabled ( i ); - int idx = find_creature ( id ); - debug() << id << " " << idx << endl; - if ( idx < 0 || size_t(idx) >= mPile->settings.animals.enabled.size() ) - { - debug() << "WARNING: animal index invalid: " << idx << endl; + mPile->settings.animals.enabled.resize(world->raws.creatures.all.size(), '\0'); + DEBUG(log).print("pile has %zd\n", mPile->settings.animals.enabled.size()); + for (auto i = 0; i < mBuffer.animals().enabled_size(); ++i) { + std::string id = mBuffer.animals().enabled(i); + int idx = find_creature(id); + DEBUG(log).print("%s %d\n", id.c_str(), idx); + if (idx < 0 || size_t(idx) >= mPile->settings.animals.enabled.size()) { + WARN(log).print("animal index invalid: %d\n", idx); continue; } - mPile->settings.animals.enabled.at ( idx ) = ( char ) 1; + mPile->settings.animals.enabled.at(idx) = (char)1; } } - else - { + else { mPile->settings.animals.enabled.clear(); mPile->settings.flags.bits.animals = 0; mPile->settings.animals.empty_cages = false; @@ -547,183 +451,162 @@ void StockpileSerializer::read_animals() } } -StockpileSerializer::food_pair StockpileSerializer::food_map ( organic_mat_category::organic_mat_category cat ) -{ +StockpileSerializer::food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_category cat) { using df::enums::organic_mat_category::organic_mat_category; using namespace std::placeholders; - switch ( cat ) - { + switch (cat) { case organic_mat_category::Meat: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_meat ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_meat(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().meat ( idx ); }; - return food_pair ( setter, &mPile->settings.food.meat, getter, mBuffer.food().meat_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().meat(idx); }; + return food_pair(setter, &mPile->settings.food.meat, getter, mBuffer.food().meat_size()); } case organic_mat_category::Fish: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_fish ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_fish(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().fish ( idx ); }; - return food_pair ( setter, &mPile->settings.food.fish, getter, mBuffer.food().fish_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().fish(idx); }; + return food_pair(setter, &mPile->settings.food.fish, getter, mBuffer.food().fish_size()); } case organic_mat_category::UnpreparedFish: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_unprepared_fish ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_unprepared_fish(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().unprepared_fish ( idx ); }; - return food_pair ( setter, &mPile->settings.food.unprepared_fish, getter, mBuffer.food().unprepared_fish_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().unprepared_fish(idx); }; + return food_pair(setter, &mPile->settings.food.unprepared_fish, getter, mBuffer.food().unprepared_fish_size()); } case organic_mat_category::Eggs: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_egg ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_egg(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().egg ( idx ); }; - return food_pair ( setter, &mPile->settings.food.egg, getter, mBuffer.food().egg_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().egg(idx); }; + return food_pair(setter, &mPile->settings.food.egg, getter, mBuffer.food().egg_size()); } case organic_mat_category::Plants: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_plants ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_plants(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().plants ( idx ); }; - return food_pair ( setter, &mPile->settings.food.plants, getter, mBuffer.food().plants_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().plants(idx); }; + return food_pair(setter, &mPile->settings.food.plants, getter, mBuffer.food().plants_size()); } case organic_mat_category::PlantDrink: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_drink_plant ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_drink_plant(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().drink_plant ( idx ); }; - return food_pair ( setter, &mPile->settings.food.drink_plant, getter, mBuffer.food().drink_plant_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().drink_plant(idx); }; + return food_pair(setter, &mPile->settings.food.drink_plant, getter, mBuffer.food().drink_plant_size()); } case organic_mat_category::CreatureDrink: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_drink_animal ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_drink_animal(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().drink_animal ( idx ); }; - return food_pair ( setter, &mPile->settings.food.drink_animal, getter, mBuffer.food().drink_animal_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().drink_animal(idx); }; + return food_pair(setter, &mPile->settings.food.drink_animal, getter, mBuffer.food().drink_animal_size()); } case organic_mat_category::PlantCheese: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_cheese_plant ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_cheese_plant(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().cheese_plant ( idx ); }; - return food_pair ( setter, &mPile->settings.food.cheese_plant, getter, mBuffer.food().cheese_plant_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().cheese_plant(idx); }; + return food_pair(setter, &mPile->settings.food.cheese_plant, getter, mBuffer.food().cheese_plant_size()); } case organic_mat_category::CreatureCheese: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_cheese_animal ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_cheese_animal(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().cheese_animal ( idx ); }; - return food_pair ( setter, &mPile->settings.food.cheese_animal, getter, mBuffer.food().cheese_animal_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().cheese_animal(idx); }; + return food_pair(setter, &mPile->settings.food.cheese_animal, getter, mBuffer.food().cheese_animal_size()); } case organic_mat_category::Seed: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_seeds ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_seeds(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().seeds ( idx ); }; - return food_pair ( setter, &mPile->settings.food.seeds, getter, mBuffer.food().seeds_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().seeds(idx); }; + return food_pair(setter, &mPile->settings.food.seeds, getter, mBuffer.food().seeds_size()); } case organic_mat_category::Leaf: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_leaves ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_leaves(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().leaves ( idx ); }; - return food_pair ( setter, &mPile->settings.food.leaves, getter, mBuffer.food().leaves_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().leaves(idx); }; + return food_pair(setter, &mPile->settings.food.leaves, getter, mBuffer.food().leaves_size()); } case organic_mat_category::PlantPowder: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_powder_plant ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_powder_plant(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().powder_plant ( idx ); }; - return food_pair ( setter, &mPile->settings.food.powder_plant, getter, mBuffer.food().powder_plant_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().powder_plant(idx); }; + return food_pair(setter, &mPile->settings.food.powder_plant, getter, mBuffer.food().powder_plant_size()); } case organic_mat_category::CreaturePowder: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_powder_creature ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_powder_creature(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().powder_creature ( idx ); }; - return food_pair ( setter, &mPile->settings.food.powder_creature, getter, mBuffer.food().powder_creature_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().powder_creature(idx); }; + return food_pair(setter, &mPile->settings.food.powder_creature, getter, mBuffer.food().powder_creature_size()); } case organic_mat_category::Glob: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_glob ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_glob(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().glob ( idx ); }; - return food_pair ( setter, &mPile->settings.food.glob, getter, mBuffer.food().glob_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().glob(idx); }; + return food_pair(setter, &mPile->settings.food.glob, getter, mBuffer.food().glob_size()); } case organic_mat_category::PlantLiquid: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_liquid_plant ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_liquid_plant(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().liquid_plant ( idx ); }; - return food_pair ( setter, &mPile->settings.food.liquid_plant, getter, mBuffer.food().liquid_plant_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().liquid_plant(idx); }; + return food_pair(setter, &mPile->settings.food.liquid_plant, getter, mBuffer.food().liquid_plant_size()); } case organic_mat_category::CreatureLiquid: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_liquid_animal ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_liquid_animal(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().liquid_animal ( idx ); }; - return food_pair ( setter, &mPile->settings.food.liquid_animal, getter, mBuffer.food().liquid_animal_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().liquid_animal(idx); }; + return food_pair(setter, &mPile->settings.food.liquid_animal, getter, mBuffer.food().liquid_animal_size()); } case organic_mat_category::MiscLiquid: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_liquid_misc ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_liquid_misc(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().liquid_misc ( idx ); }; - return food_pair ( setter, &mPile->settings.food.liquid_misc, getter, mBuffer.food().liquid_misc_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().liquid_misc(idx); }; + return food_pair(setter, &mPile->settings.food.liquid_misc, getter, mBuffer.food().liquid_misc_size()); } case organic_mat_category::Paste: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_glob_paste ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_glob_paste(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().glob_paste ( idx ); }; - return food_pair ( setter, &mPile->settings.food.glob_paste, getter, mBuffer.food().glob_paste_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().glob_paste(idx); }; + return food_pair(setter, &mPile->settings.food.glob_paste, getter, mBuffer.food().glob_paste_size()); } case organic_mat_category::Pressed: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_glob_pressed ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_glob_pressed(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().glob_pressed ( idx ); }; - return food_pair ( setter, &mPile->settings.food.glob_pressed, getter, mBuffer.food().glob_pressed_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().glob_pressed(idx); }; + return food_pair(setter, &mPile->settings.food.glob_pressed, getter, mBuffer.food().glob_pressed_size()); } case organic_mat_category::Leather: case organic_mat_category::Silk: @@ -750,55 +633,49 @@ StockpileSerializer::food_pair StockpileSerializer::food_map ( organic_mat_categ return food_pair(); } - -void StockpileSerializer::write_food() -{ - StockpileSettings::FoodSet *food = mBuffer.mutable_food(); - debug() << " food: " << endl; - food->set_prepared_meals ( mPile->settings.food.prepared_meals ); +void StockpileSerializer::write_food() { + StockpileSettings::FoodSet* food = mBuffer.mutable_food(); + DEBUG(log).print("food:\n"); + food->set_prepared_meals(mPile->settings.food.prepared_meals); using df::enums::organic_mat_category::organic_mat_category; using traits = df::enum_traits; - for ( int32_t mat_category = traits::first_item_value; mat_category ; - if ( mBuffer.has_food() ) - { + if (mBuffer.has_food()) { mPile->settings.flags.bits.food = 1; const StockpileSettings::FoodSet food = mBuffer.food(); - debug() << "food:" <settings.food.prepared_meals = food.prepared_meals(); else mPile->settings.food.prepared_meals = true; - debug() << " prepared_meals: " << mPile->settings.food.prepared_meals << endl; + DEBUG(log).print("prepared_meals: %d\n", mPile->settings.food.prepared_meals); - for ( int32_t mat_category = traits::first_item_value; mat_category clear(); } mPile->settings.flags.bits.food = 0; @@ -806,327 +683,249 @@ void StockpileSerializer::read_food() } } -void StockpileSerializer::furniture_setup_other_mats() -{ - mOtherMatsFurniture.insert ( std::make_pair ( 0,"WOOD" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 1,"PLANT_CLOTH" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 2,"BONE" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 3,"TOOTH" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 4,"HORN" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 5,"PEARL" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 6,"SHELL" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 7,"LEATHER" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 8,"SILK" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 9,"AMBER" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 10,"CORAL" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 11,"GREEN_GLASS" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 12,"CLEAR_GLASS" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 13,"CRYSTAL_GLASS" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 14,"YARN" ) ); +void StockpileSerializer::furniture_setup_other_mats() { + mOtherMatsFurniture.insert(std::make_pair(0, "WOOD")); + mOtherMatsFurniture.insert(std::make_pair(1, "PLANT_CLOTH")); + mOtherMatsFurniture.insert(std::make_pair(2, "BONE")); + mOtherMatsFurniture.insert(std::make_pair(3, "TOOTH")); + mOtherMatsFurniture.insert(std::make_pair(4, "HORN")); + mOtherMatsFurniture.insert(std::make_pair(5, "PEARL")); + mOtherMatsFurniture.insert(std::make_pair(6, "SHELL")); + mOtherMatsFurniture.insert(std::make_pair(7, "LEATHER")); + mOtherMatsFurniture.insert(std::make_pair(8, "SILK")); + mOtherMatsFurniture.insert(std::make_pair(9, "AMBER")); + mOtherMatsFurniture.insert(std::make_pair(10, "CORAL")); + mOtherMatsFurniture.insert(std::make_pair(11, "GREEN_GLASS")); + mOtherMatsFurniture.insert(std::make_pair(12, "CLEAR_GLASS")); + mOtherMatsFurniture.insert(std::make_pair(13, "CRYSTAL_GLASS")); + mOtherMatsFurniture.insert(std::make_pair(14, "YARN")); } -void StockpileSerializer::write_furniture() -{ - StockpileSettings::FurnitureSet *furniture= mBuffer.mutable_furniture(); +void StockpileSerializer::write_furniture() { + StockpileSettings::FurnitureSet* furniture = mBuffer.mutable_furniture(); // FURNITURE type using df::enums::furniture_type::furniture_type; using type_traits = df::enum_traits; - for ( size_t i = 0; i < mPile->settings.furniture.type.size(); ++i ) - { - if ( mPile->settings.furniture.type.at ( i ) ) - { - std::string f_type ( type_traits::key_table[i] ); - furniture->add_type ( f_type ); - debug() << "furniture_type " << i << " is " << f_type <settings.furniture.type.size(); ++i) { + if (mPile->settings.furniture.type.at(i)) { + std::string f_type(type_traits::key_table[i]); + furniture->add_type(f_type); + DEBUG(log).print("furniture_type %zd is %s\n", i, f_type.c_str()); } } // metal, stone/clay materials - FuncMaterialAllowed filter = std::bind ( &StockpileSerializer::furniture_mat_is_allowed, this, _1 ); - serialize_list_material ( filter, [=] ( const std::string &token ) - { - furniture->add_mats ( token ); - }, mPile->settings.furniture.mats ); + FuncMaterialAllowed filter = std::bind(&StockpileSerializer::furniture_mat_is_allowed, this, _1); + serialize_list_material( + filter, [=](const std::string& token) { furniture->add_mats(token); }, + mPile->settings.furniture.mats); // other mats - serialize_list_other_mats ( mOtherMatsFurniture, [=] ( const std::string &token ) - { - furniture->add_other_mats ( token ); - }, mPile->settings.furniture.other_mats ); + serialize_list_other_mats( + mOtherMatsFurniture, [=](const std::string& token) { furniture->add_other_mats(token); }, + mPile->settings.furniture.other_mats); - serialize_list_quality ( [=] ( const std::string &token ) - { - furniture->add_quality_core ( token ); - }, mPile->settings.furniture.quality_core ); - serialize_list_quality ( [=] ( const std::string &token ) - { - furniture->add_quality_total ( token ); - }, mPile->settings.furniture.quality_total ); + serialize_list_quality([=](const std::string& token) { furniture->add_quality_core(token); }, + mPile->settings.furniture.quality_core); + serialize_list_quality([=](const std::string& token) { furniture->add_quality_total(token); }, + mPile->settings.furniture.quality_total); } -bool StockpileSerializer::furniture_mat_is_allowed ( const MaterialInfo &mi ) -{ - return mi.isValid() && mi.material - && ( mi.material->flags.is_set ( material_flags::IS_METAL ) - || mi.material->flags.is_set ( material_flags::IS_STONE ) ); +bool StockpileSerializer::furniture_mat_is_allowed(const MaterialInfo& mi) { + return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_METAL) || mi.material->flags.is_set(material_flags::IS_STONE)); } -void StockpileSerializer::read_furniture() -{ - if ( mBuffer.has_furniture() ) - { +void StockpileSerializer::read_furniture() { + if (mBuffer.has_furniture()) { mPile->settings.flags.bits.furniture = 1; const StockpileSettings::FurnitureSet furniture = mBuffer.furniture(); - debug() << "furniture:" < type_traits; mPile->settings.furniture.type.clear(); - mPile->settings.furniture.type.resize ( type_traits.last_item_value+1, '\0' ); - if ( furniture.type_size() > 0 ) - { - for ( int i = 0; i < furniture.type_size(); ++i ) - { - const std::string type = furniture.type ( i ); - df::enum_traits::base_type idx = linear_index ( debug(), type_traits, type ); - debug() << " type " << idx << " is " << type << endl; - if ( idx < 0 || size_t(idx) >= mPile->settings.furniture.type.size() ) - { - debug() << "WARNING: furniture type index invalid " << type << ", idx=" << idx << endl; + mPile->settings.furniture.type.resize(type_traits.last_item_value + 1, '\0'); + if (furniture.type_size() > 0) { + for (int i = 0; i < furniture.type_size(); ++i) { + const std::string type = furniture.type(i); + df::enum_traits::base_type idx = linear_index(type_traits, type); + DEBUG(log).print("type %d is %s\n", idx, type.c_str()); + if (idx < 0 || size_t(idx) >= mPile->settings.furniture.type.size()) { + WARN(log).print("furniture type index invalid %s, idx=%d\n", type.c_str(), idx); continue; } - mPile->settings.furniture.type.at ( idx ) = 1; + mPile->settings.furniture.type.at(idx) = 1; } } - FuncMaterialAllowed filter = std::bind ( &StockpileSerializer::furniture_mat_is_allowed, this, _1 ); - unserialize_list_material ( filter, [=] ( const size_t & idx ) -> const std::string& - { - return furniture.mats ( idx ); - }, furniture.mats_size(), &mPile->settings.furniture.mats ); + FuncMaterialAllowed filter = std::bind(&StockpileSerializer::furniture_mat_is_allowed, this, _1); + unserialize_list_material( + filter, [=](const size_t& idx) -> const std::string& { return furniture.mats(idx); }, + furniture.mats_size(), &mPile->settings.furniture.mats); // other materials - unserialize_list_other_mats ( mOtherMatsFurniture, [=] ( const size_t & idx ) -> const std::string& - { - return furniture.other_mats ( idx ); - }, furniture.other_mats_size(), &mPile->settings.furniture.other_mats ); + unserialize_list_other_mats( + mOtherMatsFurniture, [=](const size_t& idx) -> const std::string& { return furniture.other_mats(idx); }, + furniture.other_mats_size(), &mPile->settings.furniture.other_mats); // core quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return furniture.quality_core ( idx ); - }, furniture.quality_core_size(), mPile->settings.furniture.quality_core ); + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return furniture.quality_core(idx); }, + furniture.quality_core_size(), mPile->settings.furniture.quality_core); // total quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return furniture.quality_total ( idx ); - }, furniture.quality_total_size(), mPile->settings.furniture.quality_total ); - + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return furniture.quality_total(idx); }, + furniture.quality_total_size(), mPile->settings.furniture.quality_total); } - else - { + else { mPile->settings.flags.bits.furniture = 0; mPile->settings.furniture.type.clear(); mPile->settings.furniture.other_mats.clear(); mPile->settings.furniture.mats.clear(); - quality_clear ( mPile->settings.furniture.quality_core ); - quality_clear ( mPile->settings.furniture.quality_total ); + quality_clear(mPile->settings.furniture.quality_core); + quality_clear(mPile->settings.furniture.quality_total); } } -bool StockpileSerializer::refuse_creature_is_allowed ( const df::creature_raw *raw ) -{ - if ( !raw ) return false; +bool StockpileSerializer::refuse_creature_is_allowed(const df::creature_raw* raw) { + if (!raw) + return false; // wagon and generated creatures not allowed, except angels const bool is_wagon = raw->creature_id == "EQUIPMENT_WAGON"; - const bool is_generated = raw->flags.is_set ( creature_raw_flags::GENERATED ); - const bool is_angel = is_generated && raw->creature_id.find ( "DIVINE_" ) != std::string::npos; - return !is_wagon && ! ( is_generated && !is_angel ); + const bool is_generated = raw->flags.is_set(creature_raw_flags::GENERATED); + const bool is_angel = is_generated && raw->creature_id.find("DIVINE_") != std::string::npos; + return !is_wagon && !(is_generated && !is_angel); } -void StockpileSerializer::refuse_write_helper ( std::function add_value, const vector & list ) -{ - for ( size_t i = 0; i < list.size(); ++i ) - { - if ( list.at ( i ) == 1 ) - { - df::creature_raw* r = find_creature ( i ); +void StockpileSerializer::refuse_write_helper(std::function add_value, const vector& list) { + for (size_t i = 0; i < list.size(); ++i) { + if (list.at(i) == 1) { + df::creature_raw* r = find_creature(i); // skip forgotten beasts, titans, demons, and night creatures - if ( !refuse_creature_is_allowed ( r ) ) continue; - debug() << "creature "<< r->creature_id << " " << i << endl; - add_value ( r->creature_id ); + if (!refuse_creature_is_allowed(r)) + continue; + DEBUG(log).print("creature %s %zd\n", r->creature_id.c_str(), i); + add_value(r->creature_id); } } } -bool StockpileSerializer::refuse_type_is_allowed ( item_type::item_type type ) -{ - if ( type == item_type::NONE - || type == item_type::BAR - || type == item_type::SMALLGEM - || type == item_type::BLOCKS - || type == item_type::ROUGH - || type == item_type::BOULDER - || type == item_type::CORPSE - || type == item_type::CORPSEPIECE - || type == item_type::ROCK - || type == item_type::ORTHOPEDIC_CAST - ) return false; +bool StockpileSerializer::refuse_type_is_allowed(item_type::item_type type) { + if (type == item_type::NONE || type == item_type::BAR || type == item_type::SMALLGEM || type == item_type::BLOCKS || type == item_type::ROUGH || type == item_type::BOULDER || type == item_type::CORPSE || type == item_type::CORPSEPIECE || type == item_type::ROCK || type == item_type::ORTHOPEDIC_CAST) + return false; return true; } - -void StockpileSerializer::write_refuse() -{ - StockpileSettings::RefuseSet *refuse = mBuffer.mutable_refuse(); - refuse->set_fresh_raw_hide ( mPile->settings.refuse.fresh_raw_hide ); - refuse->set_rotten_raw_hide ( mPile->settings.refuse.rotten_raw_hide ); +void StockpileSerializer::write_refuse() { + DEBUG(log).print("refuse:\n"); + StockpileSettings::RefuseSet* refuse = mBuffer.mutable_refuse(); + refuse->set_fresh_raw_hide(mPile->settings.refuse.fresh_raw_hide); + refuse->set_rotten_raw_hide(mPile->settings.refuse.rotten_raw_hide); // type - FuncItemAllowed filter = std::bind ( &StockpileSerializer::refuse_type_is_allowed, this, _1 ); - serialize_list_item_type ( filter, [=] ( const std::string &token ) - { - refuse->add_type ( token ); - }, mPile->settings.refuse.type ); + DEBUG(log).print("getting types\n"); + FuncItemAllowed filter = std::bind(&StockpileSerializer::refuse_type_is_allowed, this, _1); + serialize_list_item_type( + filter, [=](const std::string& token) { + DEBUG(log).print("adding type: %s\n", token.c_str()); + refuse->add_type(token); + }, + mPile->settings.refuse.type); // corpses - refuse_write_helper ( [=] ( const std::string &id ) - { - refuse->add_corpses ( id ); - }, mPile->settings.refuse.corpses ); + refuse_write_helper([=](const std::string& id) { refuse->add_corpses(id); }, + mPile->settings.refuse.corpses); // body_parts - refuse_write_helper ( [=] ( const std::string &id ) - { - refuse->add_body_parts ( id ); - }, mPile->settings.refuse.body_parts ); + refuse_write_helper([=](const std::string& id) { refuse->add_body_parts(id); }, + mPile->settings.refuse.body_parts); // skulls - refuse_write_helper ( [=] ( const std::string &id ) - { - refuse->add_skulls ( id ); - }, mPile->settings.refuse.skulls ); + refuse_write_helper([=](const std::string& id) { refuse->add_skulls(id); }, + mPile->settings.refuse.skulls); // bones - refuse_write_helper ( [=] ( const std::string &id ) - { - refuse->add_bones ( id ); - }, mPile->settings.refuse.bones ); + refuse_write_helper([=](const std::string& id) { refuse->add_bones(id); }, + mPile->settings.refuse.bones); // hair - refuse_write_helper ( [=] ( const std::string &id ) - { - refuse->add_hair ( id ); - }, mPile->settings.refuse.hair ); + refuse_write_helper([=](const std::string& id) { refuse->add_hair(id); }, + mPile->settings.refuse.hair); // shells - refuse_write_helper ( [=] ( const std::string &id ) - { - refuse->add_shells ( id ); - }, mPile->settings.refuse.shells ); + refuse_write_helper([=](const std::string& id) { refuse->add_shells(id); }, + mPile->settings.refuse.shells); // teeth - refuse_write_helper ( [=] ( const std::string &id ) - { - refuse->add_teeth ( id ); - }, mPile->settings.refuse.teeth ); + refuse_write_helper([=](const std::string& id) { refuse->add_teeth(id); }, + mPile->settings.refuse.teeth); // horns - refuse_write_helper ( [=] ( const std::string &id ) - { - refuse->add_horns ( id ); - }, mPile->settings.refuse.horns ); + refuse_write_helper([=](const std::string& id) { refuse->add_horns(id); }, + mPile->settings.refuse.horns); } -void StockpileSerializer::refuse_read_helper ( std::function get_value, size_t list_size, std::vector* pile_list ) -{ +void StockpileSerializer::refuse_read_helper(std::function get_value, size_t list_size, std::vector* pile_list) { pile_list->clear(); - pile_list->resize ( world->raws.creatures.all.size(), '\0' ); - if ( list_size > 0 ) - { - for ( size_t i = 0; i < list_size; ++i ) - { - const std::string creature_id = get_value ( i ); - const int idx = find_creature ( creature_id ); - const df::creature_raw* creature = find_creature ( idx ); - if ( idx < 0 || !refuse_creature_is_allowed ( creature ) || size_t(idx) >= pile_list->size() ) - { - debug() << "WARNING invalid refuse creature " << creature_id << ", idx=" << idx << endl; + pile_list->resize(world->raws.creatures.all.size(), '\0'); + if (list_size > 0) { + for (size_t i = 0; i < list_size; ++i) { + const std::string creature_id = get_value(i); + const int idx = find_creature(creature_id); + const df::creature_raw* creature = find_creature(idx); + if (idx < 0 || !refuse_creature_is_allowed(creature) || size_t(idx) >= pile_list->size()) { + WARN(log).print("invalid refuse creature %s, idx=%d\n", creature_id.c_str(), idx); continue; } - debug() << " creature " << idx << " is " << creature_id << endl; - pile_list->at ( idx ) = 1; + DEBUG(log).print("creature %d is %s\n", idx, creature_id.c_str()); + pile_list->at(idx) = 1; } } } - - -void StockpileSerializer::read_refuse() -{ - if ( mBuffer.has_refuse() ) - { +void StockpileSerializer::read_refuse() { + if (mBuffer.has_refuse()) { mPile->settings.flags.bits.refuse = 1; const StockpileSettings::RefuseSet refuse = mBuffer.refuse(); - debug() << "refuse: " <settings.refuse.fresh_raw_hide = refuse.fresh_raw_hide(); - mPile->settings.refuse.rotten_raw_hide = refuse.rotten_raw_hide(); + DEBUG(log).print("refuse:\n"); + DEBUG(log).print(" fresh hide %d\n", refuse.fresh_raw_hide()); + DEBUG(log).print(" rotten hide %d\n", refuse.rotten_raw_hide()); + mPile->settings.refuse.fresh_raw_hide = refuse.fresh_raw_hide(); + mPile->settings.refuse.rotten_raw_hide = refuse.rotten_raw_hide(); // type - FuncItemAllowed filter = std::bind ( &StockpileSerializer::refuse_type_is_allowed, this, _1 ); - unserialize_list_item_type ( filter, [=] ( const size_t & idx ) -> const std::string& - { - return refuse.type ( idx ); - }, refuse.type_size(), &mPile->settings.refuse.type ); + FuncItemAllowed filter = std::bind(&StockpileSerializer::refuse_type_is_allowed, this, _1); + unserialize_list_item_type( + filter, [=](const size_t& idx) -> const std::string& { return refuse.type(idx); }, + refuse.type_size(), &mPile->settings.refuse.type); // corpses - debug() << " corpses" << endl; - refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& - { - return refuse.corpses ( idx ); - }, refuse.corpses_size(), &mPile->settings.refuse.corpses ); + DEBUG(log).print(" corpses\n"); + refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.corpses(idx); }, + refuse.corpses_size(), &mPile->settings.refuse.corpses); // body_parts - debug() << " body_parts" << endl; - refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& - { - return refuse.body_parts ( idx ); - }, refuse.body_parts_size(), &mPile->settings.refuse.body_parts ); + DEBUG(log).print(" body_parts\n"); + refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.body_parts(idx); }, + refuse.body_parts_size(), &mPile->settings.refuse.body_parts); // skulls - debug() << " skulls" << endl; - refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& - { - return refuse.skulls ( idx ); - }, refuse.skulls_size(), &mPile->settings.refuse.skulls ); + DEBUG(log).print(" skulls\n"); + refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.skulls(idx); }, + refuse.skulls_size(), &mPile->settings.refuse.skulls); // bones - debug() << " bones" << endl; - refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& - { - return refuse.bones ( idx ); - }, refuse.bones_size(), &mPile->settings.refuse.bones ); + DEBUG(log).print(" bones\n"); + refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.bones(idx); }, + refuse.bones_size(), &mPile->settings.refuse.bones); // hair - debug() << " hair" << endl; - refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& - { - return refuse.hair ( idx ); - }, refuse.hair_size(), &mPile->settings.refuse.hair ); + DEBUG(log).print(" hair\n"); + refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.hair(idx); }, + refuse.hair_size(), &mPile->settings.refuse.hair); // shells - debug() << " shells" << endl; - refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& - { - return refuse.shells ( idx ); - }, refuse.shells_size(), &mPile->settings.refuse.shells ); + DEBUG(log).print(" shells\n"); + refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.shells(idx); }, + refuse.shells_size(), &mPile->settings.refuse.shells); // teeth - debug() << " teeth" << endl; - refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& - { - return refuse.teeth ( idx ); - }, refuse.teeth_size(), &mPile->settings.refuse.teeth ); + DEBUG(log).print(" teeth\n"); + refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.teeth(idx); }, + refuse.teeth_size(), &mPile->settings.refuse.teeth); // horns - debug() << " horns" << endl; - refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& - { - return refuse.horns ( idx ); - }, refuse.horns_size(), &mPile->settings.refuse.horns ); + DEBUG(log).print(" horns\n"); + refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.horns(idx); }, + refuse.horns_size(), &mPile->settings.refuse.horns); } - else - { + else { mPile->settings.flags.bits.refuse = 0; mPile->settings.refuse.type.clear(); mPile->settings.refuse.corpses.clear(); @@ -1142,287 +941,232 @@ void StockpileSerializer::read_refuse() } } -bool StockpileSerializer::stone_is_allowed ( const MaterialInfo &mi ) -{ - if ( !mi.isValid() ) return false; - const bool is_allowed_soil = mi.inorganic->flags.is_set ( inorganic_flags::SOIL ) && !mi.inorganic->flags.is_set ( inorganic_flags::AQUIFER ); - const bool is_allowed_stone = mi.material->flags.is_set ( material_flags::IS_STONE ) && !mi.material->flags.is_set ( material_flags::NO_STONE_STOCKPILE ); - return is_allowed_soil || is_allowed_stone; +bool StockpileSerializer::stone_is_allowed(const MaterialInfo& mi) { + if (!mi.isValid()) + return false; + const bool is_allowed_soil = mi.inorganic->flags.is_set(inorganic_flags::SOIL) && !mi.inorganic->flags.is_set(inorganic_flags::AQUIFER); + const bool is_allowed_stone = mi.material->flags.is_set(material_flags::IS_STONE) && !mi.material->flags.is_set(material_flags::NO_STONE_STOCKPILE); + return is_allowed_soil || is_allowed_stone; } -void StockpileSerializer::write_stone() -{ - StockpileSettings::StoneSet *stone= mBuffer.mutable_stone(); +void StockpileSerializer::write_stone() { + StockpileSettings::StoneSet* stone = mBuffer.mutable_stone(); - FuncMaterialAllowed filter = std::bind ( &StockpileSerializer::stone_is_allowed, this, _1 ); - serialize_list_material ( filter, [=] ( const std::string &token ) - { - stone->add_mats ( token ); - }, mPile->settings.stone.mats ); + FuncMaterialAllowed filter = std::bind(&StockpileSerializer::stone_is_allowed, this, _1); + serialize_list_material( + filter, [=](const std::string& token) { stone->add_mats(token); }, + mPile->settings.stone.mats); } -void StockpileSerializer::read_stone() -{ - if ( mBuffer.has_stone() ) - { +void StockpileSerializer::read_stone() { + if (mBuffer.has_stone()) { mPile->settings.flags.bits.stone = 1; const StockpileSettings::StoneSet stone = mBuffer.stone(); - debug() << "stone: " < const std::string& - { - return stone.mats ( idx ); - }, stone.mats_size(), &mPile->settings.stone.mats ); + FuncMaterialAllowed filter = std::bind(&StockpileSerializer::stone_is_allowed, this, _1); + unserialize_list_material( + filter, [=](const size_t& idx) -> const std::string& { return stone.mats(idx); }, + stone.mats_size(), &mPile->settings.stone.mats); } - else - { + else { mPile->settings.flags.bits.stone = 0; mPile->settings.stone.mats.clear(); } } -bool StockpileSerializer::ammo_mat_is_allowed ( const MaterialInfo &mi ) -{ - return mi.isValid() && mi.material && mi.material->flags.is_set ( material_flags::IS_METAL ); +bool StockpileSerializer::ammo_mat_is_allowed(const MaterialInfo& mi) { + return mi.isValid() && mi.material && mi.material->flags.is_set(material_flags::IS_METAL); } -void StockpileSerializer::write_ammo() -{ - StockpileSettings::AmmoSet *ammo = mBuffer.mutable_ammo(); +void StockpileSerializer::write_ammo() { + StockpileSettings::AmmoSet* ammo = mBuffer.mutable_ammo(); // ammo type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - ammo->add_type ( token ); - }, mPile->settings.ammo.type, - std::vector ( world->raws.itemdefs.ammo.begin(),world->raws.itemdefs.ammo.end() ), - item_type::AMMO ); + serialize_list_itemdef([=](const std::string& token) { ammo->add_type(token); }, + mPile->settings.ammo.type, + std::vector(world->raws.itemdefs.ammo.begin(), world->raws.itemdefs.ammo.end()), + item_type::AMMO); // metal - FuncMaterialAllowed filter = std::bind ( &StockpileSerializer::ammo_mat_is_allowed, this, _1 ); - serialize_list_material ( filter, [=] ( const std::string &token ) - { - ammo->add_mats ( token ); - }, mPile->settings.ammo.mats ); + FuncMaterialAllowed filter = std::bind(&StockpileSerializer::ammo_mat_is_allowed, this, _1); + serialize_list_material( + filter, [=](const std::string& token) { ammo->add_mats(token); }, + mPile->settings.ammo.mats); // other mats - only wood and bone - if ( mPile->settings.ammo.other_mats.size() > 2 ) - { - debug() << "WARNING: ammo other materials > 2! " << mPile->settings.ammo.other_mats.size() << endl; + if (mPile->settings.ammo.other_mats.size() > 2) { + WARN(log).print("ammo other materials > 2: %zd\n", mPile->settings.ammo.other_mats.size()); } - for ( size_t i = 0; i < std::min ( size_t ( 2 ), mPile->settings.ammo.other_mats.size() ); ++i ) - { - if ( !mPile->settings.ammo.other_mats.at ( i ) ) + for (size_t i = 0; i < std::min(size_t(2), mPile->settings.ammo.other_mats.size()); ++i) { + if (!mPile->settings.ammo.other_mats.at(i)) continue; - const std::string token = i == 0 ? "WOOD" : "BONE"; - ammo->add_other_mats ( token ); - debug() << " other mats " << i << " is " << token << endl; + const std::string token = i == 0 ? "WOOD" : "BONE"; + ammo->add_other_mats(token); + DEBUG(log).print(" other mats %zd is %s\n", i, token.c_str()); } // quality core - serialize_list_quality ( [=] ( const std::string &token ) - { - ammo->add_quality_core ( token ); - }, mPile->settings.ammo.quality_core ); + serialize_list_quality([=](const std::string& token) { ammo->add_quality_core(token); }, + mPile->settings.ammo.quality_core); // quality total - serialize_list_quality ( [=] ( const std::string &token ) - { - ammo->add_quality_total ( token ); - }, mPile->settings.ammo.quality_total ); + serialize_list_quality([=](const std::string& token) { ammo->add_quality_total(token); }, + mPile->settings.ammo.quality_total); } -void StockpileSerializer::read_ammo() -{ - if ( mBuffer.has_ammo() ) - { +void StockpileSerializer::read_ammo() { + if (mBuffer.has_ammo()) { mPile->settings.flags.bits.ammo = 1; const StockpileSettings::AmmoSet ammo = mBuffer.ammo(); - debug() << "ammo: " < const std::string& - { - return ammo.type ( idx ); - }, ammo.type_size(), &mPile->settings.ammo.type, item_type::AMMO ); + unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return ammo.type(idx); }, + ammo.type_size(), &mPile->settings.ammo.type, item_type::AMMO); // materials metals - FuncMaterialAllowed filter = std::bind ( &StockpileSerializer::ammo_mat_is_allowed, this, _1 ); - unserialize_list_material ( filter, [=] ( const size_t & idx ) -> const std::string& - { - return ammo.mats ( idx ); - }, ammo.mats_size(), &mPile->settings.ammo.mats ); + FuncMaterialAllowed filter = std::bind(&StockpileSerializer::ammo_mat_is_allowed, this, _1); + unserialize_list_material( + filter, [=](const size_t& idx) -> const std::string& { return ammo.mats(idx); }, + ammo.mats_size(), &mPile->settings.ammo.mats); // others mPile->settings.ammo.other_mats.clear(); - mPile->settings.ammo.other_mats.resize ( 2, '\0' ); - if ( ammo.other_mats_size() > 0 ) - { + mPile->settings.ammo.other_mats.resize(2, '\0'); + if (ammo.other_mats_size() > 0) { // TODO remove hardcoded value - for ( int i = 0; i < ammo.other_mats_size(); ++i ) - { - const std::string token = ammo.other_mats ( i ); - const int32_t idx = token == "WOOD" ? 0 : token == "BONE" ? 1 : -1; - debug() << " other mats " << idx << " is " << token << endl; - if ( idx != -1 ) - mPile->settings.ammo.other_mats.at ( idx ) = 1; + for (int i = 0; i < ammo.other_mats_size(); ++i) { + const std::string token = ammo.other_mats(i); + const int32_t idx = token == "WOOD" ? 0 : token == "BONE" ? 1 + : -1; + DEBUG(log).print("other mats %d is %s\n", idx, token.c_str()); + if (idx != -1) + mPile->settings.ammo.other_mats.at(idx) = 1; } } // core quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return ammo.quality_core ( idx ); - }, ammo.quality_core_size(), mPile->settings.ammo.quality_core ); + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return ammo.quality_core(idx); }, + ammo.quality_core_size(), mPile->settings.ammo.quality_core); // total quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return ammo.quality_total ( idx ); - }, ammo.quality_total_size(), mPile->settings.ammo.quality_total ); + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return ammo.quality_total(idx); }, + ammo.quality_total_size(), mPile->settings.ammo.quality_total); } - else - { + else { mPile->settings.flags.bits.ammo = 0; mPile->settings.ammo.type.clear(); mPile->settings.ammo.mats.clear(); mPile->settings.ammo.other_mats.clear(); - quality_clear ( mPile->settings.ammo.quality_core ); - quality_clear ( mPile->settings.ammo.quality_total ); + quality_clear(mPile->settings.ammo.quality_core); + quality_clear(mPile->settings.ammo.quality_total); } } -bool StockpileSerializer::coins_mat_is_allowed ( const MaterialInfo &mi ) -{ +bool StockpileSerializer::coins_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid(); } -void StockpileSerializer::write_coins() -{ - StockpileSettings::CoinSet *coins = mBuffer.mutable_coin(); - FuncMaterialAllowed filter = std::bind ( &StockpileSerializer::coins_mat_is_allowed, this, _1 ); - serialize_list_material ( filter, [=] ( const std::string &token ) - { - coins->add_mats ( token ); - }, mPile->settings.coins.mats ); +void StockpileSerializer::write_coins() { + StockpileSettings::CoinSet* coins = mBuffer.mutable_coin(); + FuncMaterialAllowed filter = std::bind(&StockpileSerializer::coins_mat_is_allowed, this, _1); + serialize_list_material( + filter, [=](const std::string& token) { coins->add_mats(token); }, + mPile->settings.coins.mats); } -void StockpileSerializer::read_coins() -{ - if ( mBuffer.has_coin() ) - { +void StockpileSerializer::read_coins() { + if (mBuffer.has_coin()) { mPile->settings.flags.bits.coins = 1; const StockpileSettings::CoinSet coins = mBuffer.coin(); - debug() << "coins: " < const std::string& - { - return coins.mats ( idx ); - }, coins.mats_size(), &mPile->settings.coins.mats ); + FuncMaterialAllowed filter = std::bind(&StockpileSerializer::coins_mat_is_allowed, this, _1); + unserialize_list_material( + filter, [=](const size_t& idx) -> const std::string& { return coins.mats(idx); }, + coins.mats_size(), &mPile->settings.coins.mats); } - else - { + else { mPile->settings.flags.bits.coins = 0; mPile->settings.coins.mats.clear(); } } -void StockpileSerializer::bars_blocks_setup_other_mats() -{ - mOtherMatsBars.insert ( std::make_pair ( 0,"COAL" ) ); - mOtherMatsBars.insert ( std::make_pair ( 1,"POTASH" ) ); - mOtherMatsBars.insert ( std::make_pair ( 2,"ASH" ) ); - mOtherMatsBars.insert ( std::make_pair ( 3,"PEARLASH" ) ); - mOtherMatsBars.insert ( std::make_pair ( 4,"SOAP" ) ); +void StockpileSerializer::bars_blocks_setup_other_mats() { + mOtherMatsBars.insert(std::make_pair(0, "COAL")); + mOtherMatsBars.insert(std::make_pair(1, "POTASH")); + mOtherMatsBars.insert(std::make_pair(2, "ASH")); + mOtherMatsBars.insert(std::make_pair(3, "PEARLASH")); + mOtherMatsBars.insert(std::make_pair(4, "SOAP")); - mOtherMatsBlocks.insert ( std::make_pair ( 0,"GREEN_GLASS" ) ); - mOtherMatsBlocks.insert ( std::make_pair ( 1,"CLEAR_GLASS" ) ); - mOtherMatsBlocks.insert ( std::make_pair ( 2,"CRYSTAL_GLASS" ) ); - mOtherMatsBlocks.insert ( std::make_pair ( 3,"WOOD" ) ); + mOtherMatsBlocks.insert(std::make_pair(0, "GREEN_GLASS")); + mOtherMatsBlocks.insert(std::make_pair(1, "CLEAR_GLASS")); + mOtherMatsBlocks.insert(std::make_pair(2, "CRYSTAL_GLASS")); + mOtherMatsBlocks.insert(std::make_pair(3, "WOOD")); } -bool StockpileSerializer::bars_mat_is_allowed ( const MaterialInfo &mi ) -{ - return mi.isValid() && mi.material && mi.material->flags.is_set ( material_flags::IS_METAL ); +bool StockpileSerializer::bars_mat_is_allowed(const MaterialInfo& mi) { + return mi.isValid() && mi.material && mi.material->flags.is_set(material_flags::IS_METAL); } -bool StockpileSerializer::blocks_mat_is_allowed ( const MaterialInfo &mi ) -{ - return mi.isValid() && mi.material - && ( mi.material->flags.is_set ( material_flags::IS_METAL ) - || mi.material->flags.is_set ( material_flags::IS_STONE ) ); +bool StockpileSerializer::blocks_mat_is_allowed(const MaterialInfo& mi) { + return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_METAL) || mi.material->flags.is_set(material_flags::IS_STONE)); } - -void StockpileSerializer::write_bars_blocks() -{ - StockpileSettings::BarsBlocksSet *bars_blocks = mBuffer.mutable_barsblocks(); +void StockpileSerializer::write_bars_blocks() { + StockpileSettings::BarsBlocksSet* bars_blocks = mBuffer.mutable_barsblocks(); MaterialInfo mi; - FuncMaterialAllowed filter = std::bind ( &StockpileSerializer::bars_mat_is_allowed, this, _1 ); - serialize_list_material ( filter, [=] ( const std::string &token ) - { - bars_blocks->add_bars_mats ( token ); - }, mPile->settings.bars_blocks.bars_mats ); + FuncMaterialAllowed filter = std::bind(&StockpileSerializer::bars_mat_is_allowed, this, _1); + serialize_list_material( + filter, [=](const std::string& token) { bars_blocks->add_bars_mats(token); }, + mPile->settings.bars_blocks.bars_mats); // blocks mats - filter = std::bind ( &StockpileSerializer::blocks_mat_is_allowed, this, _1 ); - serialize_list_material ( filter, [=] ( const std::string &token ) - { - bars_blocks->add_blocks_mats ( token ); - }, mPile->settings.bars_blocks.blocks_mats ); + filter = std::bind(&StockpileSerializer::blocks_mat_is_allowed, this, _1); + serialize_list_material( + filter, [=](const std::string& token) { bars_blocks->add_blocks_mats(token); }, + mPile->settings.bars_blocks.blocks_mats); // bars other mats - serialize_list_other_mats ( mOtherMatsBars, [=] ( const std::string &token ) - { - bars_blocks->add_bars_other_mats ( token ); - }, mPile->settings.bars_blocks.bars_other_mats ); + serialize_list_other_mats( + mOtherMatsBars, [=](const std::string& token) { bars_blocks->add_bars_other_mats(token); }, + mPile->settings.bars_blocks.bars_other_mats); // blocks other mats - serialize_list_other_mats ( mOtherMatsBlocks, [=] ( const std::string &token ) - { - bars_blocks->add_blocks_other_mats ( token ); - }, mPile->settings.bars_blocks.blocks_other_mats ); + serialize_list_other_mats( + mOtherMatsBlocks, [=](const std::string& token) { bars_blocks->add_blocks_other_mats(token); }, + mPile->settings.bars_blocks.blocks_other_mats); } -void StockpileSerializer::read_bars_blocks() -{ - if ( mBuffer.has_barsblocks() ) - { +void StockpileSerializer::read_bars_blocks() { + if (mBuffer.has_barsblocks()) { mPile->settings.flags.bits.bars_blocks = 1; const StockpileSettings::BarsBlocksSet bars_blocks = mBuffer.barsblocks(); - debug() << "bars_blocks: " < const std::string& - { - return bars_blocks.bars_mats ( idx ); - }, bars_blocks.bars_mats_size(), &mPile->settings.bars_blocks.bars_mats ); + FuncMaterialAllowed filter = std::bind(&StockpileSerializer::bars_mat_is_allowed, this, _1); + unserialize_list_material( + filter, [=](const size_t& idx) -> const std::string& { return bars_blocks.bars_mats(idx); }, + bars_blocks.bars_mats_size(), &mPile->settings.bars_blocks.bars_mats); // blocks - filter = std::bind ( &StockpileSerializer::blocks_mat_is_allowed, this, _1 ); - unserialize_list_material ( filter, [=] ( const size_t & idx ) -> const std::string& - { - return bars_blocks.blocks_mats ( idx ); - }, bars_blocks.blocks_mats_size(), &mPile->settings.bars_blocks.blocks_mats ); + filter = std::bind(&StockpileSerializer::blocks_mat_is_allowed, this, _1); + unserialize_list_material( + filter, [=](const size_t& idx) -> const std::string& { return bars_blocks.blocks_mats(idx); }, + bars_blocks.blocks_mats_size(), &mPile->settings.bars_blocks.blocks_mats); // bars other mats - unserialize_list_other_mats ( mOtherMatsBars, [=] ( const size_t & idx ) -> const std::string& - { - return bars_blocks.bars_other_mats ( idx ); - }, bars_blocks.bars_other_mats_size(), &mPile->settings.bars_blocks.bars_other_mats ); - + unserialize_list_other_mats( + mOtherMatsBars, [=](const size_t& idx) -> const std::string& { return bars_blocks.bars_other_mats(idx); }, + bars_blocks.bars_other_mats_size(), &mPile->settings.bars_blocks.bars_other_mats); // blocks other mats - unserialize_list_other_mats ( mOtherMatsBlocks, [=] ( const size_t & idx ) -> const std::string& - { - return bars_blocks.blocks_other_mats ( idx ); - }, bars_blocks.blocks_other_mats_size(), &mPile->settings.bars_blocks.blocks_other_mats ); - + unserialize_list_other_mats( + mOtherMatsBlocks, [=](const size_t& idx) -> const std::string& { return bars_blocks.blocks_other_mats(idx); }, + bars_blocks.blocks_other_mats_size(), &mPile->settings.bars_blocks.blocks_other_mats); } - else - { + else { mPile->settings.flags.bits.bars_blocks = 0; mPile->settings.bars_blocks.bars_other_mats.clear(); mPile->settings.bars_blocks.bars_mats.clear(); @@ -1431,118 +1175,102 @@ void StockpileSerializer::read_bars_blocks() } } -bool StockpileSerializer::gem_mat_is_allowed ( const MaterialInfo &mi ) -{ - return mi.isValid() && mi.material && mi.material->flags.is_set ( material_flags::IS_GEM ); +bool StockpileSerializer::gem_mat_is_allowed(const MaterialInfo& mi) { + return mi.isValid() && mi.material && mi.material->flags.is_set(material_flags::IS_GEM); } -bool StockpileSerializer::gem_cut_mat_is_allowed ( const MaterialInfo &mi ) -{ - return mi.isValid() && mi.material && ( mi.material->flags.is_set ( material_flags::IS_GEM ) || mi.material->flags.is_set ( material_flags::IS_STONE ) ) ; +bool StockpileSerializer::gem_cut_mat_is_allowed(const MaterialInfo& mi) { + return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_GEM) || mi.material->flags.is_set(material_flags::IS_STONE)); } -bool StockpileSerializer::gem_other_mat_is_allowed ( MaterialInfo &mi ) -{ - return mi.isValid() && ( mi.getToken() == "GLASS_GREEN" || mi.getToken() == "GLASS_CLEAR" || mi.getToken() == "GLASS_CRYSTAL" ); +bool StockpileSerializer::gem_other_mat_is_allowed(MaterialInfo& mi) { + return mi.isValid() && (mi.getToken() == "GLASS_GREEN" || mi.getToken() == "GLASS_CLEAR" || mi.getToken() == "GLASS_CRYSTAL"); } -void StockpileSerializer::write_gems() -{ - StockpileSettings::GemsSet *gems = mBuffer.mutable_gems(); +void StockpileSerializer::write_gems() { + StockpileSettings::GemsSet* gems = mBuffer.mutable_gems(); MaterialInfo mi; // rough mats - FuncMaterialAllowed filter_rough = std::bind ( &StockpileSerializer::gem_mat_is_allowed, this, _1 ); - serialize_list_material ( filter_rough, [=] ( const std::string &token ) - { - gems->add_rough_mats ( token ); - }, mPile->settings.gems.rough_mats ); + FuncMaterialAllowed filter_rough = std::bind(&StockpileSerializer::gem_mat_is_allowed, this, _1); + serialize_list_material( + filter_rough, [=](const std::string& token) { gems->add_rough_mats(token); }, + mPile->settings.gems.rough_mats); // cut mats - FuncMaterialAllowed filter_cut = std::bind ( &StockpileSerializer::gem_cut_mat_is_allowed, this, _1 ); - serialize_list_material ( filter_cut, [=] ( const std::string &token ) - { - gems->add_cut_mats ( token ); - }, mPile->settings.gems.cut_mats ); + FuncMaterialAllowed filter_cut = std::bind(&StockpileSerializer::gem_cut_mat_is_allowed, this, _1); + serialize_list_material( + filter_cut, [=](const std::string& token) { gems->add_cut_mats(token); }, + mPile->settings.gems.cut_mats); // rough other - for ( size_t i = 0; i < mPile->settings.gems.rough_other_mats.size(); ++i ) - { - if ( mPile->settings.gems.rough_other_mats.at ( i ) ) - { - mi.decode ( i, -1 ); - if ( !gem_other_mat_is_allowed ( mi ) ) continue; - debug() << " gem rough_other mat" << i << " is " << mi.getToken() << endl; - gems->add_rough_other_mats ( mi.getToken() ); + for (size_t i = 0; i < mPile->settings.gems.rough_other_mats.size(); ++i) { + if (mPile->settings.gems.rough_other_mats.at(i)) { + mi.decode(i, -1); + if (!gem_other_mat_is_allowed(mi)) + continue; + DEBUG(log).print("gem rough_other mat %zd is %s\n", i, mi.getToken().c_str()); + gems->add_rough_other_mats(mi.getToken()); } } // cut other - for ( size_t i = 0; i < mPile->settings.gems.cut_other_mats.size(); ++i ) - { - if ( mPile->settings.gems.cut_other_mats.at ( i ) ) - { - mi.decode ( i, -1 ); - if ( !mi.isValid() ) mi.decode ( 0, i ); - if ( !gem_other_mat_is_allowed ( mi ) ) continue; - debug() << " gem cut_other mat" << i << " is " << mi.getToken() << endl; - gems->add_cut_other_mats ( mi.getToken() ); + for (size_t i = 0; i < mPile->settings.gems.cut_other_mats.size(); ++i) { + if (mPile->settings.gems.cut_other_mats.at(i)) { + mi.decode(i, -1); + if (!mi.isValid()) + mi.decode(0, i); + if (!gem_other_mat_is_allowed(mi)) + continue; + DEBUG(log).print("gem cut_other mat %zd is %s\n", i, mi.getToken().c_str()); + gems->add_cut_other_mats(mi.getToken()); } } } -void StockpileSerializer::read_gems() -{ - if ( mBuffer.has_gems() ) - { +void StockpileSerializer::read_gems() { + if (mBuffer.has_gems()) { mPile->settings.flags.bits.gems = 1; const StockpileSettings::GemsSet gems = mBuffer.gems(); - debug() << "gems: " < const std::string& - { - return gems.rough_mats ( idx ); - }, gems.rough_mats_size(), &mPile->settings.gems.rough_mats ); + FuncMaterialAllowed filter_rough = std::bind(&StockpileSerializer::gem_mat_is_allowed, this, _1); + unserialize_list_material( + filter_rough, [=](const size_t& idx) -> const std::string& { return gems.rough_mats(idx); }, + gems.rough_mats_size(), &mPile->settings.gems.rough_mats); // cut - FuncMaterialAllowed filter_cut = std::bind ( &StockpileSerializer::gem_cut_mat_is_allowed, this, _1 ); - unserialize_list_material ( filter_cut, [=] ( const size_t & idx ) -> const std::string& - { - return gems.cut_mats ( idx ); - }, gems.cut_mats_size(), &mPile->settings.gems.cut_mats ); + FuncMaterialAllowed filter_cut = std::bind(&StockpileSerializer::gem_cut_mat_is_allowed, this, _1); + unserialize_list_material( + filter_cut, [=](const size_t& idx) -> const std::string& { return gems.cut_mats(idx); }, + gems.cut_mats_size(), &mPile->settings.gems.cut_mats); - const size_t builtin_size = std::extentraws.mat_table.builtin ) >::value; + const size_t builtin_size = std::extentraws.mat_table.builtin)>::value; // rough other mPile->settings.gems.rough_other_mats.clear(); - mPile->settings.gems.rough_other_mats.resize ( builtin_size, '\0' ); - for ( int i = 0; i < gems.rough_other_mats_size(); ++i ) - { - const std::string token = gems.rough_other_mats ( i ); + mPile->settings.gems.rough_other_mats.resize(builtin_size, '\0'); + for (int i = 0; i < gems.rough_other_mats_size(); ++i) { + const std::string token = gems.rough_other_mats(i); MaterialInfo mi; - mi.find ( token ); - if ( !mi.isValid() || size_t(mi.type) >= builtin_size ) - { - debug() << "WARNING: invalid gem mat " << token << ". idx=" << mi.type << endl; + mi.find(token); + if (!mi.isValid() || size_t(mi.type) >= builtin_size) { + WARN(log).print("invalid gem mat %s idx=%d\n", token.c_str(), mi.type); continue; } - debug() << " rough_other mats " << mi.type << " is " << token << endl; - mPile->settings.gems.rough_other_mats.at ( mi.type ) = 1; + DEBUG(log).print("rough_other mats %d is %s\n", mi.type, token.c_str()); + mPile->settings.gems.rough_other_mats.at(mi.type) = 1; } // cut other mPile->settings.gems.cut_other_mats.clear(); - mPile->settings.gems.cut_other_mats.resize ( builtin_size, '\0' ); - for ( int i = 0; i < gems.cut_other_mats_size(); ++i ) - { - const std::string token = gems.cut_other_mats ( i ); + mPile->settings.gems.cut_other_mats.resize(builtin_size, '\0'); + for (int i = 0; i < gems.cut_other_mats_size(); ++i) { + const std::string token = gems.cut_other_mats(i); MaterialInfo mi; - mi.find ( token ); - if ( !mi.isValid() || size_t(mi.type) >= builtin_size ) - { - debug() << "WARNING: invalid gem mat " << token << ". idx=" << mi.type << endl; + mi.find(token); + if (!mi.isValid() || size_t(mi.type) >= builtin_size) { + WARN(log).print("invalid gem mat %s idx=%d\n", token.c_str(), mi.type); continue; } - debug() << " cut_other mats " << mi.type << " is " << token << endl; - mPile->settings.gems.cut_other_mats.at ( mi.type ) = 1; + DEBUG(log).print("cut_other mats %d is %s\n", mi.type, token.c_str()); + mPile->settings.gems.cut_other_mats.at(mi.type) = 1; } } - else - { + else { mPile->settings.flags.bits.gems = 0; mPile->settings.gems.cut_other_mats.clear(); mPile->settings.gems.cut_mats.clear(); @@ -1551,10 +1279,8 @@ void StockpileSerializer::read_gems() } } -bool StockpileSerializer::finished_goods_type_is_allowed ( item_type::item_type type ) -{ - switch ( type ) - { +bool StockpileSerializer::finished_goods_type_is_allowed(item_type::item_type type) { + switch (type) { case item_type::CHAIN: case item_type::FLASK: case item_type::GOBLET: @@ -1584,252 +1310,182 @@ bool StockpileSerializer::finished_goods_type_is_allowed ( item_type::item_type default: return false; } - } -void StockpileSerializer::finished_goods_setup_other_mats() -{ - mOtherMatsFinishedGoods.insert ( std::make_pair ( 0,"WOOD" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 1,"PLANT_CLOTH" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 2,"BONE" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 3,"TOOTH" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 4,"HORN" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 5,"PEARL" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 6,"SHELL" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 7,"LEATHER" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 8,"SILK" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 9,"AMBER" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 10,"CORAL" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 11,"GREEN_GLASS" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 12,"CLEAR_GLASS" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 13,"CRYSTAL_GLASS" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 14,"YARN" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 15,"WAX" ) ); +void StockpileSerializer::finished_goods_setup_other_mats() { + mOtherMatsFinishedGoods.insert(std::make_pair(0, "WOOD")); + mOtherMatsFinishedGoods.insert(std::make_pair(1, "PLANT_CLOTH")); + mOtherMatsFinishedGoods.insert(std::make_pair(2, "BONE")); + mOtherMatsFinishedGoods.insert(std::make_pair(3, "TOOTH")); + mOtherMatsFinishedGoods.insert(std::make_pair(4, "HORN")); + mOtherMatsFinishedGoods.insert(std::make_pair(5, "PEARL")); + mOtherMatsFinishedGoods.insert(std::make_pair(6, "SHELL")); + mOtherMatsFinishedGoods.insert(std::make_pair(7, "LEATHER")); + mOtherMatsFinishedGoods.insert(std::make_pair(8, "SILK")); + mOtherMatsFinishedGoods.insert(std::make_pair(9, "AMBER")); + mOtherMatsFinishedGoods.insert(std::make_pair(10, "CORAL")); + mOtherMatsFinishedGoods.insert(std::make_pair(11, "GREEN_GLASS")); + mOtherMatsFinishedGoods.insert(std::make_pair(12, "CLEAR_GLASS")); + mOtherMatsFinishedGoods.insert(std::make_pair(13, "CRYSTAL_GLASS")); + mOtherMatsFinishedGoods.insert(std::make_pair(14, "YARN")); + mOtherMatsFinishedGoods.insert(std::make_pair(15, "WAX")); } -bool StockpileSerializer::finished_goods_mat_is_allowed ( const MaterialInfo &mi ) -{ - return mi.isValid() - && mi.material - && ( mi.material->flags.is_set ( material_flags::IS_GEM ) - || mi.material->flags.is_set ( material_flags::IS_METAL ) - || mi.material->flags.is_set ( material_flags::IS_STONE ) ) ; +bool StockpileSerializer::finished_goods_mat_is_allowed(const MaterialInfo& mi) { + return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_GEM) || mi.material->flags.is_set(material_flags::IS_METAL) || mi.material->flags.is_set(material_flags::IS_STONE)); } -void StockpileSerializer::write_finished_goods() -{ - StockpileSettings::FinishedGoodsSet *finished_goods = mBuffer.mutable_finished_goods(); +void StockpileSerializer::write_finished_goods() { + StockpileSettings::FinishedGoodsSet* finished_goods = mBuffer.mutable_finished_goods(); // type - FuncItemAllowed filter = std::bind ( &StockpileSerializer::finished_goods_type_is_allowed, this, _1 ); - serialize_list_item_type ( filter, [=] ( const std::string &token ) - { - finished_goods->add_type ( token ); - }, mPile->settings.finished_goods.type ); + FuncItemAllowed filter = std::bind(&StockpileSerializer::finished_goods_type_is_allowed, this, _1); + serialize_list_item_type( + filter, [=](const std::string& token) { finished_goods->add_type(token); }, + mPile->settings.finished_goods.type); // materials - FuncMaterialAllowed mat_filter = std::bind ( &StockpileSerializer::finished_goods_mat_is_allowed, this, _1 ); - serialize_list_material ( mat_filter, [=] ( const std::string &token ) - { - finished_goods->add_mats ( token ); - }, mPile->settings.finished_goods.mats ); + FuncMaterialAllowed mat_filter = std::bind(&StockpileSerializer::finished_goods_mat_is_allowed, this, _1); + serialize_list_material( + mat_filter, [=](const std::string& token) { finished_goods->add_mats(token); }, + mPile->settings.finished_goods.mats); // other mats - serialize_list_other_mats ( mOtherMatsFinishedGoods, [=] ( const std::string &token ) - { - finished_goods->add_other_mats ( token ); - }, mPile->settings.finished_goods.other_mats ); + serialize_list_other_mats( + mOtherMatsFinishedGoods, [=](const std::string& token) { finished_goods->add_other_mats(token); }, + mPile->settings.finished_goods.other_mats); // quality core - serialize_list_quality ( [=] ( const std::string &token ) - { - finished_goods->add_quality_core ( token ); - }, mPile->settings.finished_goods.quality_core ); + serialize_list_quality([=](const std::string& token) { finished_goods->add_quality_core(token); }, + mPile->settings.finished_goods.quality_core); // quality total - serialize_list_quality ( [=] ( const std::string &token ) - { - finished_goods->add_quality_total ( token ); - }, mPile->settings.finished_goods.quality_total ); + serialize_list_quality([=](const std::string& token) { finished_goods->add_quality_total(token); }, + mPile->settings.finished_goods.quality_total); } -void StockpileSerializer::read_finished_goods() -{ - if ( mBuffer.has_finished_goods() ) - { +void StockpileSerializer::read_finished_goods() { + if (mBuffer.has_finished_goods()) { mPile->settings.flags.bits.finished_goods = 1; const StockpileSettings::FinishedGoodsSet finished_goods = mBuffer.finished_goods(); - debug() << "finished_goods: " < const std::string& - { - return finished_goods.type ( idx ); - }, finished_goods.type_size(), &mPile->settings.finished_goods.type ); + FuncItemAllowed filter = std::bind(&StockpileSerializer::finished_goods_type_is_allowed, this, _1); + unserialize_list_item_type( + filter, [=](const size_t& idx) -> const std::string& { return finished_goods.type(idx); }, + finished_goods.type_size(), &mPile->settings.finished_goods.type); // materials - FuncMaterialAllowed mat_filter = std::bind ( &StockpileSerializer::finished_goods_mat_is_allowed, this, _1 ); - unserialize_list_material ( mat_filter, [=] ( const size_t & idx ) -> const std::string& - { - return finished_goods.mats ( idx ); - }, finished_goods.mats_size(), &mPile->settings.finished_goods.mats ); + FuncMaterialAllowed mat_filter = std::bind(&StockpileSerializer::finished_goods_mat_is_allowed, this, _1); + unserialize_list_material( + mat_filter, [=](const size_t& idx) -> const std::string& { return finished_goods.mats(idx); }, + finished_goods.mats_size(), &mPile->settings.finished_goods.mats); // other mats - unserialize_list_other_mats ( mOtherMatsFinishedGoods, [=] ( const size_t & idx ) -> const std::string& - { - return finished_goods.other_mats ( idx ); - }, finished_goods.other_mats_size(), &mPile->settings.finished_goods.other_mats ); + unserialize_list_other_mats( + mOtherMatsFinishedGoods, [=](const size_t& idx) -> const std::string& { return finished_goods.other_mats(idx); }, + finished_goods.other_mats_size(), &mPile->settings.finished_goods.other_mats); // core quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return finished_goods.quality_core ( idx ); - }, finished_goods.quality_core_size(), mPile->settings.finished_goods.quality_core ); + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return finished_goods.quality_core(idx); }, + finished_goods.quality_core_size(), mPile->settings.finished_goods.quality_core); // total quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return finished_goods.quality_total ( idx ); - }, finished_goods.quality_total_size(), mPile->settings.finished_goods.quality_total ); - + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return finished_goods.quality_total(idx); }, + finished_goods.quality_total_size(), mPile->settings.finished_goods.quality_total); } - else - { + else { mPile->settings.flags.bits.finished_goods = 0; mPile->settings.finished_goods.type.clear(); mPile->settings.finished_goods.other_mats.clear(); mPile->settings.finished_goods.mats.clear(); - quality_clear ( mPile->settings.finished_goods.quality_core ); - quality_clear ( mPile->settings.finished_goods.quality_total ); + quality_clear(mPile->settings.finished_goods.quality_core); + quality_clear(mPile->settings.finished_goods.quality_total); } } -void StockpileSerializer::write_leather() -{ - StockpileSettings::LeatherSet *leather = mBuffer.mutable_leather(); +void StockpileSerializer::write_leather() { + StockpileSettings::LeatherSet* leather = mBuffer.mutable_leather(); - FuncWriteExport setter = [=] ( const std::string &id ) - { - leather->add_mats ( id ); + FuncWriteExport setter = [=](const std::string& id) { + leather->add_mats(id); }; - serialize_list_organic_mat ( setter, &mPile->settings.leather.mats, organic_mat_category::Leather ); + serialize_list_organic_mat(setter, &mPile->settings.leather.mats, organic_mat_category::Leather); } -void StockpileSerializer::read_leather() -{ - if ( mBuffer.has_leather() ) - { +void StockpileSerializer::read_leather() { + if (mBuffer.has_leather()) { mPile->settings.flags.bits.leather = 1; const StockpileSettings::LeatherSet leather = mBuffer.leather(); - debug() << "leather: " < std::string - { - return leather.mats ( idx ); - }, leather.mats_size(), &mPile->settings.leather.mats, organic_mat_category::Leather ); + unserialize_list_organic_mat([=](size_t idx) -> std::string { return leather.mats(idx); }, + leather.mats_size(), &mPile->settings.leather.mats, organic_mat_category::Leather); } - else - { + else { mPile->settings.flags.bits.leather = 0; mPile->settings.leather.mats.clear(); } } -void StockpileSerializer::write_cloth() -{ - StockpileSettings::ClothSet * cloth = mBuffer.mutable_cloth(); +void StockpileSerializer::write_cloth() { + StockpileSettings::ClothSet* cloth = mBuffer.mutable_cloth(); - serialize_list_organic_mat ( [=] ( const std::string &token ) - { - cloth->add_thread_silk ( token ); - }, &mPile->settings.cloth.thread_silk, organic_mat_category::Silk ); + serialize_list_organic_mat([=](const std::string& token) { cloth->add_thread_silk(token); }, + &mPile->settings.cloth.thread_silk, organic_mat_category::Silk); - serialize_list_organic_mat ( [=] ( const std::string &token ) - { - cloth->add_thread_plant ( token ); - }, &mPile->settings.cloth.thread_plant, organic_mat_category::PlantFiber ); + serialize_list_organic_mat([=](const std::string& token) { cloth->add_thread_plant(token); }, + &mPile->settings.cloth.thread_plant, organic_mat_category::PlantFiber); - serialize_list_organic_mat ( [=] ( const std::string &token ) - { - cloth->add_thread_yarn ( token ); - }, &mPile->settings.cloth.thread_yarn, organic_mat_category::Yarn ); + serialize_list_organic_mat([=](const std::string& token) { cloth->add_thread_yarn(token); }, + &mPile->settings.cloth.thread_yarn, organic_mat_category::Yarn); - serialize_list_organic_mat ( [=] ( const std::string &token ) - { - cloth->add_thread_metal ( token ); - }, &mPile->settings.cloth.thread_metal, organic_mat_category::MetalThread ); - - serialize_list_organic_mat ( [=] ( const std::string &token ) - { - cloth->add_cloth_silk ( token ); - }, &mPile->settings.cloth.cloth_silk, organic_mat_category::Silk ); + serialize_list_organic_mat([=](const std::string& token) { cloth->add_thread_metal(token); }, + &mPile->settings.cloth.thread_metal, organic_mat_category::MetalThread); - serialize_list_organic_mat ( [=] ( const std::string &token ) - { - cloth->add_cloth_plant ( token ); - }, &mPile->settings.cloth.cloth_plant, organic_mat_category::PlantFiber ); + serialize_list_organic_mat([=](const std::string& token) { cloth->add_cloth_silk(token); }, + &mPile->settings.cloth.cloth_silk, organic_mat_category::Silk); - serialize_list_organic_mat ( [=] ( const std::string &token ) - { - cloth->add_cloth_yarn ( token ); - }, &mPile->settings.cloth.cloth_yarn, organic_mat_category::Yarn ); + serialize_list_organic_mat([=](const std::string& token) { cloth->add_cloth_plant(token); }, + &mPile->settings.cloth.cloth_plant, organic_mat_category::PlantFiber); - serialize_list_organic_mat ( [=] ( const std::string &token ) - { - cloth->add_cloth_metal ( token ); - }, &mPile->settings.cloth.cloth_metal, organic_mat_category::MetalThread ); + serialize_list_organic_mat([=](const std::string& token) { cloth->add_cloth_yarn(token); }, + &mPile->settings.cloth.cloth_yarn, organic_mat_category::Yarn); + serialize_list_organic_mat([=](const std::string& token) { cloth->add_cloth_metal(token); }, + &mPile->settings.cloth.cloth_metal, organic_mat_category::MetalThread); } -void StockpileSerializer::read_cloth() -{ - if ( mBuffer.has_cloth() ) - { +void StockpileSerializer::read_cloth() { + if (mBuffer.has_cloth()) { mPile->settings.flags.bits.cloth = 1; const StockpileSettings::ClothSet cloth = mBuffer.cloth(); - debug() << "cloth: " < std::string - { - return cloth.thread_silk ( idx ); - }, cloth.thread_silk_size(), &mPile->settings.cloth.thread_silk, organic_mat_category::Silk ); - - unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string - { - return cloth.thread_plant ( idx ); - }, cloth.thread_plant_size(), &mPile->settings.cloth.thread_plant, organic_mat_category::PlantFiber ); - - unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string - { - return cloth.thread_yarn ( idx ); - }, cloth.thread_yarn_size(), &mPile->settings.cloth.thread_yarn, organic_mat_category::Yarn ); - - unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string - { - return cloth.thread_metal ( idx ); - }, cloth.thread_metal_size(), &mPile->settings.cloth.thread_metal, organic_mat_category::MetalThread ); - - unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string - { - return cloth.cloth_silk ( idx ); - }, cloth.cloth_silk_size(), &mPile->settings.cloth.cloth_silk, organic_mat_category::Silk ); - - unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string - { - return cloth.cloth_plant ( idx ); - }, cloth.cloth_plant_size(), &mPile->settings.cloth.cloth_plant, organic_mat_category::PlantFiber ); - - unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string - { - return cloth.cloth_yarn ( idx ); - }, cloth.cloth_yarn_size(), &mPile->settings.cloth.cloth_yarn, organic_mat_category::Yarn ); - - unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string - { - return cloth.cloth_metal ( idx ); - }, cloth.cloth_metal_size(), &mPile->settings.cloth.cloth_metal, organic_mat_category::MetalThread ); - } - else - { + DEBUG(log).print("cloth:\n"); + + unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.thread_silk(idx); }, + cloth.thread_silk_size(), &mPile->settings.cloth.thread_silk, organic_mat_category::Silk); + + unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.thread_plant(idx); }, + cloth.thread_plant_size(), &mPile->settings.cloth.thread_plant, organic_mat_category::PlantFiber); + + unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.thread_yarn(idx); }, + cloth.thread_yarn_size(), &mPile->settings.cloth.thread_yarn, organic_mat_category::Yarn); + + unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.thread_metal(idx); }, + cloth.thread_metal_size(), &mPile->settings.cloth.thread_metal, organic_mat_category::MetalThread); + + unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.cloth_silk(idx); }, + cloth.cloth_silk_size(), &mPile->settings.cloth.cloth_silk, organic_mat_category::Silk); + + unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.cloth_plant(idx); }, + cloth.cloth_plant_size(), &mPile->settings.cloth.cloth_plant, organic_mat_category::PlantFiber); + + unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.cloth_yarn(idx); }, + cloth.cloth_yarn_size(), &mPile->settings.cloth.cloth_yarn, organic_mat_category::Yarn); + + unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.cloth_metal(idx); }, + cloth.cloth_metal_size(), &mPile->settings.cloth.cloth_metal, organic_mat_category::MetalThread); + } + else { mPile->settings.cloth.thread_metal.clear(); mPile->settings.cloth.thread_plant.clear(); mPile->settings.cloth.thread_silk.clear(); @@ -1842,354 +1498,274 @@ void StockpileSerializer::read_cloth() } } -bool StockpileSerializer::wood_mat_is_allowed ( const df::plant_raw * plant ) -{ - return plant && plant->flags.is_set ( plant_raw_flags::TREE ); +bool StockpileSerializer::wood_mat_is_allowed(const df::plant_raw* plant) { + return plant && plant->flags.is_set(plant_raw_flags::TREE); } -void StockpileSerializer::write_wood() -{ - StockpileSettings::WoodSet * wood = mBuffer.mutable_wood(); - for ( size_t i = 0; i < mPile->settings.wood.mats.size(); ++i ) - { - if ( mPile->settings.wood.mats.at ( i ) ) - { - const df::plant_raw * plant = find_plant ( i ); - if ( !wood_mat_is_allowed ( plant ) ) continue; - wood->add_mats ( plant->id ); - debug() << " plant " << i << " is " << plant->id << endl; +void StockpileSerializer::write_wood() { + StockpileSettings::WoodSet* wood = mBuffer.mutable_wood(); + for (size_t i = 0; i < mPile->settings.wood.mats.size(); ++i) { + if (mPile->settings.wood.mats.at(i)) { + const df::plant_raw* plant = find_plant(i); + if (!wood_mat_is_allowed(plant)) + continue; + wood->add_mats(plant->id); + DEBUG(log).print("plant %zd is %s\n", i, plant->id.c_str()); } } } -void StockpileSerializer::read_wood() -{ - if ( mBuffer.has_wood() ) - { +void StockpileSerializer::read_wood() { + if (mBuffer.has_wood()) { mPile->settings.flags.bits.wood = 1; const StockpileSettings::WoodSet wood = mBuffer.wood(); - debug() << "wood: " <settings.wood.mats.clear(); - mPile->settings.wood.mats.resize ( world->raws.plants.all.size(), '\0' ); - for ( int i = 0; i < wood.mats_size(); ++i ) - { - const std::string token = wood.mats ( i ); - const size_t idx = find_plant ( token ); - if ( idx < 0 || idx >= mPile->settings.wood.mats.size() ) - { - debug() << "WARNING wood mat index invalid " << token << ", idx=" << idx << endl; + mPile->settings.wood.mats.resize(world->raws.plants.all.size(), '\0'); + for (int i = 0; i < wood.mats_size(); ++i) { + const std::string token = wood.mats(i); + const size_t idx = find_plant(token); + if (idx < 0 || idx >= mPile->settings.wood.mats.size()) { + WARN(log).print("wood mat index invalid %s idx=%zd\n", token.c_str(), idx); continue; } - debug() << " plant " << idx << " is " << token << endl; - mPile->settings.wood.mats.at ( idx ) = 1; + DEBUG(log).print("plant %zd is %s\n", idx, token.c_str()); + mPile->settings.wood.mats.at(idx) = 1; } } - else - { + else { mPile->settings.flags.bits.wood = 0; mPile->settings.wood.mats.clear(); } } -bool StockpileSerializer::weapons_mat_is_allowed ( const MaterialInfo &mi ) -{ - return mi.isValid() && mi.material && ( - mi.material->flags.is_set ( material_flags::IS_METAL ) || - mi.material->flags.is_set ( material_flags::IS_STONE ) ); - +bool StockpileSerializer::weapons_mat_is_allowed(const MaterialInfo& mi) { + return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_METAL) || mi.material->flags.is_set(material_flags::IS_STONE)); } -void StockpileSerializer::write_weapons() -{ - StockpileSettings::WeaponsSet * weapons = mBuffer.mutable_weapons(); +void StockpileSerializer::write_weapons() { + StockpileSettings::WeaponsSet* weapons = mBuffer.mutable_weapons(); - weapons->set_unusable ( mPile->settings.weapons.unusable ); - weapons->set_usable ( mPile->settings.weapons.usable ); + weapons->set_unusable(mPile->settings.weapons.unusable); + weapons->set_usable(mPile->settings.weapons.usable); // weapon type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - weapons->add_weapon_type ( token ); - }, mPile->settings.weapons.weapon_type, - std::vector ( world->raws.itemdefs.weapons.begin(),world->raws.itemdefs.weapons.end() ), - item_type::WEAPON ); + serialize_list_itemdef([=](const std::string& token) { weapons->add_weapon_type(token); }, + mPile->settings.weapons.weapon_type, + std::vector(world->raws.itemdefs.weapons.begin(), world->raws.itemdefs.weapons.end()), + item_type::WEAPON); // trapcomp type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - weapons->add_trapcomp_type ( token ); - }, mPile->settings.weapons.trapcomp_type, - std::vector ( world->raws.itemdefs.trapcomps.begin(),world->raws.itemdefs.trapcomps.end() ), - item_type::TRAPCOMP ); + serialize_list_itemdef([=](const std::string& token) { weapons->add_trapcomp_type(token); }, + mPile->settings.weapons.trapcomp_type, + std::vector(world->raws.itemdefs.trapcomps.begin(), world->raws.itemdefs.trapcomps.end()), + item_type::TRAPCOMP); // materials - FuncMaterialAllowed mat_filter = std::bind ( &StockpileSerializer::weapons_mat_is_allowed, this, _1 ); - serialize_list_material ( mat_filter, [=] ( const std::string &token ) - { - weapons->add_mats ( token ); - }, mPile->settings.weapons.mats ); + FuncMaterialAllowed mat_filter = std::bind(&StockpileSerializer::weapons_mat_is_allowed, this, _1); + serialize_list_material( + mat_filter, [=](const std::string& token) { weapons->add_mats(token); }, + mPile->settings.weapons.mats); // other mats - serialize_list_other_mats ( mOtherMatsWeaponsArmor, [=] ( const std::string &token ) - { - weapons->add_other_mats ( token ); - }, mPile->settings.weapons.other_mats ); + serialize_list_other_mats( + mOtherMatsWeaponsArmor, [=](const std::string& token) { weapons->add_other_mats(token); }, + mPile->settings.weapons.other_mats); // quality core - serialize_list_quality ( [=] ( const std::string &token ) - { - weapons->add_quality_core ( token ); - }, mPile->settings.weapons.quality_core ); + serialize_list_quality([=](const std::string& token) { weapons->add_quality_core(token); }, + mPile->settings.weapons.quality_core); // quality total - serialize_list_quality ( [=] ( const std::string &token ) - { - weapons->add_quality_total ( token ); - }, mPile->settings.weapons.quality_total ); + serialize_list_quality([=](const std::string& token) { weapons->add_quality_total(token); }, + mPile->settings.weapons.quality_total); } -void StockpileSerializer::read_weapons() -{ - if ( mBuffer.has_weapons() ) - { +void StockpileSerializer::read_weapons() { + if (mBuffer.has_weapons()) { mPile->settings.flags.bits.weapons = 1; const StockpileSettings::WeaponsSet weapons = mBuffer.weapons(); - debug() << "weapons: " <settings.weapons.unusable = unusable; mPile->settings.weapons.usable = usable; // weapon type - unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& - { - return weapons.weapon_type ( idx ); - }, weapons.weapon_type_size(), &mPile->settings.weapons.weapon_type, item_type::WEAPON ); + unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return weapons.weapon_type(idx); }, + weapons.weapon_type_size(), &mPile->settings.weapons.weapon_type, item_type::WEAPON); // trapcomp type - unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& - { - return weapons.trapcomp_type ( idx ); - }, weapons.trapcomp_type_size(), &mPile->settings.weapons.trapcomp_type, item_type::TRAPCOMP ); + unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return weapons.trapcomp_type(idx); }, + weapons.trapcomp_type_size(), &mPile->settings.weapons.trapcomp_type, item_type::TRAPCOMP); // materials - FuncMaterialAllowed mat_filter = std::bind ( &StockpileSerializer::weapons_mat_is_allowed, this, _1 ); - unserialize_list_material ( mat_filter, [=] ( const size_t & idx ) -> const std::string& - { - return weapons.mats ( idx ); - }, weapons.mats_size(), &mPile->settings.weapons.mats ); + FuncMaterialAllowed mat_filter = std::bind(&StockpileSerializer::weapons_mat_is_allowed, this, _1); + unserialize_list_material( + mat_filter, [=](const size_t& idx) -> const std::string& { return weapons.mats(idx); }, + weapons.mats_size(), &mPile->settings.weapons.mats); // other mats - unserialize_list_other_mats ( mOtherMatsWeaponsArmor, [=] ( const size_t & idx ) -> const std::string& - { - return weapons.other_mats ( idx ); - }, weapons.other_mats_size(), &mPile->settings.weapons.other_mats ); - + unserialize_list_other_mats( + mOtherMatsWeaponsArmor, [=](const size_t& idx) -> const std::string& { return weapons.other_mats(idx); }, + weapons.other_mats_size(), &mPile->settings.weapons.other_mats); // core quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return weapons.quality_core ( idx ); - }, weapons.quality_core_size(), mPile->settings.weapons.quality_core ); + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return weapons.quality_core(idx); }, + weapons.quality_core_size(), mPile->settings.weapons.quality_core); // total quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return weapons.quality_total ( idx ); - }, weapons.quality_total_size(), mPile->settings.weapons.quality_total ); + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return weapons.quality_total(idx); }, + weapons.quality_total_size(), mPile->settings.weapons.quality_total); } - else - { + else { mPile->settings.flags.bits.weapons = 0; mPile->settings.weapons.weapon_type.clear(); mPile->settings.weapons.trapcomp_type.clear(); mPile->settings.weapons.other_mats.clear(); mPile->settings.weapons.mats.clear(); - quality_clear ( mPile->settings.weapons.quality_core ); - quality_clear ( mPile->settings.weapons.quality_total ); + quality_clear(mPile->settings.weapons.quality_core); + quality_clear(mPile->settings.weapons.quality_total); } - } -void StockpileSerializer::weapons_armor_setup_other_mats() -{ - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 0,"WOOD" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 1,"PLANT_CLOTH" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 2,"BONE" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 3,"SHELL" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 4,"LEATHER" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 5,"SILK" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 6,"GREEN_GLASS" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 7,"CLEAR_GLASS" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 8,"CRYSTAL_GLASS" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 9,"YARN" ) ); +void StockpileSerializer::weapons_armor_setup_other_mats() { + mOtherMatsWeaponsArmor.insert(std::make_pair(0, "WOOD")); + mOtherMatsWeaponsArmor.insert(std::make_pair(1, "PLANT_CLOTH")); + mOtherMatsWeaponsArmor.insert(std::make_pair(2, "BONE")); + mOtherMatsWeaponsArmor.insert(std::make_pair(3, "SHELL")); + mOtherMatsWeaponsArmor.insert(std::make_pair(4, "LEATHER")); + mOtherMatsWeaponsArmor.insert(std::make_pair(5, "SILK")); + mOtherMatsWeaponsArmor.insert(std::make_pair(6, "GREEN_GLASS")); + mOtherMatsWeaponsArmor.insert(std::make_pair(7, "CLEAR_GLASS")); + mOtherMatsWeaponsArmor.insert(std::make_pair(8, "CRYSTAL_GLASS")); + mOtherMatsWeaponsArmor.insert(std::make_pair(9, "YARN")); } -bool StockpileSerializer::armor_mat_is_allowed ( const MaterialInfo &mi ) -{ - return mi.isValid() && mi.material && mi.material->flags.is_set ( material_flags::IS_METAL ); +bool StockpileSerializer::armor_mat_is_allowed(const MaterialInfo& mi) { + return mi.isValid() && mi.material && mi.material->flags.is_set(material_flags::IS_METAL); } -void StockpileSerializer::write_armor() -{ - StockpileSettings::ArmorSet * armor = mBuffer.mutable_armor(); +void StockpileSerializer::write_armor() { + StockpileSettings::ArmorSet* armor = mBuffer.mutable_armor(); - armor->set_unusable ( mPile->settings.armor.unusable ); - armor->set_usable ( mPile->settings.armor.usable ); + armor->set_unusable(mPile->settings.armor.unusable); + armor->set_usable(mPile->settings.armor.usable); // armor type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - armor->add_body ( token ); - }, mPile->settings.armor.body, - std::vector ( world->raws.itemdefs.armor.begin(),world->raws.itemdefs.armor.end() ), - item_type::ARMOR ); + serialize_list_itemdef([=](const std::string& token) { armor->add_body(token); }, + mPile->settings.armor.body, + std::vector(world->raws.itemdefs.armor.begin(), world->raws.itemdefs.armor.end()), + item_type::ARMOR); // helm type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - armor->add_head ( token ); - }, mPile->settings.armor.head, - std::vector ( world->raws.itemdefs.helms.begin(),world->raws.itemdefs.helms.end() ), - item_type::HELM ); + serialize_list_itemdef([=](const std::string& token) { armor->add_head(token); }, + mPile->settings.armor.head, + std::vector(world->raws.itemdefs.helms.begin(), world->raws.itemdefs.helms.end()), + item_type::HELM); // shoes type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - armor->add_feet ( token ); - }, mPile->settings.armor.feet, - std::vector ( world->raws.itemdefs.shoes.begin(),world->raws.itemdefs.shoes.end() ), - item_type::SHOES ); + serialize_list_itemdef([=](const std::string& token) { armor->add_feet(token); }, + mPile->settings.armor.feet, + std::vector(world->raws.itemdefs.shoes.begin(), world->raws.itemdefs.shoes.end()), + item_type::SHOES); // gloves type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - armor->add_hands ( token ); - }, mPile->settings.armor.hands, - std::vector ( world->raws.itemdefs.gloves.begin(),world->raws.itemdefs.gloves.end() ), - item_type::GLOVES ); + serialize_list_itemdef([=](const std::string& token) { armor->add_hands(token); }, + mPile->settings.armor.hands, + std::vector(world->raws.itemdefs.gloves.begin(), world->raws.itemdefs.gloves.end()), + item_type::GLOVES); // pant type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - armor->add_legs ( token ); - }, mPile->settings.armor.legs, - std::vector ( world->raws.itemdefs.pants.begin(),world->raws.itemdefs.pants.end() ), - item_type::PANTS ); + serialize_list_itemdef([=](const std::string& token) { armor->add_legs(token); }, + mPile->settings.armor.legs, + std::vector(world->raws.itemdefs.pants.begin(), world->raws.itemdefs.pants.end()), + item_type::PANTS); // shield type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - armor->add_shield ( token ); - }, mPile->settings.armor.shield, - std::vector ( world->raws.itemdefs.shields.begin(),world->raws.itemdefs.shields.end() ), - item_type::SHIELD ); + serialize_list_itemdef([=](const std::string& token) { armor->add_shield(token); }, + mPile->settings.armor.shield, + std::vector(world->raws.itemdefs.shields.begin(), world->raws.itemdefs.shields.end()), + item_type::SHIELD); // materials - FuncMaterialAllowed mat_filter = std::bind ( &StockpileSerializer::armor_mat_is_allowed, this, _1 ); - serialize_list_material ( mat_filter, [=] ( const std::string &token ) - { - armor->add_mats ( token ); - }, mPile->settings.armor.mats ); + FuncMaterialAllowed mat_filter = std::bind(&StockpileSerializer::armor_mat_is_allowed, this, _1); + serialize_list_material( + mat_filter, [=](const std::string& token) { armor->add_mats(token); }, + mPile->settings.armor.mats); // other mats - serialize_list_other_mats ( mOtherMatsWeaponsArmor, [=] ( const std::string &token ) - { - armor->add_other_mats ( token ); - }, mPile->settings.armor.other_mats ); + serialize_list_other_mats( + mOtherMatsWeaponsArmor, [=](const std::string& token) { armor->add_other_mats(token); }, + mPile->settings.armor.other_mats); // quality core - serialize_list_quality ( [=] ( const std::string &token ) - { - armor->add_quality_core ( token ); - }, mPile->settings.armor.quality_core ); + serialize_list_quality([=](const std::string& token) { armor->add_quality_core(token); }, + mPile->settings.armor.quality_core); // quality total - serialize_list_quality ( [=] ( const std::string &token ) - { - armor->add_quality_total ( token ); - }, mPile->settings.armor.quality_total ); + serialize_list_quality([=](const std::string& token) { armor->add_quality_total(token); }, + mPile->settings.armor.quality_total); } -void StockpileSerializer::read_armor() -{ - if ( mBuffer.has_armor() ) - { +void StockpileSerializer::read_armor() { + if (mBuffer.has_armor()) { mPile->settings.flags.bits.armor = 1; const StockpileSettings::ArmorSet armor = mBuffer.armor(); - debug() << "armor: " <settings.armor.unusable = unusable; mPile->settings.armor.usable = usable; // body type - unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& - { - return armor.body ( idx ); - }, armor.body_size(), &mPile->settings.armor.body, item_type::ARMOR ); + unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.body(idx); }, + armor.body_size(), &mPile->settings.armor.body, item_type::ARMOR); // head type - unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& - { - return armor.head ( idx ); - }, armor.head_size(), &mPile->settings.armor.head, item_type::HELM ); + unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.head(idx); }, + armor.head_size(), &mPile->settings.armor.head, item_type::HELM); // feet type - unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& - { - return armor.feet ( idx ); - }, armor.feet_size(), &mPile->settings.armor.feet, item_type::SHOES ); + unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.feet(idx); }, + armor.feet_size(), &mPile->settings.armor.feet, item_type::SHOES); // hands type - unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& - { - return armor.hands ( idx ); - }, armor.hands_size(), &mPile->settings.armor.hands, item_type::GLOVES ); + unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.hands(idx); }, + armor.hands_size(), &mPile->settings.armor.hands, item_type::GLOVES); // legs type - unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& - { - return armor.legs ( idx ); - }, armor.legs_size(), &mPile->settings.armor.legs, item_type::PANTS ); + unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.legs(idx); }, + armor.legs_size(), &mPile->settings.armor.legs, item_type::PANTS); // shield type - unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& - { - return armor.shield ( idx ); - }, armor.shield_size(), &mPile->settings.armor.shield, item_type::SHIELD ); + unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.shield(idx); }, + armor.shield_size(), &mPile->settings.armor.shield, item_type::SHIELD); // materials - FuncMaterialAllowed mat_filter = std::bind ( &StockpileSerializer::armor_mat_is_allowed, this, _1 ); - unserialize_list_material ( mat_filter, [=] ( const size_t & idx ) -> const std::string& - { - return armor.mats ( idx ); - }, armor.mats_size(), &mPile->settings.armor.mats ); + FuncMaterialAllowed mat_filter = std::bind(&StockpileSerializer::armor_mat_is_allowed, this, _1); + unserialize_list_material( + mat_filter, [=](const size_t& idx) -> const std::string& { return armor.mats(idx); }, + armor.mats_size(), &mPile->settings.armor.mats); // other mats - unserialize_list_other_mats ( mOtherMatsWeaponsArmor, [=] ( const size_t & idx ) -> const std::string& - { - return armor.other_mats ( idx ); - }, armor.other_mats_size(), &mPile->settings.armor.other_mats ); + unserialize_list_other_mats( + mOtherMatsWeaponsArmor, [=](const size_t& idx) -> const std::string& { return armor.other_mats(idx); }, + armor.other_mats_size(), &mPile->settings.armor.other_mats); // core quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return armor.quality_core ( idx ); - }, armor.quality_core_size(), mPile->settings.armor.quality_core ); + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return armor.quality_core(idx); }, + armor.quality_core_size(), mPile->settings.armor.quality_core); // total quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return armor.quality_total ( idx ); - }, armor.quality_total_size(), mPile->settings.armor.quality_total ); + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return armor.quality_total(idx); }, + armor.quality_total_size(), mPile->settings.armor.quality_total); } - else - { + else { mPile->settings.flags.bits.armor = 0; mPile->settings.armor.body.clear(); mPile->settings.armor.head.clear(); @@ -2199,7 +1775,7 @@ void StockpileSerializer::read_armor() mPile->settings.armor.shield.clear(); mPile->settings.armor.other_mats.clear(); mPile->settings.armor.mats.clear(); - quality_clear ( mPile->settings.armor.quality_core ); - quality_clear ( mPile->settings.armor.quality_total ); + quality_clear(mPile->settings.armor.quality_core); + quality_clear(mPile->settings.armor.quality_total); } } diff --git a/plugins/stockpiles/StockpileSerializer.h b/plugins/stockpiles/StockpileSerializer.h index ff0a38241..3fe2087de 100644 --- a/plugins/stockpiles/StockpileSerializer.h +++ b/plugins/stockpiles/StockpileSerializer.h @@ -1,349 +1,283 @@ #pragma once -// stockpiles plugin -#include "proto/stockpiles.pb.h" - -// dfhack #include "modules/Materials.h" -#include "modules/Items.h" -// df -#include "df/world.h" -#include "df/world_data.h" +#include "df/itemdef.h" #include "df/organic_mat_category.h" -#include "df/furniture_type.h" -#include "df/item_quality.h" -#include "df/item_type.h" - -// stl -#include -#include -#include -#include - -namespace df { -struct building_stockpilest; -} +#include "proto/stockpiles.pb.h" -/** - * Null buffer that acts like /dev/null for when debug is disabled - */ -class NullBuffer : public std::streambuf -{ -public: - int overflow ( int c ); -}; +#include -class NullStream : public std::ostream +namespace df { -public: - NullStream(); -private: - NullBuffer m_sb; -}; - + struct building_stockpilest; +} /** * Class for serializing the stockpile_settings structure into a Google protobuf */ -class StockpileSerializer -{ +class StockpileSerializer { public: - /** - * @param out for debugging - * @param stockpile stockpile to read or write settings to - */ - - StockpileSerializer ( df::building_stockpilest * stockpile ); - - ~StockpileSerializer(); + /** + * @param out for debugging + * @param stockpile stockpile to read or write settings to + */ - void enable_debug ( std::ostream &out ); + StockpileSerializer(df::building_stockpilest* stockpile); - /** - * Since we depend on protobuf-lite, not the full lib, we copy this function from - * protobuf message.cc - */ - bool serialize_to_ostream(std::ostream* output); + ~StockpileSerializer(); - /** - * Will serialize stockpile settings to a file (overwrites existing files) - * @return success/failure - */ - bool serialize_to_file ( const std::string & file ); + /** + * Since we depend on protobuf-lite, not the full lib, we copy this function from + * protobuf message.cc + */ + bool serialize_to_ostream(std::ostream* output); - /** - * Again, copied from message.cc - */ - bool parse_from_istream(std::istream* input); + /** + * Will serialize stockpile settings to a file (overwrites existing files) + * @return success/failure + */ + bool serialize_to_file(const std::string& file); + /** + * Again, copied from message.cc + */ + bool parse_from_istream(std::istream* input); - /** - * Read stockpile settings from file - */ - bool unserialize_from_file ( const std::string & file ); + /** + * Read stockpile settings from file + */ + bool unserialize_from_file(const std::string& file); private: + df::building_stockpilest* mPile; + dfstockpiles::StockpileSettings mBuffer; + std::map mOtherMatsFurniture; + std::map mOtherMatsFinishedGoods; + std::map mOtherMatsBars; + std::map mOtherMatsBlocks; + std::map mOtherMatsWeaponsArmor; + + /** + read memory structures and serialize to protobuf + */ + void write(); + + // parse serialized data into ui indices + void read(); + + /** + * Find an enum's value based off the string label. + * @param traits the enum's trait struct + * @param token the string value in key_table + * @return the enum's value, -1 if not found + */ + template + static typename df::enum_traits::base_type linear_index(df::enum_traits traits, const std::string& token) { + auto j = traits.first_item_value; + auto limit = traits.last_item_value; + // sometimes enums start at -1, which is bad news for array indexing + if (j < 0) { + j += abs(traits.first_item_value); + limit += abs(traits.first_item_value); + } + for (; j <= limit; ++j) { + if (token.compare(traits.key_table[j]) == 0) + return j; + } + return -1; + } + + // read the token from the serailized list during import + typedef std::function FuncReadImport; + // add the token to the serialized list during export + typedef std::function FuncWriteExport; + // are item's of item_type allowed? + typedef std::function FuncItemAllowed; + // is this material allowed? + typedef std::function FuncMaterialAllowed; + + // convenient struct for parsing food stockpile items + struct food_pair { + // exporting + FuncWriteExport set_value; + std::vector* stockpile_values; + // importing + FuncReadImport get_value; + size_t serialized_count; + bool valid; + + food_pair(FuncWriteExport s, std::vector* sp_v, FuncReadImport g, size_t count) + : set_value(s), stockpile_values(sp_v), get_value(g), serialized_count(count), valid(true) { } + food_pair(): valid(false) { } + }; + + /** + * There are many repeated (un)serialization cases throughout the stockpile_settings structure, + * so the most common cases have been generalized into generic functions using lambdas. + * + * The basic process to serialize a stockpile_settings structure is: + * 1. loop through the list + * 2. for every element that is TRUE: + * 3. map the specific stockpile_settings index into a general material, creature, etc index + * 4. verify that type is allowed in the list (e.g., no stone in gems stockpiles) + * 5. add it to the protobuf using FuncWriteExport + * + * The unserialization process is the same in reverse. + */ + void serialize_list_organic_mat(FuncWriteExport add_value, const std::vector* list, df::enums::organic_mat_category::organic_mat_category cat); + + /** + * @see serialize_list_organic_mat + */ + void unserialize_list_organic_mat(FuncReadImport get_value, size_t list_size, std::vector* pile_list, df::enums::organic_mat_category::organic_mat_category cat); + + /** + * @see serialize_list_organic_mat + */ + void serialize_list_item_type(FuncItemAllowed is_allowed, FuncWriteExport add_value, const std::vector& list); + + /** + * @see serialize_list_organic_mat + */ + void unserialize_list_item_type(FuncItemAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector* pile_list); + + /** + * @see serialize_list_organic_mat + */ + void serialize_list_material(FuncMaterialAllowed is_allowed, FuncWriteExport add_value, const std::vector& list); + + /** + * @see serialize_list_organic_mat + */ + void unserialize_list_material(FuncMaterialAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector* pile_list); + + /** + * @see serialize_list_organic_mat + */ + void serialize_list_quality(FuncWriteExport add_value, const bool(&quality_list)[7]); + + /** + * Set all values in a bool[7] to false + */ + void quality_clear(bool(&pile_list)[7]); + + /** + * @see serialize_list_organic_mat + */ + void unserialize_list_quality(FuncReadImport read_value, int32_t list_size, bool(&pile_list)[7]); + + /** + * @see serialize_list_organic_mat + */ + void serialize_list_other_mats(const std::map other_mats, FuncWriteExport add_value, std::vector list); + + /** + * @see serialize_list_organic_mat + */ + void unserialize_list_other_mats(const std::map other_mats, FuncReadImport read_value, int32_t list_size, std::vector* pile_list); + + /** + * @see serialize_list_organic_mat + */ + void serialize_list_itemdef(FuncWriteExport add_value, std::vector list, std::vector items, df::enums::item_type::item_type type); + + /** + * @see serialize_list_organic_mat + */ + void unserialize_list_itemdef(FuncReadImport read_value, int32_t list_size, std::vector* pile_list, df::enums::item_type::item_type type); + + /** + * Given a list of other_materials and an index, return its corresponding token + * @return empty string if not found + * @see other_mats_token + */ + std::string other_mats_index(const std::map other_mats, int idx); + + /** + * Given a list of other_materials and a token, return its corresponding index + * @return -1 if not found + * @see other_mats_index + */ + int other_mats_token(const std::map other_mats, const std::string& token); + + void write_general(); + void read_general(); + + void write_animals(); + void read_animals(); - bool mDebug; - std::ostream * mOut; - NullStream mNull; - df::building_stockpilest * mPile; - dfstockpiles::StockpileSettings mBuffer; - std::map mOtherMatsFurniture; - std::map mOtherMatsFinishedGoods; - std::map mOtherMatsBars; - std::map mOtherMatsBlocks; - std::map mOtherMatsWeaponsArmor; - - - std::ostream & debug(); - - /** - read memory structures and serialize to protobuf - */ - void write(); - - // parse serialized data into ui indices - void read (); - - /** - * Find an enum's value based off the string label. - * @param traits the enum's trait struct - * @param token the string value in key_table - * @return the enum's value, -1 if not found - */ - template - static typename df::enum_traits::base_type linear_index ( std::ostream & out, df::enum_traits traits, const std::string &token ) - { - auto j = traits.first_item_value; - auto limit = traits.last_item_value; - // sometimes enums start at -1, which is bad news for array indexing - if ( j < 0 ) - { - j += abs ( traits.first_item_value ); - limit += abs ( traits.first_item_value ); - } - for ( ; j <= limit; ++j ) - { - // out << " linear_index("<< token <<") = table["< FuncReadImport; - // add the token to the serialized list during export - typedef std::function FuncWriteExport; - // are item's of item_type allowed? - typedef std::function FuncItemAllowed; - // is this material allowed? - typedef std::function FuncMaterialAllowed; - - // convenient struct for parsing food stockpile items - struct food_pair - { - // exporting - FuncWriteExport set_value; - std::vector * stockpile_values; - // importing - FuncReadImport get_value; - size_t serialized_count; - bool valid; - - food_pair ( FuncWriteExport s, std::vector* sp_v, FuncReadImport g, size_t count ) - : set_value ( s ) - , stockpile_values ( sp_v ) - , get_value ( g ) - , serialized_count ( count ) - , valid ( true ) - {} - food_pair(): valid( false ) {} - }; - - /** - * There are many repeated (un)serialization cases throughout the stockpile_settings structure, - * so the most common cases have been generalized into generic functions using lambdas. - * - * The basic process to serialize a stockpile_settings structure is: - * 1. loop through the list - * 2. for every element that is TRUE: - * 3. map the specific stockpile_settings index into a general material, creature, etc index - * 4. verify that type is allowed in the list (e.g., no stone in gems stockpiles) - * 5. add it to the protobuf using FuncWriteExport - * - * The unserialization process is the same in reverse. - */ - void serialize_list_organic_mat ( FuncWriteExport add_value, const std::vector * list, df::enums::organic_mat_category::organic_mat_category cat ); - - /** - * @see serialize_list_organic_mat - */ - void unserialize_list_organic_mat ( FuncReadImport get_value, size_t list_size, std::vector *pile_list, df::enums::organic_mat_category::organic_mat_category cat ); - - - /** - * @see serialize_list_organic_mat - */ - void serialize_list_item_type ( FuncItemAllowed is_allowed, FuncWriteExport add_value, const std::vector &list ); - - - /** - * @see serialize_list_organic_mat - */ - void unserialize_list_item_type ( FuncItemAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector *pile_list ); - - - /** - * @see serialize_list_organic_mat - */ - void serialize_list_material ( FuncMaterialAllowed is_allowed, FuncWriteExport add_value, const std::vector &list ); - - /** - * @see serialize_list_organic_mat - */ - void unserialize_list_material ( FuncMaterialAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector *pile_list ); - - /** - * @see serialize_list_organic_mat - */ - void serialize_list_quality ( FuncWriteExport add_value, const bool ( &quality_list ) [7] ); - - /** - * Set all values in a bool[7] to false - */ - void quality_clear ( bool ( &pile_list ) [7] ); - + food_pair food_map(df::enums::organic_mat_category::organic_mat_category cat); + + void write_food(); + void read_food(); - /** - * @see serialize_list_organic_mat - */ - void unserialize_list_quality ( FuncReadImport read_value, int32_t list_size, bool ( &pile_list ) [7] ); + void furniture_setup_other_mats(); + void write_furniture(); + bool furniture_mat_is_allowed(const DFHack::MaterialInfo& mi); + void read_furniture(); + bool refuse_creature_is_allowed(const df::creature_raw* raw); - /** - * @see serialize_list_organic_mat - */ - void serialize_list_other_mats ( const std::map other_mats, FuncWriteExport add_value, std::vector list ); + void refuse_write_helper(std::function add_value, const std::vector& list); - /** - * @see serialize_list_organic_mat - */ - void unserialize_list_other_mats ( const std::map other_mats, FuncReadImport read_value, int32_t list_size, std::vector *pile_list ); + bool refuse_type_is_allowed(df::enums::item_type::item_type type); + void write_refuse(); + void refuse_read_helper(std::function get_value, size_t list_size, std::vector* pile_list); - /** - * @see serialize_list_organic_mat - */ - void serialize_list_itemdef ( FuncWriteExport add_value, std::vector list, std::vector items, df::enums::item_type::item_type type ); + void read_refuse(); + bool stone_is_allowed(const DFHack::MaterialInfo& mi); - /** - * @see serialize_list_organic_mat - */ - void unserialize_list_itemdef ( FuncReadImport read_value, int32_t list_size, std::vector *pile_list, df::enums::item_type::item_type type ); + void write_stone(); + void read_stone(); - /** - * Given a list of other_materials and an index, return its corresponding token - * @return empty string if not found - * @see other_mats_token - */ - std::string other_mats_index ( const std::map other_mats, int idx ); + bool ammo_mat_is_allowed(const DFHack::MaterialInfo& mi); - /** - * Given a list of other_materials and a token, return its corresponding index - * @return -1 if not found - * @see other_mats_index - */ - int other_mats_token ( const std::map other_mats, const std::string & token ); + void write_ammo(); + void read_ammo(); + bool coins_mat_is_allowed(const DFHack::MaterialInfo& mi); - void write_general(); - void read_general(); + void write_coins(); + void read_coins(); - void write_animals(); - void read_animals(); + void bars_blocks_setup_other_mats(); + bool bars_mat_is_allowed(const DFHack::MaterialInfo& mi); - food_pair food_map ( df::enums::organic_mat_category::organic_mat_category cat ); + bool blocks_mat_is_allowed(const DFHack::MaterialInfo& mi); + void write_bars_blocks(); + void read_bars_blocks(); + bool gem_mat_is_allowed(const DFHack::MaterialInfo& mi); + bool gem_cut_mat_is_allowed(const DFHack::MaterialInfo& mi); + bool gem_other_mat_is_allowed(DFHack::MaterialInfo& mi); - void write_food(); - void read_food(); + void write_gems(); - void furniture_setup_other_mats(); - void write_furniture(); - bool furniture_mat_is_allowed ( const DFHack::MaterialInfo &mi ); - void read_furniture(); + void read_gems(); - bool refuse_creature_is_allowed ( const df::creature_raw *raw ); - - - void refuse_write_helper ( std::function add_value, const std::vector & list ); - - - bool refuse_type_is_allowed ( df::enums::item_type::item_type type ); - - - void write_refuse(); - void refuse_read_helper ( std::function get_value, size_t list_size, std::vector* pile_list ); - - void read_refuse(); - - bool stone_is_allowed ( const DFHack::MaterialInfo &mi ); - - void write_stone(); - - void read_stone(); - - bool ammo_mat_is_allowed ( const DFHack::MaterialInfo &mi ); - - void write_ammo(); - void read_ammo(); - bool coins_mat_is_allowed ( const DFHack::MaterialInfo &mi ); - - void write_coins(); - - void read_coins(); - - void bars_blocks_setup_other_mats(); - - bool bars_mat_is_allowed ( const DFHack::MaterialInfo &mi ); - - bool blocks_mat_is_allowed ( const DFHack::MaterialInfo &mi ); - - void write_bars_blocks(); - void read_bars_blocks(); - bool gem_mat_is_allowed ( const DFHack::MaterialInfo &mi ); - bool gem_cut_mat_is_allowed ( const DFHack::MaterialInfo &mi ); - bool gem_other_mat_is_allowed(DFHack::MaterialInfo &mi ); - - void write_gems(); - - void read_gems(); - - bool finished_goods_type_is_allowed ( df::enums::item_type::item_type type ); - void finished_goods_setup_other_mats(); - bool finished_goods_mat_is_allowed ( const DFHack::MaterialInfo &mi ); - void write_finished_goods(); - void read_finished_goods(); - void write_leather(); - void read_leather(); - void write_cloth(); + bool finished_goods_type_is_allowed(df::enums::item_type::item_type type); + void finished_goods_setup_other_mats(); + bool finished_goods_mat_is_allowed(const DFHack::MaterialInfo& mi); + void write_finished_goods(); + void read_finished_goods(); + void write_leather(); + void read_leather(); + void write_cloth(); void read_cloth(); - bool wood_mat_is_allowed ( const df::plant_raw * plant ); + bool wood_mat_is_allowed(const df::plant_raw* plant); void write_wood(); void read_wood(); - bool weapons_mat_is_allowed ( const DFHack::MaterialInfo &mi ); + bool weapons_mat_is_allowed(const DFHack::MaterialInfo& mi); void write_weapons(); void read_weapons(); void weapons_armor_setup_other_mats(); - bool armor_mat_is_allowed ( const DFHack::MaterialInfo &mi ); + bool armor_mat_is_allowed(const DFHack::MaterialInfo& mi); void write_armor(); void read_armor(); - }; diff --git a/plugins/stockpiles/StockpileUtils.h b/plugins/stockpiles/StockpileUtils.h index f5570be92..7dcd8c3a2 100644 --- a/plugins/stockpiles/StockpileUtils.h +++ b/plugins/stockpiles/StockpileUtils.h @@ -3,26 +3,16 @@ #include "MiscUtils.h" #include "df/world.h" -#include "df/world_data.h" #include "df/creature_raw.h" #include "df/plant_raw.h" -#include -#include -#include - -// os -#include - // Utility Functions {{{ // A set of convenience functions for doing common lookups - /** * Retrieve creature raw from index */ -static inline df::creature_raw* find_creature ( int32_t idx ) -{ +static inline df::creature_raw* find_creature(int32_t idx) { return df::global::world->raws.creatures.all[idx]; } @@ -30,16 +20,14 @@ static inline df::creature_raw* find_creature ( int32_t idx ) * Retrieve creature index from id string * @return -1 if not found */ -static inline int16_t find_creature ( const std::string &creature_id ) -{ - return linear_index ( df::global::world->raws.creatures.all, &df::creature_raw::creature_id, creature_id ); +static inline int16_t find_creature(const std::string& creature_id) { + return linear_index(df::global::world->raws.creatures.all, &df::creature_raw::creature_id, creature_id); } /** * Retrieve plant raw from index */ -static inline df::plant_raw* find_plant ( size_t idx ) -{ +static inline df::plant_raw* find_plant(size_t idx) { return df::global::world->raws.plants.all[idx]; } @@ -47,32 +35,26 @@ static inline df::plant_raw* find_plant ( size_t idx ) * Retrieve plant index from id string * @return -1 if not found */ -static inline size_t find_plant ( const std::string &plant_id ) -{ - return linear_index ( df::global::world->raws.plants.all, &df::plant_raw::id, plant_id ); +static inline size_t find_plant(const std::string& plant_id) { + return linear_index(df::global::world->raws.plants.all, &df::plant_raw::id, plant_id); } -struct less_than_no_case -{ - bool operator () (char x, char y) const - { - return toupper( static_cast< unsigned char >(x)) < toupper( static_cast< unsigned char >(y)); - } +struct less_than_no_case { + bool operator () (char x, char y) const { + return toupper(static_cast(x)) < toupper(static_cast(y)); + } }; -static inline bool CompareNoCase(const std::string &a, const std::string &b) -{ - return std::lexicographical_compare( a.begin(),a.end(), b.begin(),b.end(), less_than_no_case() ); +static inline bool CompareNoCase(const std::string& a, const std::string& b) { + return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end(), less_than_no_case()); } - /** * Checks if the parameter has the dfstock extension. * Doesn't check if the file exists or not. */ -static inline bool is_dfstockfile ( const std::string& filename ) -{ - return filename.rfind ( ".dfstock" ) != std::string::npos; +static inline bool is_dfstockfile(const std::string& filename) { + return filename.rfind(".dfstock") != std::string::npos; } // }}} utility Functions diff --git a/plugins/stockpiles/stockpiles.cpp b/plugins/stockpiles/stockpiles.cpp index 4a2bdc33c..98c94ace0 100644 --- a/plugins/stockpiles/stockpiles.cpp +++ b/plugins/stockpiles/stockpiles.cpp @@ -1,3 +1,4 @@ +#include "Debug.h" #include "PluginManager.h" #include "StockpileUtils.h" #include "StockpileSerializer.h" @@ -12,11 +13,16 @@ using namespace DFHack; DFHACK_PLUGIN("stockpiles"); -static command_result savestock ( color_ostream &out, vector & parameters ); -static command_result loadstock ( color_ostream &out, vector & parameters ); +REQUIRE_GLOBAL(world); -DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands ) -{ +namespace DFHack { + DBG_DECLARE(stockpiles, log, DebugCategory::LINFO); +} + +static command_result savestock(color_ostream& out, vector & parameters); +static command_result loadstock(color_ostream& out, vector & parameters); + +DFhackCExport command_result plugin_init(color_ostream& out, std::vector & commands) { commands.push_back(PluginCommand( "savestock", "Save the active stockpile's settings to a file.", @@ -31,61 +37,46 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector & parameters ) -{ - df::building_stockpilest *sp = Gui::getSelectedStockpile(out, true); - if ( !sp ) - { - out.printerr ( "Selected building isn't a stockpile.\n" ); +static command_result savestock(color_ostream& out, vector & parameters) { + df::building_stockpilest* sp = Gui::getSelectedStockpile(out, true); + if (!sp) { + out.printerr("Selected building isn't a stockpile.\n"); return CR_WRONG_USAGE; } - if ( parameters.size() > 2 ) - { - out.printerr ( "Invalid parameters\n" ); + if (parameters.size() > 2) { + out.printerr("Invalid parameters\n"); return CR_WRONG_USAGE; } - bool debug = false; std::string file; - for ( size_t i = 0; i < parameters.size(); ++i ) - { - const std::string o = parameters.at ( i ); - if ( o == "--debug" || o == "-d" ) - debug = true; - else if ( !o.empty() && o[0] != '-' ) - { + for (size_t i = 0; i < parameters.size(); ++i) { + const std::string o = parameters.at(i); + if (!o.empty() && o[0] != '-') { file = o; } } - if ( file.empty() ) - { - out.printerr ( "You must supply a valid filename.\n" ); + if (file.empty()) { + out.printerr("You must supply a valid filename.\n"); return CR_WRONG_USAGE; } - StockpileSerializer cereal ( sp ); - if ( debug ) - cereal.enable_debug ( out ); + StockpileSerializer cereal(sp); - if ( !is_dfstockfile ( file ) ) file += ".dfstock"; - try - { - if ( !cereal.serialize_to_file ( file ) ) - { - out.printerr ( "could not save to %s\n", file.c_str() ); + if (!is_dfstockfile(file)) file += ".dfstock"; + try { + if (!cereal.serialize_to_file(file)) { + out.printerr("could not save to %s\n", file.c_str()); return CR_FAILURE; } } - catch ( std::exception &e ) - { - out.printerr ( "serialization failed: protobuf exception: %s\n", e.what() ); + catch (std::exception& e) { + out.printerr("serialization failed: protobuf exception: %s\n", e.what()); return CR_FAILURE; } @@ -94,59 +85,45 @@ static command_result savestock ( color_ostream &out, vector & paramete // importing -static command_result loadstock ( color_ostream &out, vector & parameters ) -{ - df::building_stockpilest *sp = Gui::getSelectedStockpile(out, true); - if ( !sp ) - { - out.printerr ( "Selected building isn't a stockpile.\n" ); +static command_result loadstock(color_ostream& out, vector & parameters) { + df::building_stockpilest* sp = Gui::getSelectedStockpile(out, true); + if (!sp) { + out.printerr("Selected building isn't a stockpile.\n"); return CR_WRONG_USAGE; } - if ( parameters.size() < 1 || parameters.size() > 2 ) - { - out.printerr ( "Invalid parameters\n" ); + if (parameters.size() < 1 || parameters.size() > 2) { + out.printerr("Invalid parameters\n"); return CR_WRONG_USAGE; } - bool debug = false; std::string file; - for ( size_t i = 0; i < parameters.size(); ++i ) - { - const std::string o = parameters.at ( i ); - if ( o == "--debug" || o == "-d" ) - debug = true; - else if ( !o.empty() && o[0] != '-' ) - { + for (size_t i = 0; i < parameters.size(); ++i) { + const std::string o = parameters.at(i); + if (!o.empty() && o[0] != '-') { file = o; } } - if ( file.empty() ) { - out.printerr ( "ERROR: missing .dfstock file parameter\n"); + if (file.empty()) { + out.printerr("ERROR: missing .dfstock file parameter\n"); return DFHack::CR_WRONG_USAGE; } - if ( !is_dfstockfile ( file ) ) + if (!is_dfstockfile(file)) file += ".dfstock"; - if ( !Filesystem::exists ( file ) ) - { - out.printerr ( "ERROR: the .dfstock file doesn't exist: %s\n", file.c_str()); + if (!Filesystem::exists(file)) { + out.printerr("ERROR: the .dfstock file doesn't exist: %s\n", file.c_str()); return CR_WRONG_USAGE; } - StockpileSerializer cereal ( sp ); - if ( debug ) - cereal.enable_debug ( out ); - try - { - if ( !cereal.unserialize_from_file ( file ) ) - { - out.printerr ( "unserialization failed: %s\n", file.c_str() ); + StockpileSerializer cereal(sp); + try { + if (!cereal.unserialize_from_file(file)) { + out.printerr("unserialization failed: %s\n", file.c_str()); return CR_FAILURE; } } - catch ( std::exception &e ) - { - out.printerr ( "unserialization failed: protobuf exception: %s\n", e.what() ); + catch (std::exception& e) { + out.printerr("unserialization failed: protobuf exception: %s\n", e.what()); return CR_FAILURE; } return CR_OK; From 0f31b9897dbee898253d4cc94fc4ec4156b0be4e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 10 Mar 2023 16:22:02 -0800 Subject: [PATCH 28/28] slight editing for the quickstart guide --- docs/Quickstart.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/Quickstart.rst b/docs/Quickstart.rst index 55cfe6f36..065ba6ecc 100644 --- a/docs/Quickstart.rst +++ b/docs/Quickstart.rst @@ -5,19 +5,19 @@ Quickstart guide Welcome to DFHack! This guide will help get you oriented with the DFHack system and teach you how to find and use the tools productively. If you're reading this -in `quickstart-guide`, hit the right arrow key or click on the hotkey hint in -the lower right corner of the window to go to the next page. +in the in-game `quickstart-guide` reader, hit the right arrow key or click on +the hotkey hint in the lower right corner of the window to go to the next page. What is DFHack? --------------- -DFHack is a framework for Dwarf Fortress that provides a unified, cross-platform -environment that enables mods and tools to significantly extend the game. The -default DFHack distribution contains a wide variety of tools, including bugfixes, -interface improvements, automation agents, design blueprints, modding building -blocks, and more. Third-party tools (e.g. mods downloaded from Steam Workshop or -the forums) can also seamlessly integrate with the DFHack framework and extend -the game far beyond what can be done by just modding the raws. +DFHack is an add-on for Dwarf Fortress that enables mods and tools to +significantly extend the game. The default DFHack distribution contains a wide +variety of tools, including bugfixes, interface improvements, automation agents, +design blueprints, modding building blocks, and more. Third-party tools (e.g. +mods downloaded from Steam Workshop or the forums) can also seamlessly integrate +with the DFHack framework and extend the game far beyond what can be done by +just modding the raws. DFHack's mission is to provide tools and interfaces for players and modders to: