support melting masterworks in logistics

develop
Myk Taylor 2023-10-06 18:25:24 -07:00
parent beaba19939
commit 25600e45b0
No known key found for this signature in database
6 changed files with 128 additions and 20 deletions

@ -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. - `preserve-tombs`: tracks tomb assignments to living units and ensures that the tomb stays assigned to them when they die.
## New Features ## New Features
- `logistics`: ``automelt`` now optionally supports melting masterworks; feature accessible from `stockpiles` overlay
## Fixes ## Fixes

@ -72,3 +72,7 @@ Options
Causes the command to act upon stockpiles with the given names or numbers 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 instead of the stockpile that is currently selected in the UI. Note that
the numbers are the stockpile numbers, not the building ids. 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.

@ -100,7 +100,13 @@ Overlay
This plugin provides a panel that appears when you select a stockpile via an 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 `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: .. _stockpiles-library:

@ -44,6 +44,7 @@ enum StockpileConfigValues {
STOCKPILE_CONFIG_TRADE = 2, STOCKPILE_CONFIG_TRADE = 2,
STOCKPILE_CONFIG_DUMP = 3, STOCKPILE_CONFIG_DUMP = 3,
STOCKPILE_CONFIG_TRAIN = 4, STOCKPILE_CONFIG_TRAIN = 4,
STOCKPILE_CONFIG_MELT_MASTERWORKS = 5,
}; };
static int get_config_val(PersistentDataItem& c, int index) { 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_TRADE, false);
set_config_bool(c, STOCKPILE_CONFIG_DUMP, false); set_config_bool(c, STOCKPILE_CONFIG_DUMP, false);
set_config_bool(c, STOCKPILE_CONFIG_TRAIN, false); set_config_bool(c, STOCKPILE_CONFIG_TRAIN, false);
set_config_bool(c, STOCKPILE_CONFIG_MELT_MASTERWORKS, false);
return c; return c;
} }
@ -259,8 +261,8 @@ public:
class MeltStockProcessor : public StockProcessor { class MeltStockProcessor : public StockProcessor {
public: public:
MeltStockProcessor(int32_t stockpile_number, bool enabled, ProcessorStats &stats) MeltStockProcessor(int32_t stockpile_number, bool enabled, ProcessorStats &stats, bool melt_masterworks)
: StockProcessor("melt", stockpile_number, enabled, stats) { } : StockProcessor("melt", stockpile_number, enabled, stats), melt_masterworks(melt_masterworks) { }
bool is_designated(color_ostream &out, df::item *item) override { bool is_designated(color_ostream &out, df::item *item) override {
return item->flags.bits.melt; 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 false;
return true; return true;
@ -305,6 +309,9 @@ public:
item->flags.bits.melt = 1; item->flags.bits.melt = 1;
return true; return true;
} }
private:
const bool melt_masterworks;
}; };
class TradeStockProcessor: public StockProcessor { 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; int32_t stockpile_number = bld->stockpile_number;
bool melt = get_config_bool(c, STOCKPILE_CONFIG_MELT); 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 trade = get_config_bool(c, STOCKPILE_CONFIG_TRADE);
bool dump = get_config_bool(c, STOCKPILE_CONFIG_DUMP); bool dump = get_config_bool(c, STOCKPILE_CONFIG_DUMP);
bool train = get_config_bool(c, STOCKPILE_CONFIG_TRAIN); 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); TradeStockProcessor trade_stock_processor(stockpile_number, trade, trade_stats);
DumpStockProcessor dump_stock_processor(stockpile_number, dump, dump_stats); DumpStockProcessor dump_stock_processor(stockpile_number, dump, dump_stats);
TrainStockProcessor train_stock_processor(stockpile_number, train, train_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) { for (auto bld : df::global::world->buildings.other.STOCKPILE) {
int32_t stockpile_number = bld->stockpile_number; 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); TradeStockProcessor trade_stock_processor(stockpile_number, false, trade_stats);
DumpStockProcessor dump_stock_processor(stockpile_number, false, dump_stats); DumpStockProcessor dump_stock_processor(stockpile_number, false, dump_stats);
TrainStockProcessor train_stock_processor(stockpile_number, false, train_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; PersistentDataItem &c = entry.second;
bool melt = get_config_bool(c, STOCKPILE_CONFIG_MELT); 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 trade = get_config_bool(c, STOCKPILE_CONFIG_TRADE);
bool dump = get_config_bool(c, STOCKPILE_CONFIG_DUMP); bool dump = get_config_bool(c, STOCKPILE_CONFIG_DUMP);
bool train = get_config_bool(c, STOCKPILE_CONFIG_TRAIN); bool train = get_config_bool(c, STOCKPILE_CONFIG_TRAIN);
unordered_map<string, string> config; unordered_map<string, string> config;
config.emplace("melt", melt ? "true" : "false"); config.emplace("melt", melt ? "true" : "false");
config.emplace("melt_masterworks", melt_masterworks ? "true" : "false");
config.emplace("trade", trade ? "true" : "false"); config.emplace("trade", trade ? "true" : "false");
config.emplace("dump", dump ? "true" : "false"); config.emplace("dump", dump ? "true" : "false");
config.emplace("train", train ? "true" : "false"); config.emplace("train", train ? "true" : "false");
@ -633,11 +643,13 @@ static unordered_map<string, int> get_stockpile_config(int32_t stockpile_number)
if (watched_stockpiles.count(stockpile_number)) { if (watched_stockpiles.count(stockpile_number)) {
PersistentDataItem &c = watched_stockpiles[stockpile_number]; PersistentDataItem &c = watched_stockpiles[stockpile_number];
stockpile_config.emplace("melt", get_config_bool(c, STOCKPILE_CONFIG_MELT)); 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("trade", get_config_bool(c, STOCKPILE_CONFIG_TRADE));
stockpile_config.emplace("dump", get_config_bool(c, STOCKPILE_CONFIG_DUMP)); stockpile_config.emplace("dump", get_config_bool(c, STOCKPILE_CONFIG_DUMP));
stockpile_config.emplace("train", get_config_bool(c, STOCKPILE_CONFIG_TRAIN)); stockpile_config.emplace("train", get_config_bool(c, STOCKPILE_CONFIG_TRAIN));
} else { } else {
stockpile_config.emplace("melt", false); stockpile_config.emplace("melt", false);
stockpile_config.emplace("melt_masterworks", false);
stockpile_config.emplace("trade", false); stockpile_config.emplace("trade", false);
stockpile_config.emplace("dump", false); stockpile_config.emplace("dump", false);
stockpile_config.emplace("train", false); stockpile_config.emplace("train", false);
@ -666,9 +678,9 @@ static int logistics_getStockpileConfigs(lua_State *L) {
return 1; return 1;
} }
static void logistics_setStockpileConfig(color_ostream& out, int stockpile_number, bool melt, bool trade, bool dump, bool 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\n", 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); stockpile_number, melt, trade, dump, train, melt_masterworks);
if (!find_stockpile(stockpile_number)) { if (!find_stockpile(stockpile_number)) {
out.printerr("invalid stockpile number: %d\n", 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); auto &c = ensure_stockpile_config(out, stockpile_number);
set_config_bool(c, STOCKPILE_CONFIG_MELT, melt); 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_TRADE, trade);
set_config_bool(c, STOCKPILE_CONFIG_DUMP, dump); set_config_bool(c, STOCKPILE_CONFIG_DUMP, dump);
set_config_bool(c, STOCKPILE_CONFIG_TRAIN, train); set_config_bool(c, STOCKPILE_CONFIG_TRAIN, train);

@ -29,6 +29,7 @@ function getStockpileData()
trade=make_stat('trade', stockpile_number, stats, configs), trade=make_stat('trade', stockpile_number, stats, configs),
dump=make_stat('dump', stockpile_number, stats, configs), dump=make_stat('dump', stockpile_number, stats, configs),
train=make_stat('train', 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 end
table.sort(data, function(a, b) return a.sort_key < b.sort_key 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)) name_len = math.min(40, math.max(name_len, #sp.name))
end end
local has_melt_mastworks = false
print('Designated/designatable items in stockpiles:') print('Designated/designatable items in stockpiles:')
print() print()
local fmt = '%6s %-' .. name_len .. 's %4s %10s %5s %11s %4s %10s %5s %11s'; 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')) 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 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))) 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 local function get_dstat(stats) return ('%d/%d'):format(stats.designated, stats.designated + stats.can_designate) end
for _,sp in ipairs(data) do 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
end end
@ -101,7 +110,8 @@ local function do_add_stockpile_config(features, opts)
features.melt or config.melt == 1, features.melt or config.melt == 1,
features.trade or config.trade == 1, features.trade or config.trade == 1,
features.dump or config.dump == 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 end
end) end)
@ -125,6 +135,7 @@ local function process_args(opts, args)
return argparse.processArgsGetopt(args, { return argparse.processArgsGetopt(args, {
{'h', 'help', handler=function() opts.help = true end}, {'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}, {'s', 'stockpile', hasArg=true, handler=function(arg) opts.sp = arg end},
}) })
end end

@ -4,6 +4,7 @@ local argparse = require('argparse')
local gui = require('gui') local gui = require('gui')
local logistics = require('plugins.logistics') local logistics = require('plugins.logistics')
local overlay = require('plugins.overlay') local overlay = require('plugins.overlay')
local textures = require('gui.textures')
local widgets = require('gui.widgets') local widgets = require('gui.widgets')
local STOCKPILES_DIR = 'dfhack-config/stockpiles' 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() export_view = export_view and export_view:raise() or StockpilesExportScreen{}:show()
end 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 -- MinimizeButton
-------------------- --------------------
@ -368,9 +408,7 @@ function StockpilesOverlay:init()
view_id='main', view_id='main',
frame_style=gui.MEDIUM_FRAME, frame_style=gui.MEDIUM_FRAME,
frame_background=gui.CLEAR_PEN, frame_background=gui.CLEAR_PEN,
visible=function() visible=function() return not self.minimized end,
return not self.minimized
end,
subviews={ subviews={
-- widgets.HotkeyLabel{ -- widgets.HotkeyLabel{
-- frame={t=0, l=0}, -- 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{ self:addviews{
main_panel, MinimizeButton{ main_panel,
MinimizeButton{
frame={t=0, r=9}, frame={t=0, r=9},
get_minimized_fn=function() get_minimized_fn=function() return self.minimized end,
return self.minimized
end,
on_click=self:callback('toggleMinimized'), 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 end
@ -475,7 +539,16 @@ function StockpilesOverlay:toggleLogisticsFeature(feature)
-- logical xor -- logical xor
logistics.logistics_setStockpileConfig(config.stockpile_number, logistics.logistics_setStockpileConfig(config.stockpile_number,
(feature == 'melt') ~= (config.melt == 1), (feature == 'trade') ~= (config.trade == 1), (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 end
function StockpilesOverlay:toggleMinimized() function StockpilesOverlay:toggleMinimized()