From 25600e45b092c04a177c4f72a60571e3992c0059 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 6 Oct 2023 18:25:24 -0700 Subject: [PATCH] support melting masterworks in logistics --- docs/changelog.txt | 1 + docs/plugins/logistics.rst | 4 ++ docs/plugins/stockpiles.rst | 8 +++- plugins/logistics.cpp | 29 ++++++++---- plugins/lua/logistics.lua | 17 +++++-- plugins/lua/stockpiles.lua | 89 +++++++++++++++++++++++++++++++++---- 6 files changed, 128 insertions(+), 20 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index ee608652c..1a97e637d 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -56,6 +56,7 @@ Template for new versions: - `preserve-tombs`: tracks tomb assignments to living units and ensures that the tomb stays assigned to them when they die. ## New Features +- `logistics`: ``automelt`` now optionally supports melting masterworks; feature accessible from `stockpiles` overlay ## Fixes diff --git a/docs/plugins/logistics.rst b/docs/plugins/logistics.rst index a65571484..d9aeee067 100644 --- a/docs/plugins/logistics.rst +++ b/docs/plugins/logistics.rst @@ -72,3 +72,7 @@ Options Causes the command to act upon stockpiles with the given names or numbers instead of the stockpile that is currently selected in the UI. Note that the numbers are the stockpile numbers, not the building ids. +``-m``, ``--melt-masterworks`` + If specified with a ``logistics add melt`` command, will configure the + stockpile to allow melting of masterworks. By default, masterworks are not + marked for melting, even if they are in an automelt stockpile. diff --git a/docs/plugins/stockpiles.rst b/docs/plugins/stockpiles.rst index 249a27d13..7d4ec050b 100644 --- a/docs/plugins/stockpiles.rst +++ b/docs/plugins/stockpiles.rst @@ -100,7 +100,13 @@ Overlay This plugin provides a panel that appears when you select a stockpile via an `overlay` widget. You can use it to easily toggle `logistics` plugin features -like autotrade, automelt, or autotrain. +like autotrade, automelt, or autotrain. There are also buttons along the top frame for: + +- minimizing the panel (if it is in the way of the vanilla stockpile + configuration widgets) +- showing help for the overlay widget in `gui/launcher` (this page) +- configuring advanced settings for the stockpile, such as whether automelt + will melt masterworks .. _stockpiles-library: diff --git a/plugins/logistics.cpp b/plugins/logistics.cpp index 86f65d351..d4d2f0afe 100644 --- a/plugins/logistics.cpp +++ b/plugins/logistics.cpp @@ -44,6 +44,7 @@ enum StockpileConfigValues { STOCKPILE_CONFIG_TRADE = 2, STOCKPILE_CONFIG_DUMP = 3, STOCKPILE_CONFIG_TRAIN = 4, + STOCKPILE_CONFIG_MELT_MASTERWORKS = 5, }; static int get_config_val(PersistentDataItem& c, int index) { @@ -81,6 +82,7 @@ static PersistentDataItem& ensure_stockpile_config(color_ostream& out, int stock set_config_bool(c, STOCKPILE_CONFIG_TRADE, false); set_config_bool(c, STOCKPILE_CONFIG_DUMP, false); set_config_bool(c, STOCKPILE_CONFIG_TRAIN, false); + set_config_bool(c, STOCKPILE_CONFIG_MELT_MASTERWORKS, false); return c; } @@ -259,8 +261,8 @@ public: class MeltStockProcessor : public StockProcessor { public: - MeltStockProcessor(int32_t stockpile_number, bool enabled, ProcessorStats &stats) - : StockProcessor("melt", stockpile_number, enabled, stats) { } + MeltStockProcessor(int32_t stockpile_number, bool enabled, ProcessorStats &stats, bool melt_masterworks) + : StockProcessor("melt", stockpile_number, enabled, stats), melt_masterworks(melt_masterworks) { } bool is_designated(color_ostream &out, df::item *item) override { return item->flags.bits.melt; @@ -294,7 +296,9 @@ public: } } - if (item->getQuality() >= df::item_quality::Masterful) + if (!melt_masterworks && item->getQuality() >= df::item_quality::Masterful) + return false; + if (item->flags.bits.artifact) return false; return true; @@ -305,6 +309,9 @@ public: item->flags.bits.melt = 1; return true; } + + private: + const bool melt_masterworks; }; class TradeStockProcessor: public StockProcessor { @@ -519,11 +526,12 @@ static void do_cycle(color_ostream& out, int32_t& melt_count, int32_t& trade_cou int32_t stockpile_number = bld->stockpile_number; bool melt = get_config_bool(c, STOCKPILE_CONFIG_MELT); + bool melt_masterworks = get_config_bool(c, STOCKPILE_CONFIG_MELT_MASTERWORKS); bool trade = get_config_bool(c, STOCKPILE_CONFIG_TRADE); bool dump = get_config_bool(c, STOCKPILE_CONFIG_DUMP); bool train = get_config_bool(c, STOCKPILE_CONFIG_TRAIN); - MeltStockProcessor melt_stock_processor(stockpile_number, melt, melt_stats); + MeltStockProcessor melt_stock_processor(stockpile_number, melt, melt_stats, melt_masterworks); TradeStockProcessor trade_stock_processor(stockpile_number, trade, trade_stats); DumpStockProcessor dump_stock_processor(stockpile_number, dump, dump_stats); TrainStockProcessor train_stock_processor(stockpile_number, train, train_stats); @@ -555,7 +563,7 @@ static int logistics_getStockpileData(lua_State *L) { for (auto bld : df::global::world->buildings.other.STOCKPILE) { int32_t stockpile_number = bld->stockpile_number; - MeltStockProcessor melt_stock_processor(stockpile_number, false, melt_stats); + MeltStockProcessor melt_stock_processor(stockpile_number, false, melt_stats, false); TradeStockProcessor trade_stock_processor(stockpile_number, false, trade_stats); DumpStockProcessor dump_stock_processor(stockpile_number, false, dump_stats); TrainStockProcessor train_stock_processor(stockpile_number, false, train_stats); @@ -581,12 +589,14 @@ static int logistics_getStockpileData(lua_State *L) { PersistentDataItem &c = entry.second; bool melt = get_config_bool(c, STOCKPILE_CONFIG_MELT); + bool melt_masterworks = get_config_bool(c, STOCKPILE_CONFIG_MELT_MASTERWORKS); bool trade = get_config_bool(c, STOCKPILE_CONFIG_TRADE); bool dump = get_config_bool(c, STOCKPILE_CONFIG_DUMP); bool train = get_config_bool(c, STOCKPILE_CONFIG_TRAIN); unordered_map config; config.emplace("melt", melt ? "true" : "false"); + config.emplace("melt_masterworks", melt_masterworks ? "true" : "false"); config.emplace("trade", trade ? "true" : "false"); config.emplace("dump", dump ? "true" : "false"); config.emplace("train", train ? "true" : "false"); @@ -633,11 +643,13 @@ static unordered_map get_stockpile_config(int32_t stockpile_number) if (watched_stockpiles.count(stockpile_number)) { PersistentDataItem &c = watched_stockpiles[stockpile_number]; stockpile_config.emplace("melt", get_config_bool(c, STOCKPILE_CONFIG_MELT)); + stockpile_config.emplace("melt_masterworks", get_config_bool(c, STOCKPILE_CONFIG_MELT_MASTERWORKS)); stockpile_config.emplace("trade", get_config_bool(c, STOCKPILE_CONFIG_TRADE)); stockpile_config.emplace("dump", get_config_bool(c, STOCKPILE_CONFIG_DUMP)); stockpile_config.emplace("train", get_config_bool(c, STOCKPILE_CONFIG_TRAIN)); } else { stockpile_config.emplace("melt", false); + stockpile_config.emplace("melt_masterworks", false); stockpile_config.emplace("trade", false); stockpile_config.emplace("dump", false); stockpile_config.emplace("train", false); @@ -666,9 +678,9 @@ static int logistics_getStockpileConfigs(lua_State *L) { return 1; } -static void logistics_setStockpileConfig(color_ostream& out, int stockpile_number, bool melt, bool trade, bool dump, bool train) { - DEBUG(status, out).print("entering logistics_setStockpileConfig stockpile_number=%d, melt=%d, trade=%d, dump=%d, train=%d\n", - stockpile_number, melt, trade, dump, train); +static void logistics_setStockpileConfig(color_ostream& out, int stockpile_number, bool melt, bool trade, bool dump, bool train, bool melt_masterworks) { + DEBUG(status, out).print("entering logistics_setStockpileConfig stockpile_number=%d, melt=%d, trade=%d, dump=%d, train=%d, melt_masterworks=%d\n", + stockpile_number, melt, trade, dump, train, melt_masterworks); if (!find_stockpile(stockpile_number)) { out.printerr("invalid stockpile number: %d\n", stockpile_number); @@ -677,6 +689,7 @@ static void logistics_setStockpileConfig(color_ostream& out, int stockpile_numbe auto &c = ensure_stockpile_config(out, stockpile_number); set_config_bool(c, STOCKPILE_CONFIG_MELT, melt); + set_config_bool(c, STOCKPILE_CONFIG_MELT_MASTERWORKS, melt_masterworks); set_config_bool(c, STOCKPILE_CONFIG_TRADE, trade); set_config_bool(c, STOCKPILE_CONFIG_DUMP, dump); set_config_bool(c, STOCKPILE_CONFIG_TRAIN, train); diff --git a/plugins/lua/logistics.lua b/plugins/lua/logistics.lua index 0231ce593..2f260cc59 100644 --- a/plugins/lua/logistics.lua +++ b/plugins/lua/logistics.lua @@ -29,6 +29,7 @@ function getStockpileData() trade=make_stat('trade', stockpile_number, stats, configs), dump=make_stat('dump', stockpile_number, stats, configs), train=make_stat('train', stockpile_number, stats, configs), + melt_masterworks=configs[stockpile_number] and configs[stockpile_number].melt_masterworks == 'true', }) end table.sort(data, function(a, b) return a.sort_key < b.sort_key end) @@ -41,16 +42,24 @@ local function print_stockpile_data(data) name_len = math.min(40, math.max(name_len, #sp.name)) end + local has_melt_mastworks = false + print('Designated/designatable items in stockpiles:') print() local fmt = '%6s %-' .. name_len .. 's %4s %10s %5s %11s %4s %10s %5s %11s'; print(fmt:format('number', 'name', 'melt', 'melt items', 'trade', 'trade items', 'dump', 'dump items', 'train', 'train items')) local function uline(len) return ('-'):rep(len) end print(fmt:format(uline(6), uline(name_len), uline(4), uline(10), uline(5), uline(11), uline(4), uline(10), uline(5), uline(11))) - local function get_enab(stats) return ('[%s]'):format(stats.enabled and 'x' or ' ') end + local function get_enab(stats, ch) return ('[%s]'):format(stats.enabled and (ch or 'x') or ' ') end local function get_dstat(stats) return ('%d/%d'):format(stats.designated, stats.designated + stats.can_designate) end for _,sp in ipairs(data) do - print(fmt:format(sp.stockpile_number, sp.name, get_enab(sp.melt), get_dstat(sp.melt), get_enab(sp.trade), get_dstat(sp.trade), get_enab(sp.dump), get_dstat(sp.dump), get_enab(sp.train), get_dstat(sp.train))) + has_melt_mastworks = has_melt_mastworks or sp.melt_masterworks + print(fmt:format(sp.stockpile_number, sp.name, get_enab(sp.melt, sp.melt_masterworks and 'X'), get_dstat(sp.melt), + get_enab(sp.trade), get_dstat(sp.trade), get_enab(sp.dump), get_dstat(sp.dump), get_enab(sp.train), get_dstat(sp.train))) + end + if has_melt_mastworks then + print() + print('An "X" in the "melt" column indicates that masterworks in the stockpile will be melted.') end end @@ -101,7 +110,8 @@ local function do_add_stockpile_config(features, opts) features.melt or config.melt == 1, features.trade or config.trade == 1, features.dump or config.dump == 1, - features.train or config.train == 1) + features.train or config.train == 1, + not not opts.melt_masterworks) end end end) @@ -125,6 +135,7 @@ local function process_args(opts, args) return argparse.processArgsGetopt(args, { {'h', 'help', handler=function() opts.help = true end}, + {'m', 'melt-masterworks', handler=function() opts.melt_masterworks = true end}, {'s', 'stockpile', hasArg=true, handler=function(arg) opts.sp = arg end}, }) end diff --git a/plugins/lua/stockpiles.lua b/plugins/lua/stockpiles.lua index 4707c97ad..f25205a9c 100644 --- a/plugins/lua/stockpiles.lua +++ b/plugins/lua/stockpiles.lua @@ -4,6 +4,7 @@ local argparse = require('argparse') local gui = require('gui') local logistics = require('plugins.logistics') local overlay = require('plugins.overlay') +local textures = require('gui.textures') local widgets = require('gui.widgets') local STOCKPILES_DIR = 'dfhack-config/stockpiles' @@ -262,6 +263,45 @@ local function do_export() export_view = export_view and export_view:raise() or StockpilesExportScreen{}:show() end +-------------------- +-- ConfigModal +-------------------- + +ConfigModal = defclass(ConfigModal, gui.ZScreenModal) +ConfigModal.ATTRS{ + focus_path='stockpiles_config', + on_close=DEFAULT_NIL, +} + +function ConfigModal:init() + local sp = dfhack.gui.getSelectedStockpile(true) + local cur_setting = false + if sp then + local config = logistics.logistics_getStockpileConfigs(sp.stockpile_number)[1] + cur_setting = config.melt_masterworks == 1 + end + + self:addviews{ + widgets.Window{ + frame={w=35, h=10}, + frame_title='Advanced logistics settings', + subviews={ + widgets.ToggleHotkeyLabel{ + view_id='melt_masterworks', + frame={l=0, t=0}, + key='CUSTOM_M', + label='Melt masterworks', + initial_option=cur_setting, + }, + }, + }, + } +end + +function ConfigModal:onDismiss() + self.on_close{melt_masterworks=self.subviews.melt_masterworks:getOptionValue()} +end + -------------------- -- MinimizeButton -------------------- @@ -368,9 +408,7 @@ function StockpilesOverlay:init() view_id='main', frame_style=gui.MEDIUM_FRAME, frame_background=gui.CLEAR_PEN, - visible=function() - return not self.minimized - end, + visible=function() return not self.minimized end, subviews={ -- widgets.HotkeyLabel{ -- frame={t=0, l=0}, @@ -439,14 +477,40 @@ function StockpilesOverlay:init() }, } + local button_pen_left = dfhack.pen.parse{fg=COLOR_CYAN, + tile=curry(textures.tp_control_panel, 7) or nil, ch=string.byte('[')} + local button_pen_right = dfhack.pen.parse{fg=COLOR_CYAN, + tile=curry(textures.tp_control_panel, 8) or nil, ch=string.byte(']')} + local help_pen_center = dfhack.pen.parse{ + tile=curry(textures.tp_control_panel, 9) or nil, ch=string.byte('?')} + local configure_pen_center = dfhack.pen.parse{ + tile=curry(textures.tp_control_panel, 10) or nil, ch=15} -- gear/masterwork symbol + self:addviews{ - main_panel, MinimizeButton{ + main_panel, + MinimizeButton{ frame={t=0, r=9}, - get_minimized_fn=function() - return self.minimized - end, + get_minimized_fn=function() return self.minimized end, on_click=self:callback('toggleMinimized'), }, + widgets.Label{ + frame={t=0, r=5, w=3}, + text={ + {tile=button_pen_left}, + {tile=configure_pen_center}, + {tile=button_pen_right}, + }, + on_click=function() ConfigModal{on_close=self:callback('on_custom_config')}:show() end, + }, + widgets.Label{ + frame={t=0, r=1, w=3}, + text={ + {tile=button_pen_left}, + {tile=help_pen_center}, + {tile=button_pen_right}, + }, + on_click=function() dfhack.run_command('gui/launcher', 'stockpiles ') end, + }, } end @@ -475,7 +539,16 @@ function StockpilesOverlay:toggleLogisticsFeature(feature) -- logical xor logistics.logistics_setStockpileConfig(config.stockpile_number, (feature == 'melt') ~= (config.melt == 1), (feature == 'trade') ~= (config.trade == 1), - (feature == 'dump') ~= (config.dump == 1), (feature == 'train') ~= (config.train == 1)) + (feature == 'dump') ~= (config.dump == 1), (feature == 'train') ~= (config.train == 1), + config.melt_masterworks == 1) +end + +function StockpilesOverlay:on_custom_config(custom) + local sp = dfhack.gui.getSelectedStockpile(true) + if not sp then return end + local config = logistics.logistics_getStockpileConfigs(sp.stockpile_number)[1] + logistics.logistics_setStockpileConfig(config.stockpile_number, + config.melt == 1, config.trade == 1, config.dump == 1, config.train == 1, custom.melt_masterworks) end function StockpilesOverlay:toggleMinimized()