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