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.
## New Features
- `logistics`: ``automelt`` now optionally supports melting masterworks; feature accessible from `stockpiles` overlay
## Fixes

@ -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.

@ -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:

@ -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<string, string> 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<string, int> 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);

@ -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

@ -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()