From 7e78d8802eebfe3ec1d0d505106a1c96dacfc1dd Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 22 Oct 2020 21:37:49 -0700 Subject: [PATCH 1/2] migrate qf's buildings_use_blocks to buildingplan - remove buildings_use_blocks setting from quickfort config file - add a new Buildingplan Global Settings dialog to house global settings - move Quickfort Mode (for legacy Python Quickfort) into that dialog - add four settings to control how generic building materials are matched: - blocks - boulders - logs - bars - ajust the buildingplan algorithm to register duplicate tasks for building material item filters, one for each type. since we track how many items we've matched for a filter, the first matched item will "win" and the extras will get detected as invalid and popped off the queue. - ensure boulders, logs, and bars are scanned last, and in that order - more global settings planned for the future! see http://www.bay12forums.com/smf/index.php?topic=176889.msg8202679#msg8202679 --- dfhack-config/quickfort/quickfort.txt | 13 +- docs/changelog.txt | 6 +- plugins/buildingplan-planner.cpp | 282 ++++++++++++++++++-------- plugins/buildingplan-planner.h | 6 + plugins/buildingplan.cpp | 65 ++++-- plugins/lua/buildingplan.lua | 111 ++++++++++ 6 files changed, 377 insertions(+), 106 deletions(-) diff --git a/dfhack-config/quickfort/quickfort.txt b/dfhack-config/quickfort/quickfort.txt index c1d70201f..3a57bfbaf 100644 --- a/dfhack-config/quickfort/quickfort.txt +++ b/dfhack-config/quickfort/quickfort.txt @@ -5,19 +5,16 @@ # # If you have edited this file but want to revert to "factory defaults", delete # this file and a fresh one will be copied from -# dfhack-config/default/quickfort/qickfort.txt the next time you start DFHack. +# dfhack-config/default/quickfort/quickfort.txt the next time you start DFHack. # Directory tree to search for blueprints. Can be set to an absolute or relative # path. If set to a relative path, resolves to a directory under the DF folder. +# Note that if you change this directory, you will not automatically pick up +# blueprints written by the DFHack "blueprint" plugin (which always writes to +# the "blueprints" dir). blueprints_dir=blueprints -# Force all blueprint buildings that could be built with any building material -# to only use blocks. The prevents logs, boulders, and bars (e.g. potash and -# coal) from being wasted on constructions. If set to false, buildings will be -# built with any available building material. -buildings_use_blocks=true - -# Set to "true" or "false". If true, will designate dig blueprints in marker +# Set to "true" or "false". If true, will designate all dig blueprints in marker # mode. If false, only cells with dig codes explicitly prefixed with an "m" will # be designated in marker mode. force_marker_mode=false diff --git a/docs/changelog.txt b/docs/changelog.txt index dbe296c85..b26984d53 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,9 +38,11 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `dwarfmonitor`: fixed a crash when opening the ``prefs`` screen if units have vague preferences ## Misc Improvements -- `buildingplan`: all buildings, furniture, and constructions are now supported (except for the few building types not supported by dfhack itself) -- `buildingplan`: now respects building job_item filters when matching items +- `buildingplan`: all buildings, furniture, and constructions are now supported (except for instruments) +- `buildingplan`: now respects building job_item filters when matching items, so you can set your own programmatic filters for buildings before submitting them to buildingplan - `buildingplan`: default filter setting for max quality changed from ``artifact`` to ``masterwork`` +- `buildingplan`: new global settings page accessible via ``G`` hotkey when on any building build screen; ``Quickfort Mode`` toggle for legacy Python Quickfort has been moved to this page +- `buildingplan`: new global settings for whether generic building materials should match blocks, boulders, logs, and/or bars. defaults are everything but bars. ## API - `buildingplan`: added Lua interface API diff --git a/plugins/buildingplan-planner.cpp b/plugins/buildingplan-planner.cpp index 0a714854c..42c84f4dc 100644 --- a/plugins/buildingplan-planner.cpp +++ b/plugins/buildingplan-planner.cpp @@ -501,6 +501,33 @@ void migrateV1ToV2() } } +static void init_global_settings(std::map & settings) +{ + settings.clear(); + settings["blocks"] = true; + settings["boulders"] = true; + settings["logs"] = true; + settings["bars"] = false; +} + +const std::map & Planner::getGlobalSettings() const +{ + return global_settings; +} + +bool Planner::setGlobalSetting(std::string name, bool value) +{ + if (global_settings.count(name) == 0) + { + debug("attempted to set invalid setting: '%s'", name.c_str()); + return false; + } + debug("global setting '%s' %d -> %d", + name.c_str(), global_settings[name], value); + global_settings[name] = value; + return true; +} + void Planner::reset() { debug("resetting Planner state"); @@ -508,6 +535,8 @@ void Planner::reset() planned_buildings.clear(); tasks.clear(); + init_global_settings(global_settings); + migrateV1ToV2(); std::vector items; @@ -584,6 +613,41 @@ static std::string getBucket(const df::job_item & ji, return ser.str(); } +// get a list of item vectors that we should search for matches +static std::vector getVectorIds(df::job_item *job_item, + const std::map & global_settings) +{ + std::vector ret; + + // if the filter already has the vector_id set to something specific, use it + if (job_item->vector_id > df::job_item_vector_id::IN_PLAY) + { + debug("using vector_id from job_item: %s", + ENUM_KEY_STR(job_item_vector_id, job_item->vector_id).c_str()); + ret.push_back(job_item->vector_id); + return ret; + } + + // if the filer is for building material, refer to our global settings for + // which vectors to search + if (job_item->flags2.bits.building_material) + { + if (global_settings.at("blocks")) + ret.push_back(df::job_item_vector_id::BLOCKS); + if (global_settings.at("boulders")) + ret.push_back(df::job_item_vector_id::BOULDER); + if (global_settings.at("logs")) + ret.push_back(df::job_item_vector_id::WOOD); + if (global_settings.at("bars")) + ret.push_back(df::job_item_vector_id::BAR); + } + + // fall back to IN_PLAY if no other vector was appropriate + if (ret.empty()) + ret.push_back(df::job_item_vector_id::IN_PLAY); + return ret; +} + bool Planner::registerTasks(PlannedBuilding & pb) { df::building * bld = pb.getBuilding(); @@ -599,22 +663,27 @@ bool Planner::registerTasks(PlannedBuilding & pb) debug("unexpected number of job items: want >0, got %d", num_job_items); return false; } + int32_t id = bld->id; for (int job_item_idx = 0; job_item_idx < num_job_items; ++job_item_idx) { - auto vector_id = df::job_item_vector_id::IN_PLAY; auto job_item = job_items[job_item_idx]; - if (job_item->vector_id) - vector_id = job_item->vector_id; auto bucket = getBucket(*job_item, pb.getFilters()); - for (int item_num = 0; item_num < job_item->quantity; ++item_num) + auto vector_ids = getVectorIds(job_item, global_settings); + + // if there are multiple vector_ids, schedule duplicate tasks. after + // the correct number of items are matched, the extras will get popped + // as invalid + for (auto vector_id : vector_ids) { - int32_t id = bld->id; - tasks[vector_id][bucket].push(std::make_pair(id, job_item_idx)); - debug("added task: %s/%s/%d,%d; " - "%zu vector(s), %zu filter bucket(s), %zu task(s) in bucket", - ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), - bucket.c_str(), id, job_item_idx, tasks.size(), - tasks[vector_id].size(), tasks[vector_id][bucket].size()); + for (int item_num = 0; item_num < job_item->quantity; ++item_num) + { + tasks[vector_id][bucket].push(std::make_pair(id, job_item_idx)); + debug("added task: %s/%s/%d,%d; " + "%zu vector(s), %zu filter bucket(s), %zu task(s) in bucket", + ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), + bucket.c_str(), id, job_item_idx, tasks.size(), + tasks[vector_id].size(), tasks[vector_id][bucket].size()); + } } } return true; @@ -794,99 +863,144 @@ void Planner::popInvalidTasks(std::queue> & task_queue) } } -void Planner::doCycle() +void Planner::doVector(df::job_item_vector_id vector_id, + std::map>> & buckets) { - debug("running cycle for %zu registered buildings", - planned_buildings.size()); - for (auto it = tasks.begin(); it != tasks.end();) + auto other_id = ENUM_ATTR(job_item_vector_id, other, vector_id); + auto item_vector = df::global::world->items.other[other_id]; + debug("matching %zu item(s) in vector %s against %zu filter bucket(s)", + item_vector.size(), + ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), + buckets.size()); + for (auto item_it = item_vector.rbegin(); + item_it != item_vector.rend(); + ++item_it) { - auto & buckets = it->second; - auto other_id = ENUM_ATTR(job_item_vector_id, other, it->first); - auto item_vector = df::global::world->items.other[other_id]; - debug("matching %zu item(s) in vector %s against %zu filter bucket(s)", - item_vector.size(), - ENUM_KEY_STR(job_item_vector_id, it->first).c_str(), - buckets.size()); - for (auto item_it = item_vector.rbegin(); - item_it != item_vector.rend(); - ++item_it) + auto item = *item_it; + if (!itemPassesScreen(item)) + continue; + for (auto bucket_it = buckets.begin(); bucket_it != buckets.end();) { - auto item = *item_it; - if (!itemPassesScreen(item)) + auto & task_queue = bucket_it->second; + popInvalidTasks(task_queue); + if (task_queue.empty()) + { + debug("removing empty bucket: %s/%s; %zu bucket(s) left", + ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), + bucket_it->first.c_str(), + buckets.size() - 1); + bucket_it = buckets.erase(bucket_it); continue; - for (auto bucket_it = buckets.begin(); bucket_it != buckets.end();) + } + auto & task = task_queue.front(); + auto id = task.first; + auto & pb = planned_buildings.at(id); + auto building = pb.getBuilding(); + auto job = building->jobs[0]; + auto filter_idx = task.second; + if (matchesFilters(item, job->job_items[filter_idx], + pb.getFilters()[filter_idx]) + && DFHack::Job::attachJobItem(job, item, + df::job_item_ref::Hauled, filter_idx)) { - auto & task_queue = bucket_it->second; - popInvalidTasks(task_queue); - if (task_queue.empty()) + MaterialInfo material; + material.decode(item); + ItemTypeInfo item_type; + item_type.decode(item); + debug("attached %s %s to filter %d for %s(%d): %s/%s", + material.toString().c_str(), + item_type.toString().c_str(), + filter_idx, + ENUM_KEY_STR(building_type, building->getType()).c_str(), + id, + ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), + bucket_it->first.c_str()); + // keep quantity aligned with the actual number of remaining + // items so if buildingplan is turned off, the building will + // be completed with the correct number of items. + --job->job_items[filter_idx]->quantity; + task_queue.pop(); + if (isJobReady(job)) { - debug("removing empty bucket: %s/%s; %zu bucket(s) left", - ENUM_KEY_STR(job_item_vector_id, it->first).c_str(), - bucket_it->first.c_str(), - buckets.size() - 1); - bucket_it = buckets.erase(bucket_it); - continue; + finalizeBuilding(building); + unregisterBuilding(id); } - auto & task = task_queue.front(); - auto id = task.first; - auto & pb = planned_buildings.at(id); - auto building = pb.getBuilding(); - auto job = building->jobs[0]; - auto filter_idx = task.second; - if (matchesFilters(item, job->job_items[filter_idx], - pb.getFilters()[filter_idx]) - && DFHack::Job::attachJobItem(job, item, - df::job_item_ref::Hauled, filter_idx)) + if (task_queue.empty()) { - MaterialInfo material; - material.decode(item); - ItemTypeInfo item_type; - item_type.decode(item); - debug("attached %s %s to filter %d for %s(%d): %s/%s", - material.toString().c_str(), - item_type.toString().c_str(), - filter_idx, - ENUM_KEY_STR(building_type, building->getType()).c_str(), - id, - ENUM_KEY_STR(job_item_vector_id, it->first).c_str(), - bucket_it->first.c_str()); - // keep quantity aligned with the actual number of remaining - // items so if buildingplan is turned off, the building will - // be completed with the correct number of items. - --job->job_items[filter_idx]->quantity; - task_queue.pop(); - if (isJobReady(job)) - { - finalizeBuilding(building); - unregisterBuilding(id); - } - if (task_queue.empty()) - { - debug( - "removing empty item bucket: %s/%s; %zu left", - ENUM_KEY_STR(job_item_vector_id, it->first).c_str(), - bucket_it->first.c_str(), - buckets.size() - 1); - buckets.erase(bucket_it); - } - // we found a home for this item; no need to look further - break; + debug( + "removing empty item bucket: %s/%s; %zu left", + ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), + bucket_it->first.c_str(), + buckets.size() - 1); + buckets.erase(bucket_it); } - ++bucket_it; - } - if (buckets.empty()) + // we found a home for this item; no need to look further break; + } + ++bucket_it; } + if (buckets.empty()) + break; + } +} + +struct VectorsToScanLast +{ + std::vector vectors; + VectorsToScanLast() + { + // order is important here. we want to match boulders before wood and + // everything before bars. blocks are not listed here since we'll have + // already scanned them when we did the first pass through the buckets. + vectors.push_back(df::job_item_vector_id::BOULDER); + vectors.push_back(df::job_item_vector_id::WOOD); + vectors.push_back(df::job_item_vector_id::BAR); + } +}; + +void Planner::doCycle() +{ + debug("running cycle for %zu registered building(s)", + planned_buildings.size()); + static const VectorsToScanLast vectors_to_scan_last; + for (auto it = tasks.begin(); it != tasks.end();) + { + auto vector_id = it->first; + // we could make this a set, but it's only three elements + if (std::find(vectors_to_scan_last.vectors.begin(), + vectors_to_scan_last.vectors.end(), + vector_id) != vectors_to_scan_last.vectors.end()) + { + ++it; + continue; + } + + auto & buckets = it->second; + doVector(vector_id, buckets); if (buckets.empty()) { debug("removing empty vector: %s; %zu vector(s) left", - ENUM_KEY_STR(job_item_vector_id, it->first).c_str(), + ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), tasks.size() - 1); it = tasks.erase(it); } else ++it; } + for (auto vector_id : vectors_to_scan_last.vectors) + { + if (tasks.count(vector_id) == 0) + continue; + auto & buckets = tasks[vector_id]; + doVector(vector_id, buckets); + if (buckets.empty()) + { + debug("removing empty vector: %s; %zu vector(s) left", + ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), + tasks.size() - 1); + tasks.erase(vector_id); + } + } debug("cycle done; %zu registered building(s) left", planned_buildings.size()); } diff --git a/plugins/buildingplan-planner.h b/plugins/buildingplan-planner.h index 22d0487c3..18cfaf0b1 100644 --- a/plugins/buildingplan-planner.h +++ b/plugins/buildingplan-planner.h @@ -104,6 +104,9 @@ public: std::vector &item_filters; }; + const std::map & getGlobalSettings() const; + bool setGlobalSetting(std::string name, bool value); + void reset(); void addPlannedBuilding(df::building *bld); @@ -117,6 +120,7 @@ public: void doCycle(); private: + std::map global_settings; std::unordered_map, BuildingTypeKeyHash> default_item_filters; @@ -128,6 +132,8 @@ private: bool registerTasks(PlannedBuilding &plannedBuilding); void unregisterBuilding(int32_t id); void popInvalidTasks(std::queue> &task_queue); + void doVector(df::job_item_vector_id vector_id, + std::map>> & buckets); }; extern Planner planner; diff --git a/plugins/buildingplan.cpp b/plugins/buildingplan.cpp index 58c911dcf..52bd5118f 100644 --- a/plugins/buildingplan.cpp +++ b/plugins/buildingplan.cpp @@ -18,7 +18,7 @@ DFHACK_PLUGIN("buildingplan"); #define PLUGIN_VERSION 2.0 REQUIRE_GLOBAL(ui); REQUIRE_GLOBAL(ui_build_selector); -REQUIRE_GLOBAL(world); +REQUIRE_GLOBAL(world); // used in buildingplan library #define MAX_MASK 10 #define MAX_MATERIAL 21 @@ -287,9 +287,9 @@ static bool is_planmode_enabled(BuildingTypeKey key) static std::string get_item_label(const BuildingTypeKey &key, int item_idx) { - auto L = Lua::Core::State; - color_ostream_proxy out(Core::getInstance().getConsole()); - Lua::StackUnwinder top(L); + auto L = Lua::Core::State; + color_ostream_proxy out(Core::getInstance().getConsole()); + Lua::StackUnwinder top(L); if (!lua_checkstack(L, 5) || !Lua::PushModulePublic( @@ -314,11 +314,11 @@ static std::string get_item_label(const BuildingTypeKey &key, int item_idx) static bool construct_planned_building() { - auto L = Lua::Core::State; - color_ostream_proxy out(Core::getInstance().getConsole()); + auto L = Lua::Core::State; + color_ostream_proxy out(Core::getInstance().getConsole()); - CoreSuspendClaimer suspend; - Lua::StackUnwinder top(L); + CoreSuspendClaimer suspend; + Lua::StackUnwinder top(L); if (!(lua_checkstack(L, 1) && Lua::PushModulePublic(out, L, "plugins.buildingplan", @@ -339,6 +339,36 @@ static bool construct_planned_building() return true; } +static void show_global_settings_dialog() +{ + auto L = Lua::Core::State; + color_ostream_proxy out(Core::getInstance().getConsole()); + Lua::StackUnwinder top(L); + + if (!lua_checkstack(L, 2) || + !Lua::PushModulePublic( + out, L, "plugins.buildingplan", "show_global_settings_dialog")) + { + debug("Failed to push the module"); + return; + } + + lua_newtable(L); + int ctable = lua_gettop(L); + Lua::SetField(L, quickfort_mode, ctable, "quickfort_mode"); + + for (auto & setting : planner.getGlobalSettings()) + { + Lua::SetField(L, setting.second, ctable, setting.first.c_str()); + } + + if (!Lua::SafeCall(out, L, 1, 0)) + { + debug("Failed call to show_global_settings_dialog"); + return; + } +} + struct buildingplan_query_hook : public df::viewscreen_dwarfmodest { typedef df::viewscreen_dwarfmodest interpose_base; @@ -522,7 +552,7 @@ struct buildingplan_place_hook : public df::viewscreen_dwarfmodest } if (input->count(interface_key::CUSTOM_P) || - input->count(interface_key::CUSTOM_F) || + input->count(interface_key::CUSTOM_G) || input->count(interface_key::CUSTOM_D) || input->count(interface_key::CUSTOM_M)) { @@ -536,9 +566,9 @@ struct buildingplan_place_hook : public df::viewscreen_dwarfmodest Gui::refreshSidebar(); return true; } - if (input->count(interface_key::CUSTOM_SHIFT_F)) + if (input->count(interface_key::CUSTOM_SHIFT_G)) { - quickfort_mode = !quickfort_mode; + show_global_settings_dialog(); return true; } @@ -648,7 +678,7 @@ struct buildingplan_place_hook : public df::viewscreen_dwarfmodest } OutputToggleString(x, y, "Planning Mode", "P", planmode_enabled[key], true, left_margin); - OutputToggleString(x, y, "Quickfort Mode", "F", quickfort_mode, true, left_margin); + OutputHotkeyString(x, y, "Global Settings", "G", true, left_margin); if (!is_planmode_enabled(key)) return; @@ -893,10 +923,21 @@ static void scheduleCycle() { cycle_requested = true; } +static void setSetting(std::string name, bool value) { + if (name == "quickfort_mode") + { + debug("setting quickfort_mode %d -> %d", quickfort_mode, value); + quickfort_mode = value; + return; + } + planner.setGlobalSetting(name, value); +} + DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_LUA_FUNCTION(isPlannableBuilding), DFHACK_LUA_FUNCTION(addPlannedBuilding), DFHACK_LUA_FUNCTION(doCycle), DFHACK_LUA_FUNCTION(scheduleCycle), + DFHACK_LUA_FUNCTION(setSetting), DFHACK_LUA_END }; diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 2de7db7d2..ac92082f3 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -4,6 +4,7 @@ local _ENV = mkmodule('plugins.buildingplan') Native functions: + * void setSetting(string name, boolean value) * bool isPlannableBuilding(df::building_type type, int16_t subtype, int32_t custom) * void addPlannedBuilding(df::building *bld) * void doCycle() @@ -11,6 +12,7 @@ local _ENV = mkmodule('plugins.buildingplan') --]] +local dialogs = require('gui.dialogs') local guidm = require('gui.dwarfmode') require('dfhack.buildings') @@ -92,4 +94,113 @@ function construct_building_from_ui_state() return bld end +-- +-- GlobalSettings dialog +-- + +local GlobalSettings = defclass(GlobalSettings, dialogs.MessageBox) +GlobalSettings.focus_path = 'buildingplan_globalsettings' + +GlobalSettings.ATTRS{ + settings = {} +} + +function GlobalSettings:onDismiss() + for k,v in pairs(self.settings) do + -- call back into C++ to save changes + setSetting(k, v) + end +end + +-- does not need the core suspended. +function show_global_settings_dialog(settings) + GlobalSettings{ + frame_title="Buildingplan Global Settings", + settings=settings, + }:show() +end + +function GlobalSettings:toggle_setting(name) + self.settings[name] = not self.settings[name] +end + +function GlobalSettings:get_setting_string(name) + if self.settings[name] then return 'On' end + return 'Off' +end + +function GlobalSettings:is_setting_enabled(name) + return self.settings[name] +end + +function GlobalSettings:make_setting_label_token(text, key, name, width) + return {text=text, key=key, key_sep=': ', key_pen=COLOR_GREEN, + on_activate=self:callback('toggle_setting', name), width=width} +end + +function GlobalSettings:make_setting_value_token(name) + return {text=self:callback('get_setting_string', name), + enabled=self:callback('is_setting_enabled', name), + pen=COLOR_YELLOW, dpen=COLOR_GRAY} +end + +-- mockup: +--[[ + Buildingplan Global Settings + + e: Enable all: Off + Enables buildingplan for all building types. Use this to avoid having to + manually enable buildingplan for each building type that you want to plan. + Note that DFHack quickfort will use buildingplan to manage buildings + regardless of whether buildingplan is "enabled" for the building type. + + Allowed types for generic, fire-safe, and magma-safe building material: + b: Blocks: On + s: Boulders: On + w: Wood: On + r: Bars: Off + Changes to these settings will be applied to newly-planned buildings. + + A: Apply building material filter settings to existing planned buildings + Use this if your planned buildings can't be completed because the settings + above were too restrictive when the buildings were originally planned. + + M: Edit list of materials to avoid + potash + pearlash + ash + coal + Buildingplan will avoid using these material types when a planned building's + material filter is set to 'any'. They can stil be matched when they are + explicitly allowed by a planned building's material filter. Changes to this + list take effect for existing buildings immediately. + + g: Allow bags: Off + This allows bags to be placed where a 'coffer' is planned. + + f: Legacy Quickfort Mode: Off + Compatibility mode for the legacy Python-based Quickfort application. This + setting is not needed for DFHack quickfort. +--]] +function GlobalSettings:init() + self.subviews.label:setText{ + 'Allowed types for generic, fire-safe, and magma-safe building material:\n', + self:make_setting_label_token('Blocks', 'CUSTOM_B', 'blocks', 10), + self:make_setting_value_token('blocks'), '\n', + self:make_setting_label_token('Boulders', 'CUSTOM_S', 'boulders', 10), + self:make_setting_value_token('boulders'), '\n', + self:make_setting_label_token('Wood', 'CUSTOM_W', 'logs', 10), + self:make_setting_value_token('logs'), '\n', + self:make_setting_label_token('Bars', 'CUSTOM_R', 'bars', 10), + self:make_setting_value_token('bars'), '\n', + ' Changes to these settings will be applied to newly-planned buildings.\n', + '\n', + self:make_setting_label_token('Legacy Quickfort Mode', 'CUSTOM_F', + 'quickfort_mode', 23), + self:make_setting_value_token('quickfort_mode'), '\n', + ' Compatibility mode for the legacy Python-based Quickfort application.\n', + ' This setting is not needed for DFHack quickfort.' + } +end + return _ENV From c1af3e281719933148f1a8570ebc6c272bebfc79 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 26 Oct 2020 21:13:11 -0700 Subject: [PATCH 2/2] document what happens if no types are allowed --- plugins/lua/buildingplan.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index ac92082f3..aa5a71eba 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -194,6 +194,7 @@ function GlobalSettings:init() self:make_setting_label_token('Bars', 'CUSTOM_R', 'bars', 10), self:make_setting_value_token('bars'), '\n', ' Changes to these settings will be applied to newly-planned buildings.\n', + ' If no types are enabled above, then any building material is allowed.\n', '\n', self:make_setting_label_token('Legacy Quickfort Mode', 'CUSTOM_F', 'quickfort_mode', 23),