From 5ffbb4d0efc01edeee6d404a9e7843d0cc289eef Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 18 Apr 2023 08:22:52 -0700 Subject: [PATCH 01/24] unify and update automelt/autotrade/autodump --- data/init/dfhack.tools.init | 23 +- docs/about/Removed.rst | 23 +- docs/plugins/automelt.rst | 55 -- docs/plugins/autotrade.rst | 18 - docs/plugins/logistics.rst | 71 ++ docs/plugins/stockpiles.rst | 13 +- plugins/CMakeLists.txt | 5 +- plugins/autodump.cpp | 230 ------ plugins/automelt.cpp | 799 --------------------- plugins/autotrade.cpp | 467 ------------ plugins/logistics.cpp | 676 +++++++++++++++++ plugins/lua/automelt.lua | 87 --- plugins/lua/logistics.lua | 146 ++++ plugins/lua/stockpiles.lua | 9 +- plugins/stockpiles/OrganicMatLookup.cpp | 5 +- plugins/stockpiles/StockpileSerializer.cpp | 27 +- plugins/stockpiles/StockpileSerializer.h | 2 + plugins/stockpiles/proto/stockpiles.proto | 5 + plugins/stockpiles/stockpiles.cpp | 14 +- 19 files changed, 973 insertions(+), 1702 deletions(-) delete mode 100644 docs/plugins/automelt.rst delete mode 100644 docs/plugins/autotrade.rst create mode 100644 docs/plugins/logistics.rst delete mode 100644 plugins/automelt.cpp delete mode 100644 plugins/autotrade.cpp create mode 100644 plugins/logistics.cpp delete mode 100644 plugins/lua/automelt.lua create mode 100644 plugins/lua/logistics.lua diff --git a/data/init/dfhack.tools.init b/data/init/dfhack.tools.init index 9d8c0dd33..38221883a 100644 --- a/data/init/dfhack.tools.init +++ b/data/init/dfhack.tools.init @@ -78,14 +78,11 @@ # Display DFHack version on title screen #enable title-version -# Allow DFHack tools to overlay functionality and information on the DF screen -enable overlay - -# Allow buildings to be placed now and built later when materials are available +# Enable system services enable buildingplan - -#Allow designated stockpiles to automatically mark items for melting -enable automelt +enable confirm +enable logistics +enable overlay # Dwarf Manipulator (simple in-game Dwarf Therapist replacement) #enable manipulator @@ -97,28 +94,18 @@ enable automelt #enable automaterial # Other interface improvement tools -enable \ - confirm # dwarfmonitor \ # mousequery \ # autogems \ -# autodump \ -# automelt \ -# autotrade \ -# buildingplan \ # trackstop \ # zone \ # stocks \ -# autochop \ -# stockpiles +# #end a line with a backslash to make it continue to the next line. The \ is deleted for the final command. # Multiline commands are ONLY supported for scripts like dfhack.init. You cannot do multiline command manually on the DFHack console. # You cannot extend a commented line. # You can comment out the extension of a line. -# enable mouse controls and sand indicator in embark screen -#embark-tools enable sticky sand mouse - # enable option to enter embark assistant #enable embark-assistant diff --git a/docs/about/Removed.rst b/docs/about/Removed.rst index 174fc56c4..d402eefb6 100644 --- a/docs/about/Removed.rst +++ b/docs/about/Removed.rst @@ -26,6 +26,20 @@ 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`. +.. _automelt: + +automelt +======== +Automatically mark items for melting when they are brought to a monitored +stockpile. Merged into `logistics`. + +.. _autotrade: + +autotrade +========= +Automatically mark items for trading when they are brought to a monitored +stockpile. Merged into `logistics`. + .. _autounsuspend: autounsuspend @@ -56,7 +70,7 @@ Replaced by `gui/launcher --minimal `. create-items ============ -Replaced by `gui/create-item --multi `. +Replaced by `gui/create-item`. .. _deteriorateclothes: @@ -172,6 +186,13 @@ Tool that warned the user when the ``dfhack.init`` file did not exist. Now that ``dfhack.init`` is autogenerated in ``dfhack-config/init``, this warning is no longer necessary. +.. _gui/stockpiles: + +gui/stockpiles +============== +Provided import/export dialogs. Converted to an `overlay` that displays when +a stockpile is selected. + .. _masspit: masspit diff --git a/docs/plugins/automelt.rst b/docs/plugins/automelt.rst deleted file mode 100644 index f652e52b8..000000000 --- a/docs/plugins/automelt.rst +++ /dev/null @@ -1,55 +0,0 @@ -automelt -======== - -.. dfhack-tool:: - :summary: Quickly designate items to be melted. - :tags: fort productivity items stockpiles - :no-command: - -Automelt checks monitor-enabled stockpiles once every in-game day, and will mark valid items for melting. - -Please see `gui/automelt` for the interactive configuration dialog. - -Usage ------ - -:: - - enable automelt - automelt [status] - automelt designate - automelt (monitor|nomonitor) [,...] - -Examples --------- - -Automatically monitor stockpile ("melt"), marking new valid items for melting. This also immediately marks all present items for melting:: - - enable automelt - automelt monitor melt - -Enable monitoring for ("Stockpile #52"), which has not been given a custom name:: - - automelt monitor "Stockpile #52" - -Enable monitoring for ("Stockpile #52"), which has not been given a custom name:: - - automelt monitor 52 - -Enable monitoring for multiple stockpiles ("Stockpile #52", "Stockpile #35", and "melt"):: - - automelt monitor 52,"Stockpile #35",melt - -Commands --------- - -``status`` - Shows current configuration and relevant statistics - -``designate`` - Designates items in monitored stockpiles for melting right now. This works even if ``automelt`` is not currently enabled. - -``(no)monitor `` - Enable/disable monitoring of a given stockpile. Works with either the stockpile's name (if set) or ID. - If the stockpile has no custom name set, you may designate it by either the full name as reported by - the status command, or by just the number. diff --git a/docs/plugins/autotrade.rst b/docs/plugins/autotrade.rst deleted file mode 100644 index 24c1b42fa..000000000 --- a/docs/plugins/autotrade.rst +++ /dev/null @@ -1,18 +0,0 @@ -autotrade -========= - -.. dfhack-tool:: - :summary: Quickly designate items to be traded. - :tags: unavailable fort productivity items stockpiles - :no-command: - -When `enabled `, this plugin adds an option to the :kbd:`q` menu for -stockpiles. When the ``autotrade`` option is selected for the stockpile, any -items placed in the stockpile will automatically be designated to be traded. - -Usage ------ - -:: - - enable autotrade diff --git a/docs/plugins/logistics.rst b/docs/plugins/logistics.rst new file mode 100644 index 000000000..59f71ab8e --- /dev/null +++ b/docs/plugins/logistics.rst @@ -0,0 +1,71 @@ +logistics +========= + +.. dfhack-tool:: + :summary: Automatically mark and route items in monitored stockpiles. + :tags: fort productivity items stockpiles + :no-command: + +Commands act upon the stockpile selected in the UI unless another stockpile +identifier is specified on the commandline. + +When the plugin is enabled, it checks stockpiles marked with automelt, +autotrade, and/or autodump features twice every in-game day, and will mark valid +items in those stockpiles for melting, trading, and/or dumping, respectively. +Note that items will only be marked for trading if a caravan is approaching or +is already at the trade depot. + +Please see `gui/logistics` for the interactive status and configuration dialog. + +Usage +----- + +:: + + enable logistics + logistics [status] + logistics now + logistics add [melt] [trade] [dump] [] + logistics clear [all] [] + +Examples +-------- + +``logistics`` + Print a summary of all your stockpiles, their ``logistics`` configuration, + and the number of items that are designated (or can be designated) by each + of the ``logistics`` processors. + +``logistics now`` + Designate items in monitored stockpiles according to the current + configuration. This works regardless of whether ``logistics`` is currently + enabled. + +``logistics add melt`` + Register the currently selected stockpile for automelting. Meltable items + that are brought to this stockpile will be designated for melting. + +``logistics add melt trade -s goblinite`` + Register the stockpile(s) named "goblinite" for automatic melting and + automatic trading. Items will be marked for melting, but any items still in + the stockpile when a caravan shows up will be brought to the trade depot + for trading. + +``logistics clear`` + Unregisters the currently selected stockpile from any monitoring. Any + currently designated items will remain designated. + +``logistics clear -s 12,15,goblinite`` + Unregisters the stockpiles with stockpile numbers 12 and 15, along with any + stockpiles named "goblinite", from any monitoring. + +``logistics clear all`` + Unregister all stockpiles from any monitoring. + +Options +------- + +``-s``, ``--stockpile [,...]`` + 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. diff --git a/docs/plugins/stockpiles.rst b/docs/plugins/stockpiles.rst index cc1f3c016..0fed3e25f 100644 --- a/docs/plugins/stockpiles.rst +++ b/docs/plugins/stockpiles.rst @@ -5,8 +5,8 @@ stockpiles :summary: Import, export, or modify stockpile settings and features. :tags: fort design productivity stockpiles -If you are importing or exporting setting and don't want to specify a building -ID, select a stockpile in the UI before running the command. +Commands act upon the stockpile selected in the UI unless another stockpile +identifier is specified on the commandline. Usage ----- @@ -15,8 +15,8 @@ Usage stockpiles [status] stockpiles list [] - stockpiles export [] stockpiles import [] + stockpiles export [] Exported stockpile settings are saved in the ``dfhack-config/stockpiles`` folder, where you can view and delete them, if desired. Names can only @@ -61,9 +61,9 @@ Examples Options ------- -``-s``, ``--stockpile `` - Specify a specific stockpile ID instead of using the one currently selected - in the UI. +``-s``, ``--stockpile `` + Specify a specific stockpile by name or internal ID instead of using the + stockpile currently selected in the UI. ``-i``, ``--include `` When exporting, you can include this option to select only specific elements of the stockpile to record. If not specified, everything is included. When @@ -94,6 +94,7 @@ file are: :types: The elements below the categories, which include the sub-categories, the specific item types, and any toggles the category might have (like Prepared meals for the Food category). +:features: DFHack `logistics` features. .. _stockpiles-library: diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 3ae78d320..4029b8e2e 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -83,9 +83,8 @@ dfhack_plugin(autodump autodump.cpp) dfhack_plugin(autofarm autofarm.cpp) #dfhack_plugin(autogems autogems.cpp LINK_LIBRARIES jsoncpp_static) add_subdirectory(autolabor) -dfhack_plugin(automelt automelt.cpp LINK_LIBRARIES lua) dfhack_plugin(autonestbox autonestbox.cpp LINK_LIBRARIES lua) -#dfhack_plugin(autotrade autotrade.cpp) +dfhack_plugin(autoslab autoslab.cpp) dfhack_plugin(blueprint blueprint.cpp LINK_LIBRARIES lua) #dfhack_plugin(burrows burrows.cpp LINK_LIBRARIES lua) #dfhack_plugin(building-hacks building-hacks.cpp LINK_LIBRARIES lua) @@ -129,13 +128,13 @@ dfhack_plugin(hotkeys hotkeys.cpp LINK_LIBRARIES lua) dfhack_plugin(lair lair.cpp) dfhack_plugin(liquids liquids.cpp Brushes.h LINK_LIBRARIES lua) dfhack_plugin(luasocket luasocket.cpp LINK_LIBRARIES clsocket lua dfhack-tinythread) +dfhack_plugin(logistics logistics.cpp LINK_LIBRARIES lua) #dfhack_plugin(manipulator manipulator.cpp) #dfhack_plugin(map-render map-render.cpp LINK_LIBRARIES lua) dfhack_plugin(misery misery.cpp LINK_LIBRARIES lua) #dfhack_plugin(mode mode.cpp) #dfhack_plugin(mousequery mousequery.cpp) dfhack_plugin(nestboxes nestboxes.cpp) -dfhack_plugin(autoslab autoslab.cpp) dfhack_plugin(orders orders.cpp LINK_LIBRARIES jsoncpp_static lua) dfhack_plugin(overlay overlay.cpp LINK_LIBRARIES lua) dfhack_plugin(pathable pathable.cpp LINK_LIBRARIES lua) diff --git a/plugins/autodump.cpp b/plugins/autodump.cpp index 839b3b727..a214f5c94 100644 --- a/plugins/autodump.cpp +++ b/plugins/autodump.cpp @@ -41,236 +41,6 @@ DFHACK_PLUGIN("autodump"); REQUIRE_GLOBAL(gps); REQUIRE_GLOBAL(world); -/* TODO: merge with stockpiles plugin -// Stockpile interface START -static const string PERSISTENCE_KEY = "autodump/stockpiles"; - -static void mark_all_in_stockpiles(vector &stockpiles) -{ - std::vector &items = world->items.other[items_other_id::IN_PLAY]; - - // Precompute a bitmask with the bad flags - df::item_flags bad_flags; - bad_flags.whole = 0; - -#define F(x) bad_flags.bits.x = true; - F(dump); F(forbid); F(garbage_collect); - F(hostile); F(on_fire); F(rotten); F(trader); - F(in_building); F(construction); F(artifact); - F(spider_web); F(owned); F(in_job); -#undef F - - size_t marked_count = 0; - for (size_t i = 0; i < items.size(); i++) - { - df::item *item = items[i]; - if (item->flags.whole & bad_flags.whole) - continue; - - for (auto it = stockpiles.begin(); it != stockpiles.end(); it++) - { - if (!it->inStockpile(item)) - continue; - - ++marked_count; - item->flags.bits.dump = true; - } - } - - if (marked_count) - Gui::showAnnouncement("Marked " + int_to_string(marked_count) + " items to dump", COLOR_GREEN, false); -} - -class StockpileMonitor -{ -public: - bool isMonitored(df::building_stockpilest *sp) - { - for (auto it = monitored_stockpiles.begin(); it != monitored_stockpiles.end(); it++) - { - if (it->matches(sp)) - return true; - } - - return false; - } - - void add(df::building_stockpilest *sp) - { - auto pile = PersistentStockpileInfo(sp, PERSISTENCE_KEY); - if (pile.isValid()) - { - monitored_stockpiles.push_back(PersistentStockpileInfo(pile)); - monitored_stockpiles.back().save(); - } - } - - void remove(df::building_stockpilest *sp) - { - for (auto it = monitored_stockpiles.begin(); it != monitored_stockpiles.end(); it++) - { - if (it->matches(sp)) - { - it->remove(); - monitored_stockpiles.erase(it); - break; - } - } - } - - void doCycle() - { - for (auto it = monitored_stockpiles.begin(); it != monitored_stockpiles.end();) - { - if (!it->isValid()) - it = monitored_stockpiles.erase(it); - else - ++it; - } - - mark_all_in_stockpiles(monitored_stockpiles); - } - - void reset() - { - monitored_stockpiles.clear(); - std::vector items; - DFHack::World::GetPersistentData(&items, PERSISTENCE_KEY); - - for (auto i = items.begin(); i != items.end(); i++) - { - auto pile = PersistentStockpileInfo(*i, PERSISTENCE_KEY); - if (pile.load()) - monitored_stockpiles.push_back(PersistentStockpileInfo(pile)); - else - pile.remove(); - } - } - - -private: - vector monitored_stockpiles; -}; - -static StockpileMonitor monitor; - -#define DELTA_TICKS 620 - - -DFhackCExport command_result plugin_onupdate ( color_ostream &out ) -{ - if(!Maps::IsValid()) - return CR_OK; - - if (DFHack::World::ReadPauseState()) - return CR_OK; - - if (world->frame_counter % DELTA_TICKS != 0) - return CR_OK; - - monitor.doCycle(); - - return CR_OK; -} - -struct dump_hook : public df::viewscreen_dwarfmodest -{ - typedef df::viewscreen_dwarfmodest interpose_base; - - bool handleInput(set *input) - { - if (Gui::inRenameBuilding()) - return false; - - building_stockpilest *sp = get_selected_stockpile(); - if (!sp) - return false; - - if (input->count(interface_key::CUSTOM_SHIFT_D)) - { - if (monitor.isMonitored(sp)) - monitor.remove(sp); - else - monitor.add(sp); - } - - return false; - } - - DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) - { - if (!handleInput(input)) - INTERPOSE_NEXT(feed)(input); - } - - DEFINE_VMETHOD_INTERPOSE(void, render, ()) - { - INTERPOSE_NEXT(render)(); - - building_stockpilest *sp = get_selected_stockpile(); - if (!sp) - return; - - auto dims = Gui::getDwarfmodeViewDims(); - int left_margin = dims.menu_x1 + 1; - int x = left_margin; - int y = dims.y2 - 7; - - int links = 0; - links += sp->links.give_to_pile.size(); - links += sp->links.take_from_pile.size(); - links += sp->links.give_to_workshop.size(); - links += sp->links.take_from_workshop.size(); - bool state = monitor.isMonitored(sp); - - if (links + 12 >= y) { - y = dims.y2; - OutputString(COLOR_WHITE, x, y, "Auto: "); - OutputString(COLOR_LIGHTRED, x, y, "D"); - OutputString(state? COLOR_LIGHTGREEN: COLOR_GREY, x, y, "ump "); - } else { - OutputToggleString(x, y, "Auto dump", "D", state, true, left_margin, COLOR_WHITE, COLOR_LIGHTRED); - } - } -}; - -IMPLEMENT_VMETHOD_INTERPOSE(dump_hook, feed); -IMPLEMENT_VMETHOD_INTERPOSE(dump_hook, render); - -DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) -{ - switch (event) - { - case DFHack::SC_MAP_LOADED: - monitor.reset(); - break; - case DFHack::SC_MAP_UNLOADED: - break; - default: - break; - } - return CR_OK; -} - -DFHACK_PLUGIN_IS_ENABLED(is_enabled); - -DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) -{ - if (enable != is_enabled) - { - if (!INTERPOSE_HOOK(dump_hook, feed).apply(enable) || - !INTERPOSE_HOOK(dump_hook, render).apply(enable)) - return CR_FAILURE; - - is_enabled = enable; - } - - return CR_OK; -} - -// Stockpile interface END -*/ - command_result df_autodump(color_ostream &out, vector & parameters); command_result df_autodump_destroy_here(color_ostream &out, vector & parameters); command_result df_autodump_destroy_item(color_ostream &out, vector & parameters); diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp deleted file mode 100644 index dc0bc0f79..000000000 --- a/plugins/automelt.cpp +++ /dev/null @@ -1,799 +0,0 @@ -#include "Debug.h" -#include "LuaTools.h" -#include "PluginManager.h" - -#include "modules/Buildings.h" -#include "modules/Items.h" -#include "modules/World.h" -#include "modules/Persistence.h" -#include "modules/Gui.h" - -#include "df/world.h" -#include "df/building_stockpilest.h" -#include "df/item_quality.h" - -#include -#include - -using std::map; -using std::string; -using std::unordered_map; -using std::vector; - -using namespace DFHack; -using namespace df::enums; - -DFHACK_PLUGIN("automelt"); -DFHACK_PLUGIN_IS_ENABLED(is_enabled); -REQUIRE_GLOBAL(world); - -namespace DFHack -{ - DBG_DECLARE(automelt, status, DebugCategory::LINFO); - DBG_DECLARE(automelt, cycle, DebugCategory::LINFO); - DBG_DECLARE(automelt, perf, DebugCategory::LINFO); -} - -static const string CONFIG_KEY = string(plugin_name) + "/config"; -static const string STOCKPILE_CONFIG_KEY_PREFIX = string(plugin_name) + "/stockpile/"; -static PersistentDataItem config; - -static unordered_map watched_stockpiles; - -enum StockpileConfigValues -{ - STOCKPILE_CONFIG_ID = 0, - STOCKPILE_CONFIG_MONITORED = 1, -}; - -static int get_config_val(PersistentDataItem &c, int index) -{ - if (!c.isValid()) - return -1; - return c.ival(index); -} - -static bool get_config_bool(PersistentDataItem &c, int index) -{ - return get_config_val(c, index) == 1; -} - -static void set_config_val(PersistentDataItem &c, int index, int value) -{ - if (c.isValid()) - c.ival(index) = value; -} - -static void set_config_bool(PersistentDataItem &c, int index, bool value) -{ - set_config_val(c, index, value ? 1 : 0); -} - -static PersistentDataItem &ensure_stockpile_config(color_ostream &out, int id) -{ - DEBUG(cycle,out).print("ensuring stockpile config id=%d\n", id); - if (watched_stockpiles.count(id)){ - DEBUG(cycle,out).print("stockpile exists in watched_indices\n"); - return watched_stockpiles[id]; - } - - string keyname = STOCKPILE_CONFIG_KEY_PREFIX + int_to_string(id); - DEBUG(status,out).print("creating new persistent key for stockpile %d\n", id); - watched_stockpiles.emplace(id, World::GetPersistentData(keyname, NULL)); - return watched_stockpiles[id]; -} - -static void remove_stockpile_config(color_ostream &out, int id) -{ - if (!watched_stockpiles.count(id)) - return; - DEBUG(status, out).print("removing persistent key for stockpile %d\n", id); - World::DeletePersistentData(watched_stockpiles[id]); - watched_stockpiles.erase(id); -} - -static bool isStockpile(df::building * bld) { - return bld && bld->getType() == df::building_type::Stockpile; -} - -static void validate_stockpile_configs(color_ostream &out) -{ - for (auto &c : watched_stockpiles) { - int id = get_config_val(c.second, STOCKPILE_CONFIG_ID); - auto bld = df::building::find(id); - if (!isStockpile(bld)) - remove_stockpile_config(out, id); - } -} - -static const int32_t CYCLE_TICKS = 1200; -static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle - -static command_result do_command(color_ostream &out, vector ¶meters); -static int32_t do_cycle(color_ostream &out); - -DFhackCExport command_result plugin_init(color_ostream &out, vector &commands) -{ - DEBUG(status, out).print("initializing %s\n", plugin_name); - - // provide a configuration interface for the plugin - commands.push_back(PluginCommand( - plugin_name, - "Auto melt items in designated stockpiles.", - do_command)); - - return CR_OK; -} - -DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) -{ - if (enable != is_enabled) - { - is_enabled = enable; - DEBUG(status, out).print("%s from the API; persisting\n", is_enabled ? "enabled" : "disabled"); - } - else - { - DEBUG(status, out).print("%s from the API, but already %s; no action\n", is_enabled ? "enabled" : "disabled", is_enabled ? "enabled" : "disabled"); - } - return CR_OK; -} - -DFhackCExport command_result plugin_shutdown(color_ostream &out) -{ - DEBUG(status, out).print("shutting down %s\n", plugin_name); - - return CR_OK; -} - -DFhackCExport command_result plugin_load_data(color_ostream &out) -{ - cycle_timestamp = 0; - config = World::GetPersistentData(CONFIG_KEY); - - if (!config.isValid()) - { - DEBUG(status, out).print("no config found in this save; initializing\n"); - config = World::AddPersistentData(CONFIG_KEY); - } - - DEBUG(status, out).print("loading persisted enabled state: %s\n", is_enabled ? "true" : "false"); - vector loaded_persist_data; - World::GetPersistentData(&loaded_persist_data, STOCKPILE_CONFIG_KEY_PREFIX, true); - watched_stockpiles.clear(); - const size_t num_watched_stockpiles = loaded_persist_data.size(); - for (size_t idx = 0; idx < num_watched_stockpiles; ++idx) - { - auto &c = loaded_persist_data[idx]; - watched_stockpiles.emplace(get_config_val(c, STOCKPILE_CONFIG_ID), c); - } - validate_stockpile_configs(out); - - return CR_OK; -} - -DFhackCExport command_result plugin_onupdate(color_ostream &out) -{ - if (!Core::getInstance().isWorldLoaded()) - return CR_OK; - if (is_enabled && world->frame_counter - cycle_timestamp >= CYCLE_TICKS) - { - int32_t marked = do_cycle(out); - if (0 < marked) - out.print("automelt: marked %d item(s) for melting\n", marked); - } - return CR_OK; -} - -static bool call_automelt_lua(color_ostream *out, const char *fn_name, - int nargs = 0, int nres = 0, - Lua::LuaLambda && args_lambda = Lua::DEFAULT_LUA_LAMBDA, - Lua::LuaLambda && res_lambda = Lua::DEFAULT_LUA_LAMBDA) { - DEBUG(status).print("calling automelt lua function: '%s'\n", fn_name); - - CoreSuspender guard; - - auto L = Lua::Core::State; - Lua::StackUnwinder top(L); - - if (!out) - out = &Core::getInstance().getConsole(); - - return Lua::CallLuaModuleFunction(*out, L, "plugins.automelt", fn_name, - nargs, nres, - std::forward(args_lambda), - std::forward(res_lambda)); -} - -static command_result do_command(color_ostream &out, vector ¶meters) { - CoreSuspender suspend; - - if (!Core::getInstance().isWorldLoaded()) { - out.printerr("Cannot run %s without a loaded world.\n", plugin_name); - return CR_FAILURE; - } - - bool show_help = false; - if (!call_automelt_lua(&out, "parse_commandline", parameters.size(), 1, - [&](lua_State *L) { - for (const string ¶m : parameters) - Lua::Push(L, param); - }, - [&](lua_State *L) { - show_help = !lua_toboolean(L, -1); - })) { - return CR_FAILURE; - } - - return show_help ? CR_WRONG_USAGE : CR_OK; -} - -static inline bool is_metal_item(df::item *item) -{ - if (!item) - return false; - MaterialInfo mat(item); - return (mat.getCraftClass() == craft_material_class::Metal); -} - -struct BadFlagsCanMelt { - uint32_t whole; - - BadFlagsCanMelt() { - df::item_flags flags; - #define F(x) flags.bits.x = true; - F(dump); F(forbid); F(garbage_collect); - F(hostile); F(on_fire); F(rotten); F(trader); - F(in_building); F(construction); F(artifact); - F(spider_web); F(owned); F(in_job); - #undef F - whole = flags.whole; - } -}; - -struct BadFlagsMarkItem { - uint32_t whole; - - BadFlagsMarkItem() { - df::item_flags flags; - #define F(x) flags.bits.x = true; - F(dump); F(forbid); F(garbage_collect); - F(hostile); F(on_fire); F(rotten); F(trader); - F(in_building); F(construction); F(artifact); - F(spider_web); F(owned); - #undef F - whole = flags.whole; - } -}; - -// Copied from Kelly Martin's code -static inline bool can_melt(df::item *item) -{ - static const BadFlagsCanMelt bad_flags; - - if (!is_metal_item(item)) - return false; - - if (item->flags.whole & bad_flags.whole) - return false; - - df::item_type t = item->getType(); - - if (t == df::enums::item_type::BAR) - return false; - - for (auto &g : item->general_refs) - { - switch (g->getType()) - { - case general_ref_type::CONTAINS_ITEM: - case general_ref_type::UNIT_HOLDER: - case general_ref_type::CONTAINS_UNIT: - return false; - case general_ref_type::CONTAINED_IN_ITEM: - { - df::item *c = g->getItem(); - for (auto &gg : c->general_refs) - { - if (gg->getType() == general_ref_type::UNIT_HOLDER) - return false; - } - } - break; - default: - break; - } - } - - if (item->getQuality() >= item_quality::Masterful) - return false; - - return true; -} - -static inline bool is_set_to_melt(df::item *item) -{ - return item->flags.bits.melt; -} - -static int mark_item(color_ostream &out, df::item *item, BadFlagsMarkItem bad_flags, int32_t stockpile_id, - int32_t &premarked_item_count, int32_t &item_count, map &tracked_item_map, bool should_melt) -{ - DEBUG(perf,out).print("%s running mark_item: should_melt=%d\n", plugin_name, should_melt); - - if (DBG_NAME(perf).isEnabled(DebugCategory::LDEBUG)) { - string name = ""; - item->getItemDescription(&name, 0); - DEBUG(perf,out).print("item %s %d\n", name.c_str(), item->id); - } - - if (item->flags.whole & bad_flags.whole){ - DEBUG(perf,out).print("rejected flag check\n"); - return 0; - } - - if (item->isAssignedToThisStockpile(stockpile_id)) - { - DEBUG(perf,out).print("assignedToStockpile\n"); - size_t marked_count = 0; - vector contents; - Items::getContainedItems(item, &contents); - for (auto child = contents.begin(); child != contents.end(); child++) - { - DEBUG(perf,out).print("inside child loop\n"); - marked_count += mark_item(out, *child, bad_flags, stockpile_id, premarked_item_count, item_count, tracked_item_map, should_melt); - } - return marked_count; - } - - DEBUG(perf,out).print("past recurse loop\n"); - - if (is_set_to_melt(item)) { - DEBUG(perf,out).print("already set to melt\n"); - tracked_item_map.emplace(item->id, true); - premarked_item_count++; - DEBUG(perf,out).print("premarked_item_count=%d\n", premarked_item_count); - item_count++; - return 0; - } - - if (!can_melt(item)) { - DEBUG(perf,out).print("cannot melt\n"); - return 0; - } - - DEBUG(perf,out).print("increment item count\n"); - item_count++; - - //Only melt if told to, else count - if (should_melt) { - DEBUG(perf,out).print("should_melt hit\n"); - insert_into_vector(world->items.other[items_other_id::ANY_MELT_DESIGNATED], &df::item::id, item); - item->flags.bits.melt = 1; - tracked_item_map.emplace(item->id, true); - return 1; - } else { - return 0; - } - -} - -static int32_t mark_all_in_stockpile(color_ostream &out, PersistentDataItem & stockpile, int32_t &premarked_item_count, int32_t &item_count, map &tracked_item_map, bool should_melt) -{ - DEBUG(perf,out).print("%s running mark_all_in_stockpile\nshould_melt=%d\n", plugin_name, should_melt); - - static const BadFlagsMarkItem bad_flags; - - int32_t marked_count = 0; - - if(!stockpile.isValid()) { - return 0; - } - - int spid = get_config_val(stockpile, STOCKPILE_CONFIG_ID); - auto found = df::building::find(spid); - if (!isStockpile(found)) - return 0; - - df::building_stockpilest * pile_cast = virtual_cast(found); - - if (!pile_cast) - return 0; - - Buildings::StockpileIterator stored; - DEBUG(perf,out).print("starting item iter. loop\n"); - for (stored.begin(pile_cast); !stored.done(); ++stored) { - DEBUG(perf,out).print("calling mark_item\n"); - marked_count += mark_item(out, *stored, bad_flags, spid, premarked_item_count, item_count, tracked_item_map, should_melt); - DEBUG(perf,out).print("end mark_item call\npremarked_item_count=%d\n", premarked_item_count); - } - DEBUG(perf,out).print("end item iter. loop\n"); - DEBUG(perf,out).print("exit mark_all_in_stockpile\nmarked_count %d\npremarked_count %d\n", marked_count, premarked_item_count); - return marked_count; -} - -static int32_t scan_stockpiles(color_ostream &out, bool should_melt, map &item_count_piles, map &premarked_item_count_piles, - map &marked_item_count_piles, map &tracked_item_map) { - DEBUG(perf,out).print("running scan_stockpiles\n"); - int32_t newly_marked = 0; - - item_count_piles.clear(); - premarked_item_count_piles.clear(); - marked_item_count_piles.clear(); - - //Parse all the watched piles - for (auto &c : watched_stockpiles) { - int id = get_config_val(c.second, STOCKPILE_CONFIG_ID); - //Check monitor status - bool monitored = get_config_bool(c.second, STOCKPILE_CONFIG_MONITORED); - - if (!monitored) continue; - - DEBUG(perf,out).print("%s past monitor check\nmonitored=%d\n", plugin_name, monitored); - - int32_t item_count = 0; - - int32_t premarked_count = 0; - - int32_t marked = mark_all_in_stockpile(out, c.second, premarked_count, item_count, tracked_item_map, should_melt); - - DEBUG(perf,out).print("post mark_all_in_stockpile premarked_count=%d\n", premarked_count); - - item_count_piles.emplace(id, item_count); - - premarked_item_count_piles.emplace(id, premarked_count); - - marked_item_count_piles.emplace(id, marked); - - newly_marked += marked; - - } - DEBUG(perf,out).print("exit scan_stockpiles\n"); - return newly_marked; -} - -static int32_t scan_all_melt_designated(color_ostream &out, map &tracked_item_map) { - - DEBUG(perf,out).print("running scan_all_melt_designated\n"); - int32_t marked_item_count = 0; - //loop over all items marked as melt-designated - for (auto item : world->items.other[items_other_id::ANY_MELT_DESIGNATED]) { - //item has already been marked/counted as inside a stockpile. Don't double-count. - if (tracked_item_map.count(item->id)) { - continue; - } - marked_item_count++; - } - DEBUG(perf,out).print("exiting scan_all_melt_designated\n"); - return marked_item_count; -} - -static int32_t scan_count_all(color_ostream &out, bool should_melt, int32_t &marked_item_count_total, int32_t &marked_total_count_all_piles, int32_t &marked_item_count_global, - int32_t &total_items_all_piles, map &item_count_piles, map &premarked_item_count_piles, map &marked_item_count_piles) { - - //Wraps both scan_stockpiles and scan_all_melt_designated - //Returns count of items in piles freshly marked - - int32_t newly_marked_items_piles = 0; - - map tracked_item_map_piles; - - newly_marked_items_piles = scan_stockpiles(out, should_melt, item_count_piles, premarked_item_count_piles, marked_item_count_piles, tracked_item_map_piles); - marked_item_count_global = scan_all_melt_designated(out, tracked_item_map_piles); - - for (auto &i : watched_stockpiles) { - int id = get_config_val(i.second, STOCKPILE_CONFIG_ID); - total_items_all_piles+= item_count_piles[id]; - marked_total_count_all_piles += premarked_item_count_piles[id]; - } - - marked_item_count_total = marked_item_count_global + marked_total_count_all_piles; - - - return newly_marked_items_piles; - -} - -static int32_t do_cycle(color_ostream &out) { - DEBUG(cycle,out).print("running %s cycle\n", plugin_name); - cycle_timestamp = world->frame_counter; - - validate_stockpile_configs(out); - - int32_t marked_item_count_total = 0; - int32_t marked_item_count_global = 0; - int32_t newly_marked_items_piles = 0; - int32_t total_items_all_piles = 0; - int32_t marked_total_count_all_piles = 0; - map item_count_piles, premarked_item_count_piles, marked_item_count_piles; - - newly_marked_items_piles = scan_count_all(out, true, marked_item_count_total, marked_total_count_all_piles, marked_item_count_global, - total_items_all_piles, item_count_piles, premarked_item_count_piles, marked_item_count_piles); - - DEBUG(perf,out).print("exit %s do_cycle\n", plugin_name); - return newly_marked_items_piles; -} - -static int getSelectedStockpile(color_ostream &out) { - df::building *bld = Gui::getSelectedBuilding(out, true); - if (!isStockpile(bld)) { - DEBUG(status,out).print("Selected building is not stockpile\n"); - return -1; - } - - return bld->id; -} - -static PersistentDataItem *getSelectedStockpileConfig(color_ostream &out) { - int32_t bldg_id = getSelectedStockpile(out); - if (bldg_id == -1) { - return NULL; - } - - validate_stockpile_configs(out); - PersistentDataItem *c = NULL; - if (watched_stockpiles.count(bldg_id)) { - c = &(watched_stockpiles[bldg_id]); - return c; - } - - DEBUG(status,out).print("No existing config\n"); - return NULL; -} - -static void push_stockpile_config(lua_State *L, int id, bool monitored) { - map stockpile_config; - stockpile_config.emplace("id", id); - stockpile_config.emplace("monitored", monitored); - Lua::Push(L, stockpile_config); -} - -static void push_stockpile_config(lua_State *L, PersistentDataItem &c) { - push_stockpile_config(L, get_config_val(c, STOCKPILE_CONFIG_ID), - get_config_bool(c, STOCKPILE_CONFIG_MONITORED)); -} - -static void emplace_bulk_stockpile_config(lua_State *L, int id, bool monitored, map> &stockpiles) { - map stockpile_config; - stockpile_config.emplace("id", id); - stockpile_config.emplace("monitored", monitored); - - stockpiles.emplace(id, stockpile_config); -} - -static void emplace_bulk_stockpile_config(lua_State *L, PersistentDataItem &c, map> &stockpiles) { - int32_t id = get_config_val(c, STOCKPILE_CONFIG_ID); - bool monitored = get_config_bool(c, STOCKPILE_CONFIG_MONITORED); - emplace_bulk_stockpile_config(L, id, monitored, stockpiles); -} - -static void automelt_designate(color_ostream &out) { - DEBUG(status, out).print("entering automelt designate\n"); - out.print("designated %d item(s) for melting\n", do_cycle(out)); -} - -static void automelt_printStatus(color_ostream &out) { - DEBUG(status,out).print("entering automelt_printStatus\n"); - validate_stockpile_configs(out); - out.print("automelt is %s\n\n", is_enabled ? "enabled" : "disabled"); - - int32_t marked_item_count_total = 0; - int32_t marked_item_count_global = 0; - int32_t total_items_all_piles = 0; - int32_t marked_total_count_all_piles = 0; - map item_count_piles, premarked_item_count_piles, marked_item_count_piles; - - scan_count_all(out, false, marked_item_count_total, marked_total_count_all_piles, marked_item_count_global, - total_items_all_piles, item_count_piles, premarked_item_count_piles, marked_item_count_piles); - - out.print("summary:\n"); - out.print(" total items in monitored piles: %d\n", total_items_all_piles); - out.print(" marked items in monitored piles: %d\n", marked_total_count_all_piles); - out.print("marked items global (excludes those in monitored piles): %d\n", marked_item_count_global); - out.print(" marked items global (monitored piles + others): %d\n", marked_item_count_total); - - int name_width = 11; - for (auto &stockpile : world->buildings.other.STOCKPILE) { - if (!isStockpile(stockpile)) continue; - if (stockpile->name.empty()) { - string stock_name = "Stockpile #" + int_to_string(stockpile->stockpile_number); - name_width = std::max(name_width, (int)(stock_name.size())); - } else { - name_width = std::max(name_width, (int)stockpile->name.size()); - } - } - name_width = -name_width; - - const char *fmt = "%*s %9s %5s %6s\n"; - out.print(fmt, name_width, "name", "monitored", "items", "marked"); - out.print(fmt, name_width, "----", "---------", "-----", "------"); - - for (auto &stockpile : world->buildings.other.STOCKPILE) { - if (!isStockpile(stockpile)) continue; - bool monitored = false; - int32_t item_count = 0; - int32_t marked_item_count = 0; - if (watched_stockpiles.count(stockpile->id)) { - auto &c = watched_stockpiles[stockpile->id]; - monitored = get_config_bool(c, STOCKPILE_CONFIG_MONITORED); - int id = get_config_val(c, STOCKPILE_CONFIG_ID); - item_count = item_count_piles[id]; - marked_item_count = premarked_item_count_piles[id]; - } - - if (stockpile->name.empty()) { - string stock_name = "Stockpile #" + int_to_string(stockpile->stockpile_number); - out.print(fmt, name_width, stock_name.c_str(), monitored ? "[x]": "[ ]", - int_to_string(item_count).c_str(), int_to_string(marked_item_count).c_str()); - } else { - out.print(fmt, name_width, stockpile->name.c_str(), monitored ? "[x]": "[ ]", - int_to_string(item_count).c_str(), int_to_string(marked_item_count).c_str()); - } - - - } - DEBUG(status,out).print("exiting automelt_printStatus\n"); - -} - -static void automelt_setStockpileConfig(color_ostream &out, int id, bool monitored) { - DEBUG(status,out).print("entering automelt_setStockpileConfig for id=%d and monitored=%d\n", id, monitored); - validate_stockpile_configs(out); - auto bldg = df::building::find(id); - bool isInvalidStockpile = !isStockpile(bldg); - bool hasNoData = !monitored; - if (isInvalidStockpile || hasNoData) { - DEBUG(cycle,out).print("calling remove_stockpile_config with id=%d monitored=%d\n", id, monitored); - remove_stockpile_config(out, id); - return; - } - - PersistentDataItem &c = ensure_stockpile_config(out, id); - set_config_val(c, STOCKPILE_CONFIG_ID, id); - set_config_bool(c, STOCKPILE_CONFIG_MONITORED, monitored); - - //If we're marking something as monitored, go ahead and designate contents. - if (monitored) { - automelt_designate(out); - } -} - -static int automelt_getStockpileConfig(lua_State *L) { - color_ostream *out = Lua::GetOutput(L); - if (!out) - out = &Core::getInstance().getConsole(); - DEBUG(status, *out).print("entering automelt_getStockpileConfig\n"); - validate_stockpile_configs(*out); - - int id; - if (lua_isnumber(L, -1)) { - bool found = false; - id = lua_tointeger(L, -1); - - for (auto &stockpile : world->buildings.other.STOCKPILE) { - if (!isStockpile(stockpile)) continue; - if (id == stockpile->stockpile_number){ - id = stockpile->id; - found = true; - break; - } - } - - if (!found) - return 0; - - } else { - const char * name = lua_tostring(L, -1); - if (!name) - return 0; - string nameStr = name; - bool found = false; - for (auto &stockpile : world->buildings.other.STOCKPILE) { - if (!isStockpile(stockpile)) continue; - if (nameStr == stockpile->name) { - id = stockpile->id; - found = true; - break; - } else { - string stock_name = "Stockpile #" + int_to_string(stockpile->stockpile_number); - if (stock_name == nameStr) { - id = stockpile->id; - found = true; - break; - } - } - - } - if (!found) - return 0; - } - - if (watched_stockpiles.count(id)) { - push_stockpile_config(L, watched_stockpiles[id]); - } else { - push_stockpile_config(L, id, false); - } - return 1; -} - -static int automelt_getSelectedStockpileConfig(lua_State *L){ - color_ostream *out = Lua::GetOutput(L); - if (!out) - out = &Core::getInstance().getConsole(); - DEBUG(status, *out).print("entering automelt_getSelectedStockpileConfig\n"); - validate_stockpile_configs(*out); - - int32_t stock_id = getSelectedStockpile(*out); - PersistentDataItem *c = getSelectedStockpileConfig(*out); - - //If we have a valid config entry, use that. Else assume all false. - if (c) { - push_stockpile_config(L, *c); - } else { - push_stockpile_config(L, stock_id, false); - } - - return 1; -} - -static int automelt_getItemCountsAndStockpileConfigs(lua_State *L) { - color_ostream *out = Lua::GetOutput(L); - if (!out) - out = &Core::getInstance().getConsole(); - DEBUG(status,*out).print("entering automelt_getItemCountsAndStockpileConfigs\n"); - validate_stockpile_configs(*out); - - int32_t marked_item_count_total = 0; - int32_t marked_item_count_global = 0; - int32_t total_items_all_piles = 0; - int32_t marked_total_count_all_piles = 0; - map item_count_piles, premarked_item_count_piles, marked_item_count_piles; - - scan_count_all(*out, false, marked_item_count_total, marked_total_count_all_piles, marked_item_count_global, - total_items_all_piles, item_count_piles, premarked_item_count_piles, marked_item_count_piles); - - map summary; - summary.emplace("total_items", total_items_all_piles); - summary.emplace("premarked_items", marked_total_count_all_piles); - summary.emplace("marked_item_count_global", marked_item_count_global); - summary.emplace("marked_item_count_total", marked_item_count_total); - - Lua::Push(L, summary); - Lua::Push(L, item_count_piles); - Lua::Push(L, marked_item_count_piles); - Lua::Push(L, premarked_item_count_piles); - - map> stockpile_config_map; - - for (auto pile : world->buildings.other.STOCKPILE) { - if (!isStockpile(pile)) - continue; - - int id = pile->id; - if (watched_stockpiles.count(id)) { - emplace_bulk_stockpile_config(L, watched_stockpiles[id], stockpile_config_map); - - } else { - emplace_bulk_stockpile_config(L, id, false, stockpile_config_map); - } - } - - Lua::Push(L, stockpile_config_map); - - - DEBUG(perf, *out).print("exit automelt_getItemCountsAndStockpileConfigs\n"); - - return 5; -} - -DFHACK_PLUGIN_LUA_FUNCTIONS{ - DFHACK_LUA_FUNCTION(automelt_printStatus), - DFHACK_LUA_FUNCTION(automelt_designate), - DFHACK_LUA_FUNCTION(automelt_setStockpileConfig), - DFHACK_LUA_END}; - -DFHACK_PLUGIN_LUA_COMMANDS{ - DFHACK_LUA_COMMAND(automelt_getStockpileConfig), - DFHACK_LUA_COMMAND(automelt_getItemCountsAndStockpileConfigs), - DFHACK_LUA_COMMAND(automelt_getSelectedStockpileConfig), - DFHACK_LUA_END}; diff --git a/plugins/autotrade.cpp b/plugins/autotrade.cpp deleted file mode 100644 index 333e34e95..000000000 --- a/plugins/autotrade.cpp +++ /dev/null @@ -1,467 +0,0 @@ -#include "uicommon.h" - -#include "modules/Gui.h" - -#include "df/world.h" -#include "df/world_raws.h" -#include "df/building_def.h" -#include "df/viewscreen_dwarfmodest.h" -#include "df/viewscreen_tradegoodsst.h" -#include "df/building_stockpilest.h" -#include "modules/Buildings.h" -#include "modules/Items.h" -#include "df/building_tradedepotst.h" -#include "df/general_ref_building_holderst.h" -#include "df/job.h" -#include "df/job_item_ref.h" -#include "modules/Job.h" -#include "df/plotinfost.h" -#include "df/mandate.h" -#include "modules/Maps.h" - -using df::building_stockpilest; - -DFHACK_PLUGIN("autotrade"); -REQUIRE_GLOBAL(gps); -REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(cursor); -REQUIRE_GLOBAL(plotinfo); - -static const string PERSISTENCE_KEY = "autotrade/stockpiles"; - -/* - * Depot Access - */ - -class TradeDepotInfo -{ -public: - TradeDepotInfo() : depot(0) - { - - } - - bool findDepot() - { - if (isValid()) - return true; - - reset(); - for(auto bld_it = world->buildings.all.begin(); bld_it != world->buildings.all.end(); bld_it++) - { - auto bld = *bld_it; - if (!isUsableDepot(bld)) - continue; - - depot = bld; - id = depot->id; - break; - } - - return depot; - } - - bool assignItem(df::item *item) - { - auto href = df::allocate(); - if (!href) - return false; - - auto job = new df::job(); - - df::coord tpos(depot->centerx, depot->centery, depot->z); - job->pos = tpos; - - job->job_type = job_type::BringItemToDepot; - - // job <-> item link - if (!Job::attachJobItem(job, item, df::job_item_ref::Hauled)) - { - delete job; - delete href; - return false; - } - - // job <-> building link - href->building_id = id; - depot->jobs.push_back(job); - job->general_refs.push_back(href); - - // add to job list - Job::linkIntoWorld(job); - - return true; - } - - void reset() - { - depot = 0; - } - -private: - int32_t id; - df::building *depot; - - bool isUsableDepot(df::building* bld) - { - if (bld->getType() != building_type::TradeDepot) - return false; - - if (bld->getBuildStage() < bld->getMaxBuildStage()) - return false; - - if (bld->jobs.size() == 1 && bld->jobs[0]->job_type == job_type::DestroyBuilding) - return false; - - return true; - } - - bool isValid() - { - if (!depot) - return false; - - auto found = df::building::find(id); - return found && found == depot && isUsableDepot(found); - } - -}; - -static TradeDepotInfo depot_info; - - -/* - * Item Manipulation - */ - -static bool is_valid_item(df::item *item) -{ - // Similar to Items::canTrade() with a few checks changed - for (size_t i = 0; i < item->general_refs.size(); i++) - { - df::general_ref *ref = item->general_refs[i]; - - switch (ref->getType()) - { - case general_ref_type::CONTAINED_IN_ITEM: - return false; - - case general_ref_type::UNIT_HOLDER: - return false; - - case general_ref_type::BUILDING_HOLDER: - return false; - - default: - break; - } - } - - for (size_t i = 0; i < item->specific_refs.size(); i++) - { - df::specific_ref *ref = item->specific_refs[i]; - - if (ref->type == specific_ref_type::JOB) - { - // Ignore any items assigned to a job - return false; - } - } - - if (!Items::checkMandates(item)) - return false; - - return true; -} - -static void mark_all_in_stockpiles(vector &stockpiles) -{ - if (!depot_info.findDepot()) - return; - - - // Precompute a bitmask with the bad flags - df::item_flags bad_flags; - bad_flags.whole = 0; - -#define F(x) bad_flags.bits.x = true; - F(dump); F(forbid); F(garbage_collect); - F(hostile); F(on_fire); F(rotten); F(trader); - F(in_building); F(construction); F(artifact); - F(spider_web); F(owned); F(in_job); -#undef F - - size_t marked_count = 0; - size_t error_count = 0; - for (auto it = stockpiles.begin(); it != stockpiles.end(); it++) - { - if (!it->isValid()) - continue; - - Buildings::StockpileIterator stored; - for (stored.begin(it->getStockpile()); !stored.done(); ++stored) - { - df::item *item = *stored; - if (item->flags.whole & bad_flags.whole) - continue; - - if (!is_valid_item(item)) - continue; - - // In case of container, check contained items for mandates - bool mandates_ok = true; - vector contained_items; - Items::getContainedItems(item, &contained_items); - for (df::item *cit : contained_items) - { - if (!Items::checkMandates(cit)) - { - mandates_ok = false; - break; - } - } - - if (!mandates_ok) - continue; - - if (depot_info.assignItem(item)) - { - ++marked_count; - } - else - { - if (++error_count < 5) - { - Gui::showZoomAnnouncement(df::announcement_type::CANCEL_JOB, item->pos, - "Cannot trade item from stockpile " + int_to_string(it->getId()), COLOR_RED, true); - } - } - } - } - - if (marked_count) - Gui::showAnnouncement("Marked " + int_to_string(marked_count) + " items for trade", COLOR_GREEN, false); - - if (error_count >= 5) - { - Gui::showAnnouncement(int_to_string(error_count) + " items were not marked", COLOR_RED, true); - } -} - - -/* - * Stockpile Monitoring - */ - -class StockpileMonitor -{ -public: - bool isMonitored(df::building_stockpilest *sp) - { - for (auto it = monitored_stockpiles.begin(); it != monitored_stockpiles.end(); it++) - { - if (it->matches(sp)) - return true; - } - - return false; - } - - void add(df::building_stockpilest *sp) - { - auto pile = PersistentStockpileInfo(sp, PERSISTENCE_KEY); - if (pile.isValid()) - { - monitored_stockpiles.push_back(pile); - monitored_stockpiles.back().save(); - } - } - - void remove(df::building_stockpilest *sp) - { - for (auto it = monitored_stockpiles.begin(); it != monitored_stockpiles.end(); it++) - { - if (it->matches(sp)) - { - it->remove(); - monitored_stockpiles.erase(it); - break; - } - } - } - - void doCycle() - { - if (!can_trade()) - return; - - for (auto it = monitored_stockpiles.begin(); it != monitored_stockpiles.end();) - { - if (!it->isValid()) - { - it = monitored_stockpiles.erase(it); - continue; - } - - ++it; - } - - mark_all_in_stockpiles(monitored_stockpiles); - } - - void reset() - { - monitored_stockpiles.clear(); - std::vector items; - DFHack::World::GetPersistentData(&items, PERSISTENCE_KEY); - - for (auto i = items.begin(); i != items.end(); i++) - { - auto pile = PersistentStockpileInfo(*i, PERSISTENCE_KEY); - if (pile.load()) - monitored_stockpiles.push_back(pile); - else - pile.remove(); - } - } - - -private: - vector monitored_stockpiles; -}; - -static StockpileMonitor monitor; - -#define DELTA_TICKS 600 - -DFhackCExport command_result plugin_onupdate ( color_ostream &out ) -{ - if(!Maps::IsValid()) - return CR_OK; - - if (DFHack::World::ReadPauseState()) - return CR_OK; - - if (world->frame_counter % DELTA_TICKS != 0) - return CR_OK; - - monitor.doCycle(); - - return CR_OK; -} - - -/* - * Interface - */ - -struct trade_hook : public df::viewscreen_dwarfmodest -{ - typedef df::viewscreen_dwarfmodest interpose_base; - - bool handleInput(set *input) - { - if (Gui::inRenameBuilding()) - return false; - - building_stockpilest *sp = get_selected_stockpile(); - if (!sp) - return false; - - if (input->count(interface_key::CUSTOM_SHIFT_T)) - { - if (monitor.isMonitored(sp)) - monitor.remove(sp); - else - monitor.add(sp); - } - - return false; - } - - DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) - { - if (!handleInput(input)) - INTERPOSE_NEXT(feed)(input); - } - - DEFINE_VMETHOD_INTERPOSE(void, render, ()) - { - INTERPOSE_NEXT(render)(); - - building_stockpilest *sp = get_selected_stockpile(); - if (!sp) - return; - - auto dims = Gui::getDwarfmodeViewDims(); - int left_margin = dims.menu_x1 + 1; - int x = left_margin; - int y = dims.y2 - 5; - - int links = 0; - links += sp->links.give_to_pile.size(); - links += sp->links.take_from_pile.size(); - links += sp->links.give_to_workshop.size(); - links += sp->links.take_from_workshop.size(); - bool state = monitor.isMonitored(sp); - - if (links + 12 >= y) { - y = dims.y2; - OutputString(COLOR_WHITE, x, y, "Auto: "); - x += 11; - OutputString(COLOR_LIGHTRED, x, y, "T"); - OutputString(state? COLOR_LIGHTGREEN: COLOR_GREY, x, y, "rade "); - } else { - OutputToggleString(x, y, "Auto trade", "T", state, true, left_margin, COLOR_WHITE, COLOR_LIGHTRED); - } - } -}; - -IMPLEMENT_VMETHOD_INTERPOSE(trade_hook, feed); -IMPLEMENT_VMETHOD_INTERPOSE(trade_hook, render); - - -DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) -{ - switch (event) - { - case DFHack::SC_MAP_LOADED: - depot_info.reset(); - monitor.reset(); - break; - case DFHack::SC_MAP_UNLOADED: - break; - default: - break; - } - return CR_OK; -} - -DFHACK_PLUGIN_IS_ENABLED(is_enabled); - -DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) -{ - if (enable != is_enabled) - { - depot_info.reset(); - monitor.reset(); - - if (!INTERPOSE_HOOK(trade_hook, feed).apply(enable) || - !INTERPOSE_HOOK(trade_hook, render).apply(enable)) - return CR_FAILURE; - - is_enabled = enable; - } - - return CR_OK; -} - -DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) -{ - return CR_OK; -} - -DFhackCExport command_result plugin_shutdown ( color_ostream &out ) -{ - return CR_OK; -} diff --git a/plugins/logistics.cpp b/plugins/logistics.cpp new file mode 100644 index 000000000..77b462bbb --- /dev/null +++ b/plugins/logistics.cpp @@ -0,0 +1,676 @@ +#include "Debug.h" +#include "LuaTools.h" +#include "PluginManager.h" + +#include "modules/Buildings.h" +#include "modules/Job.h" +#include "modules/Persistence.h" +#include "modules/World.h" + +#include "df/building.h" +#include "df/building_stockpilest.h" +#include "df/building_tradedepotst.h" +#include "df/caravan_state.h" +#include "df/general_ref_building_holderst.h" +#include "df/plotinfost.h" +#include "df/world.h" + +using std::string; +using std::unordered_map; +using std::vector; + +using namespace DFHack; + +DFHACK_PLUGIN("logistics"); + +DFHACK_PLUGIN_IS_ENABLED(is_enabled); + +REQUIRE_GLOBAL(plotinfo); +REQUIRE_GLOBAL(world); + +namespace DFHack { +DBG_DECLARE(logistics, status, DebugCategory::LINFO); +DBG_DECLARE(logistics, cycle, DebugCategory::LINFO); +} + +static const string CONFIG_KEY_PREFIX = string(plugin_name) + "/"; +static unordered_map watched_stockpiles; + +enum StockpileConfigValues { + STOCKPILE_CONFIG_STOCKPILE_NUMBER = 0, + STOCKPILE_CONFIG_MELT = 1, + STOCKPILE_CONFIG_TRADE = 2, + STOCKPILE_CONFIG_DUMP = 3, +}; + +static int get_config_val(PersistentDataItem& c, int index) { + if (!c.isValid()) + return -1; + return c.ival(index); +} + +static bool get_config_bool(PersistentDataItem& c, int index) { + return get_config_val(c, index) == 1; +} + +static void set_config_val(PersistentDataItem& c, int index, int value) { + if (c.isValid()) + c.ival(index) = value; +} + +static void set_config_bool(PersistentDataItem& c, int index, bool value) { + set_config_val(c, index, value ? 1 : 0); +} + +static PersistentDataItem& ensure_stockpile_config(color_ostream& out, int stockpile_number) { + DEBUG(cycle, out).print("ensuring stockpile config stockpile_number=%d\n", stockpile_number); + if (watched_stockpiles.count(stockpile_number)) { + DEBUG(cycle, out).print("stockpile exists in watched_stockpiles\n"); + return watched_stockpiles[stockpile_number]; + } + + string keyname = CONFIG_KEY_PREFIX + int_to_string(stockpile_number); + DEBUG(status, out).print("creating new persistent key for stockpile %d\n", stockpile_number); + watched_stockpiles.emplace(stockpile_number, World::GetPersistentData(keyname, NULL)); + PersistentDataItem& c = watched_stockpiles[stockpile_number]; + set_config_val(c, STOCKPILE_CONFIG_STOCKPILE_NUMBER, stockpile_number); + set_config_bool(c, STOCKPILE_CONFIG_MELT, false); + set_config_bool(c, STOCKPILE_CONFIG_TRADE, false); + set_config_bool(c, STOCKPILE_CONFIG_DUMP, false); + return c; +} + +static void remove_stockpile_config(color_ostream& out, int stockpile_number) { + if (!watched_stockpiles.count(stockpile_number)) + return; + DEBUG(status, out).print("removing persistent key for stockpile %d\n", stockpile_number); + World::DeletePersistentData(watched_stockpiles[stockpile_number]); + watched_stockpiles.erase(stockpile_number); +} + +static const int32_t CYCLE_TICKS = 600; +static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle + +static command_result do_command(color_ostream &out, vector ¶meters); +static void do_cycle(color_ostream &out, int32_t &melt_count, int32_t &trade_count, int32_t &dump_count); + +DFhackCExport command_result plugin_init(color_ostream &out, vector &commands) { + DEBUG(status, out).print("initializing %s\n", plugin_name); + + commands.push_back(PluginCommand( + plugin_name, + "Automatically mark and route items in monitored stockpiles.", + do_command)); + + return CR_OK; +} + +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { + is_enabled = enable; + DEBUG(status, out).print("now %s\n", is_enabled ? "enabled" : "disabled"); + return CR_OK; +} + +static df::building_stockpilest* find_stockpile(int32_t stockpile_number) { + return binsearch_in_vector(world->buildings.other.STOCKPILE, + &df::building_stockpilest::stockpile_number, stockpile_number); +} + +static void validate_stockpile_configs(color_ostream& out, + unordered_map &cache) { + for (auto& entry : watched_stockpiles) { + int stockpile_number = entry.first; + PersistentDataItem &c = entry.second; + auto bld = find_stockpile(stockpile_number); + if (!bld || ( + !get_config_bool(c, STOCKPILE_CONFIG_MELT) && + !get_config_bool(c, STOCKPILE_CONFIG_TRADE) && + !get_config_bool(c, STOCKPILE_CONFIG_DUMP))) { + remove_stockpile_config(out, stockpile_number); + continue; + } + cache.emplace(bld, c); + } +} + +// remove this function once saves from 50.08 are no longer compatible +static void migrate_old_keys(color_ostream &out) { + vector old_data; + World::GetPersistentData(&old_data, "automelt/stockpile/", true); + const size_t num_old_keys = old_data.size(); + for (size_t idx = 0; idx < num_old_keys; ++idx) { + auto& old_c = old_data[idx]; + int32_t bld_id = get_config_val(old_c, 0); + bool melt_was_on = get_config_bool(old_c, 1); + World::DeletePersistentData(old_c); + auto bld = df::building::find(bld_id); + if (!bld || bld->getType() != df::building_type::Stockpile || + watched_stockpiles.count(static_cast(bld)->stockpile_number)) + continue; + auto &c = ensure_stockpile_config(out, static_cast(bld)->stockpile_number); + set_config_bool(c, STOCKPILE_CONFIG_MELT, melt_was_on); + } +} + +DFhackCExport command_result plugin_load_data(color_ostream &out) { + cycle_timestamp = 0; + + vector loaded_persist_data; + World::GetPersistentData(&loaded_persist_data, CONFIG_KEY_PREFIX, true); + watched_stockpiles.clear(); + const size_t num_watched_stockpiles = loaded_persist_data.size(); + for (size_t idx = 0; idx < num_watched_stockpiles; ++idx) { + auto& c = loaded_persist_data[idx]; + watched_stockpiles.emplace(get_config_val(c, STOCKPILE_CONFIG_STOCKPILE_NUMBER), c); + } + migrate_old_keys(out); + + return CR_OK; +} + +DFhackCExport command_result plugin_onupdate(color_ostream &out) { + if (!is_enabled || !Core::getInstance().isWorldLoaded()) + return CR_OK; + if (world->frame_counter - cycle_timestamp >= CYCLE_TICKS) { + int32_t melt_count = 0, trade_count = 0, dump_count = 0; + do_cycle(out, melt_count, trade_count, dump_count); + if (0 < melt_count) + out.print("logistics: marked %d item(s) for melting\n", melt_count); + if (0 < trade_count) + out.print("logistics: marked %d item(s) for trading\n", trade_count); + if (0 < dump_count) + out.print("logistics: marked %d item(s) for dumping\n", dump_count); + } + return CR_OK; +} + +static bool call_logistics_lua(color_ostream* out, const char* fn_name, + int nargs = 0, int nres = 0, + Lua::LuaLambda && args_lambda = Lua::DEFAULT_LUA_LAMBDA, + Lua::LuaLambda && res_lambda = Lua::DEFAULT_LUA_LAMBDA) { + DEBUG(status).print("calling logistics lua function: '%s'\n", fn_name); + + CoreSuspender guard; + + auto L = Lua::Core::State; + Lua::StackUnwinder top(L); + + if (!out) + out = &Core::getInstance().getConsole(); + + return Lua::CallLuaModuleFunction(*out, L, "plugins.logistics", fn_name, + nargs, nres, + std::forward(args_lambda), + std::forward(res_lambda)); +} + +static command_result do_command(color_ostream &out, vector ¶meters) { + CoreSuspender suspend; + + bool show_help = false; + if (!call_logistics_lua(&out, "parse_commandline", 1, 1, + [&](lua_State *L) { + Lua::PushVector(L, parameters); + }, + [&](lua_State *L) { + show_help = !lua_toboolean(L, -1); + })) { + return CR_FAILURE; + } + + return show_help ? CR_WRONG_USAGE : CR_OK; +} + +///////////////////////////////////////////////////// +// cycle +// + +typedef unordered_map StatMap; + +struct ProcessorStats { + size_t newly_designated = 0; + StatMap designated_counts, can_designate_counts; +}; + +class StockProcessor { +public: + const string name; + const int32_t stockpile_number; + const bool enabled; + ProcessorStats &stats; +protected: + StockProcessor(const string &name, int32_t stockpile_number, bool enabled, ProcessorStats &stats) + : name(name), stockpile_number(stockpile_number), enabled(enabled), stats(stats) { } +public: + virtual bool is_designated(color_ostream &out, df::item *item) = 0; + virtual bool can_designate(color_ostream &out, df::item *item) = 0; + virtual bool designate(color_ostream &out, df::item *item) = 0; +}; + +class MeltStockProcessor : public StockProcessor { +public: + MeltStockProcessor(int32_t stockpile_number, bool enabled, ProcessorStats &stats) + : StockProcessor("melt", stockpile_number, enabled, stats) { } + + virtual bool is_designated(color_ostream &out, df::item *item) override { + return item->flags.bits.melt; + } + + virtual bool can_designate(color_ostream &out, df::item *item) override { + MaterialInfo mat(item); + if (mat.getCraftClass() != df::craft_material_class::Metal) + return false; + + if (item->getType() == df::item_type::BAR) + return false; + + for (auto &g : item->general_refs) { + switch (g->getType()) { + case df::general_ref_type::CONTAINS_ITEM: + case df::general_ref_type::UNIT_HOLDER: + case df::general_ref_type::CONTAINS_UNIT: + return false; + case df::general_ref_type::CONTAINED_IN_ITEM: + { + df::item *c = g->getItem(); + for (auto &gg : c->general_refs) { + if (gg->getType() == df::general_ref_type::UNIT_HOLDER) + return false; + } + break; + } + default: + break; + } + } + + if (item->getQuality() >= df::item_quality::Masterful) + return false; + + return true; + } + + virtual bool designate(color_ostream &out, df::item *item) override { + insert_into_vector(world->items.other.ANY_MELT_DESIGNATED, &df::item::id, item); + item->flags.bits.melt = 1; + return true; + } +}; + +class TradeStockProcessor: public StockProcessor { +public: + TradeStockProcessor(int32_t stockpile_number, bool enabled, ProcessorStats& stats) + : StockProcessor("trade", stockpile_number, enabled && get_active_trade_depot(), stats), + depot(get_active_trade_depot()) { } + + virtual bool is_designated(color_ostream& out, df::item* item) override { + auto ref = Items::getSpecificRef(item, df::specific_ref_type::JOB); + return ref && ref->data.job && + ref->data.job->job_type == df::job_type::BringItemToDepot; + } + + virtual bool can_designate(color_ostream& out, df::item* item) override { + return Items::canTradeWithContents(item); + } + + virtual bool designate(color_ostream& out, df::item* item) override { + if (!depot) + return false; + + auto href = df::allocate(); + if (!href) + return false; + + auto job = new df::job(); + job->job_type = df::job_type::BringItemToDepot; + job->pos = df::coord(depot->centerx, depot->centery, depot->z); + + // job <-> item link + if (!Job::attachJobItem(job, item, df::job_item_ref::Hauled)) { + delete job; + delete href; + return false; + } + + // job <-> building link + href->building_id = depot->id; + depot->jobs.push_back(job); + job->general_refs.push_back(href); + + // add to job list + Job::linkIntoWorld(job); + + return true; + } + +private: + df::building_tradedepotst * const depot; + + static df::building_tradedepotst * get_active_trade_depot() { + // at least one caravan must be approaching or ready to trade + if (!plotinfo->caravans.size()) + return NULL; + bool found = false; + for (auto caravan : plotinfo->caravans) { + auto trade_state = caravan->trade_state; + auto time_remaining = caravan->time_remaining; + if ((trade_state == df::caravan_state::T_trade_state::Approaching || + trade_state == df::caravan_state::T_trade_state::AtDepot) && time_remaining != 0) { + found = true; + break; + } + } + if (!found) + return NULL; + + // at least one trade depot must be ready to receive goods + for (auto bld : world->buildings.other.TRADE_DEPOT) { + if (bld->getBuildStage() < bld->getMaxBuildStage()) + continue; + + if (bld->jobs.size() == 1 && + bld->jobs[0]->job_type == df::job_type::DestroyBuilding) + continue; + + return bld; + } + return NULL; + } +}; + +class DumpStockProcessor: public StockProcessor { +public: + DumpStockProcessor(int32_t stockpile_number, bool enabled, ProcessorStats& stats) + : StockProcessor("dump", stockpile_number, enabled, stats) { } + + virtual bool is_designated(color_ostream& out, df::item* item) override { + return item->flags.bits.dump; + } + + virtual bool can_designate(color_ostream& out, df::item* item) override { + return true; + } + + virtual bool designate(color_ostream& out, df::item* item) override { + item->flags.bits.dump = true; + return true; + } +}; + +static const struct BadFlags { + const uint32_t whole; + + BadFlags() : whole(get_bad_flags()) { } + +private: + uint32_t get_bad_flags() { + df::item_flags flags; + #define F(x) flags.bits.x = true; + F(forbid); F(garbage_collect); F(hostile); F(on_fire); + F(rotten); F(trader); F(in_building); F(construction); + F(artifact); F(spider_web); F(owned); F(in_job); + #undef F + return flags.whole; + } +} bad_flags; + +static void scan_item(color_ostream &out, df::item *item, StockProcessor &processor) { + DEBUG(cycle,out).print("scan_item [%s] item_id=%d\n", processor.name.c_str(), item->id); + + if (DBG_NAME(cycle).isEnabled(DebugCategory::LTRACE)) { + string name = ""; + item->getItemDescription(&name, 0); + TRACE(cycle,out).print("item: %s\n", name.c_str()); + } + + if (item->flags.whole & bad_flags.whole) { + TRACE(cycle,out).print("rejected flag check\n"); + return; + } + + if (processor.is_designated(out, item)) { + TRACE(cycle,out).print("already designated\n"); + ++processor.stats.designated_counts[processor.stockpile_number]; + return; + } + + if (!processor.can_designate(out, item)) { + TRACE(cycle,out).print("cannot designate\n"); + return; + } + + if (!processor.enabled) { + ++processor.stats.can_designate_counts[processor.stockpile_number]; + return; + } + + processor.designate(out, item); + ++processor.stats.newly_designated; + ++processor.stats.designated_counts[processor.stockpile_number]; +} + +static void scan_stockpile(color_ostream &out, df::building_stockpilest *bld, + MeltStockProcessor &melt_stock_processor, + TradeStockProcessor &trade_stock_processor, + DumpStockProcessor &dump_stock_processor) { + auto id = bld->id; + Buildings::StockpileIterator items; + for (items.begin(bld); !items.done(); ++items) { + df::item *item = *items; + scan_item(out, item, trade_stock_processor); + if (0 == (item->flags.whole & bad_flags.whole) && + item->isAssignedToThisStockpile(id)) { + TRACE(cycle,out).print("assignedToStockpile\n"); + vector contents; + Items::getContainedItems(item, &contents); + for (df::item *contained_item : contents) { + scan_item(out, contained_item, melt_stock_processor); + scan_item(out, contained_item, dump_stock_processor); + } + continue; + } + scan_item(out, item, melt_stock_processor); + scan_item(out, item, dump_stock_processor); + } +} + +static void do_cycle(color_ostream &out, int32_t &melt_count, int32_t &trade_count, int32_t &dump_count) { + DEBUG(cycle,out).print("running %s cycle\n", plugin_name); + cycle_timestamp = world->frame_counter; + + ProcessorStats melt_stats, trade_stats, dump_stats; + unordered_map cache; + validate_stockpile_configs(out, cache); + + for (auto &entry : cache) { + df::building_stockpilest *bld = entry.first; + PersistentDataItem &c = entry.second; + int32_t stockpile_number = bld->stockpile_number; + + bool melt = get_config_bool(c, STOCKPILE_CONFIG_MELT); + bool trade = get_config_bool(c, STOCKPILE_CONFIG_TRADE); + bool dump = get_config_bool(c, STOCKPILE_CONFIG_DUMP); + + MeltStockProcessor melt_stock_processor(stockpile_number, melt, melt_stats); + TradeStockProcessor trade_stock_processor(stockpile_number, trade, trade_stats); + DumpStockProcessor dump_stock_processor(stockpile_number, dump, dump_stats); + + scan_stockpile(out, bld, melt_stock_processor, + trade_stock_processor, dump_stock_processor); + } + + melt_count = melt_stats.newly_designated; + trade_count = trade_stats.newly_designated; + dump_count = dump_stats.newly_designated; + TRACE(cycle,out).print("exit %s do_cycle\n", plugin_name); +} + +///////////////////////////////////////////////////// +// Lua API +// + +static int logistics_getStockpileData(lua_State *L) { + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + DEBUG(status,*out).print("entering logistics_getStockpileData\n"); + + unordered_map cache; + validate_stockpile_configs(*out, cache); + + ProcessorStats melt_stats, trade_stats, dump_stats; + + 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); + TradeStockProcessor trade_stock_processor(stockpile_number, false, trade_stats); + DumpStockProcessor dump_stock_processor(stockpile_number, false, dump_stats); + + scan_stockpile(*out, bld, melt_stock_processor, + trade_stock_processor, dump_stock_processor); + } + + unordered_map stats; + stats.emplace("melt_designated", melt_stats.designated_counts); + stats.emplace("melt_can_designate", melt_stats.can_designate_counts); + stats.emplace("trade_designated", trade_stats.designated_counts); + stats.emplace("trade_can_designate", trade_stats.can_designate_counts); + stats.emplace("dump_designated", dump_stats.designated_counts); + stats.emplace("dump_can_designate", dump_stats.can_designate_counts); + Lua::Push(L, stats); + + unordered_map> configs; + for (auto &entry : cache) { + df::building_stockpilest *bld = entry.first; + PersistentDataItem &c = entry.second; + + bool melt = get_config_bool(c, STOCKPILE_CONFIG_MELT); + bool trade = get_config_bool(c, STOCKPILE_CONFIG_TRADE); + bool dump = get_config_bool(c, STOCKPILE_CONFIG_DUMP); + + unordered_map config; + config.emplace("melt", melt ? "true" : "false"); + config.emplace("trade", trade ? "true" : "false"); + config.emplace("dump", dump ? "true" : "false"); + + configs.emplace(bld->stockpile_number, config); + } + Lua::Push(L, configs); + + TRACE(cycle, *out).print("exit logistics_getStockpileData\n"); + + return 2; +} + +static void logistics_cycle(color_ostream &out) { + DEBUG(status, out).print("entering logistics_cycle\n"); + int32_t melt_count = 0, trade_count = 0, dump_count = 0; + do_cycle(out, melt_count, trade_count, dump_count); + out.print("logistics: marked %d item(s) for melting\n", melt_count); + out.print("logistics: marked %d item(s) for trading\n", trade_count); + out.print("logistics: marked %d item(s) for dumping\n", dump_count); +} + +static void find_stockpiles(lua_State *L, int idx, + vector &sps) { + if (lua_isnumber(L, idx)) { + sps.emplace_back(find_stockpile(lua_tointeger(L, -1))); + return; + } + + const char * pname = lua_tostring(L, -1); + if (!pname || !*pname) + return; + string name(pname); + for (auto sp : world->buildings.other.STOCKPILE) { + if (name == sp->name) + sps.emplace_back(sp); + } +} + +static unordered_map get_stockpile_config(int32_t stockpile_number) { + unordered_map stockpile_config; + stockpile_config.emplace("stockpile_number", 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("trade", get_config_bool(c, STOCKPILE_CONFIG_TRADE)); + stockpile_config.emplace("dump", get_config_bool(c, STOCKPILE_CONFIG_DUMP)); + } else { + stockpile_config.emplace("melt", false); + stockpile_config.emplace("trade", false); + stockpile_config.emplace("dump", false); + } + return stockpile_config; +} + +static int logistics_getStockpileConfigs(lua_State *L) { + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + DEBUG(status, *out).print("entering logistics_getStockpileConfig\n"); + + unordered_map cache; + validate_stockpile_configs(*out, cache); + + vector sps; + find_stockpiles(L, -1, sps); + if (sps.empty()) + return 0; + + vector> configs; + for (auto sp : sps) + configs.emplace_back(get_stockpile_config(sp->stockpile_number)); + Lua::PushVector(L, configs); + return 1; +} + +static void logistics_setStockpileConfig(color_ostream &out, int stockpile_number, bool melt, bool trade, bool dump) { + DEBUG(status, out).print("entering logistics_setStockpileConfig stockpile_number=%d, melt=%d, trade=%d, dump=%d\n", + stockpile_number, melt, trade, dump); + + if (!find_stockpile(stockpile_number)) { + out.printerr("invalid stockpile number: %d\n", stockpile_number); + return; + } + + auto &c = ensure_stockpile_config(out, stockpile_number); + set_config_bool(c, STOCKPILE_CONFIG_MELT, melt); + set_config_bool(c, STOCKPILE_CONFIG_TRADE, trade); + set_config_bool(c, STOCKPILE_CONFIG_DUMP, dump); +} + +static int logistics_clearStockpileConfig(lua_State *L) { + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + DEBUG(status, *out).print("entering logistics_clearStockpileConfig\n"); + + vector sps; + find_stockpiles(L, -1, sps); + if (sps.empty()) + return 0; + + for (auto sp : sps) + remove_stockpile_config(*out, sp->stockpile_number); + return 0; +} + +static void logistics_clearAllStockpileConfigs(color_ostream &out) { + DEBUG(status, out).print("entering logistics_clearAllStockpileConfigs\n"); + for (auto &entry : watched_stockpiles) + World::DeletePersistentData(entry.second); + watched_stockpiles.clear(); +} + +DFHACK_PLUGIN_LUA_FUNCTIONS{ + DFHACK_LUA_FUNCTION(logistics_cycle), + DFHACK_LUA_FUNCTION(logistics_setStockpileConfig), + DFHACK_LUA_FUNCTION(logistics_clearAllStockpileConfigs), + DFHACK_LUA_END}; + +DFHACK_PLUGIN_LUA_COMMANDS{ + DFHACK_LUA_COMMAND(logistics_getStockpileData), + DFHACK_LUA_COMMAND(logistics_getStockpileConfigs), + DFHACK_LUA_COMMAND(logistics_clearStockpileConfig), + DFHACK_LUA_END}; diff --git a/plugins/lua/automelt.lua b/plugins/lua/automelt.lua deleted file mode 100644 index cbd1bd538..000000000 --- a/plugins/lua/automelt.lua +++ /dev/null @@ -1,87 +0,0 @@ -local _ENV = mkmodule('plugins.automelt') - -local argparse = require('argparse') - -local function process_args(opts, args) - if args[1] == 'help' then - opts.help = true - return - end - - return argparse.processArgsGetopt(args, { - {'h', 'help', handler=function() opts.help = true end}, - }) -end - - -local function do_set_stockpile_config(var_name, val, stockpiles) - for _,bspec in ipairs(argparse.stringList(stockpiles)) do - local config = automelt_getStockpileConfig(bspec) - if not config then - dfhack.printerr('invalid stockpile: '..tostring(bspec)) - else - config[var_name] = val - automelt_setStockpileConfig(config.id, config.monitor, config.melt) - end - end -end - - -function parse_commandline(...) - local args, opts = {...}, {} - local positionals = process_args(opts, args) - - if opts.help then - return false - end - - local command = positionals[1] - if not command or command == 'status' then - automelt_printStatus() - elseif command == 'designate' then - automelt_designate() - elseif command == 'monitor' then - do_set_stockpile_config('monitor', true, args[2]) - elseif command == 'nomonitor' or command == 'unmonitor' then - do_set_stockpile_config('monitor', false, args[2]) - else - return false - end - - return true -end - --- used by gui/automelt -function setStockpileConfig(config) - automelt_setStockpileConfig(config.id, config.monitored) -end - -function getItemCountsAndStockpileConfigs() - local fmt = 'Stockpile #%-5s' - local data = {automelt_getItemCountsAndStockpileConfigs()} - local ret = {} - ret.summary = table.remove(data, 1) - ret.item_counts = table.remove(data, 1) - ret.marked_item_counts = table.remove(data, 1) - ret.premarked_item_counts = table.remove(data, 1) - local unparsed_stockpile_configs = table.remove(data, 1) - ret.stockpile_configs = {} - - for idx,c in pairs(unparsed_stockpile_configs) do - if not c.id or c.id == -1 then - c.name = "ERROR" - c.monitored = false - else - c.name = df.building.find(c.id).name - if not c.name or c.name == '' then - c.name = (fmt):format(tostring(df.building.find(c.id).stockpile_number)) - end - c.monitored = c.monitored ~= 0 - end - table.insert(ret.stockpile_configs, c) - - end - return ret -end - -return _ENV diff --git a/plugins/lua/logistics.lua b/plugins/lua/logistics.lua new file mode 100644 index 000000000..806d1aa97 --- /dev/null +++ b/plugins/lua/logistics.lua @@ -0,0 +1,146 @@ +local _ENV = mkmodule('plugins.logistics') + +local argparse = require('argparse') +local utils = require('utils') + +local function make_stat(name, stockpile_number, stats, configs) + return { + enabled=configs[stockpile_number] and configs[stockpile_number][name] == 'true', + designated=stats[name..'_designated'][stockpile_number] or 0, + can_designate=stats[name..'_can_designate'][stockpile_number] or 0, + } +end + +function getStockpileData() + local stats, configs = logistics_getStockpileData() + local data = {} + for _,bld in ipairs(df.global.world.buildings.other.STOCKPILE) do + local stockpile_number, name = bld.stockpile_number, bld.name + local sort_key = name + if #name == 0 then + name = ('Stockpile #%d'):format(bld.stockpile_number) + sort_key = ('Stockpile #%09d'):format(bld.stockpile_number) + end + table.insert(data, { + stockpile_number=stockpile_number, + name=name, + sort_key=sort_key, + melt=make_stat('melt', stockpile_number, stats, configs), + trade=make_stat('trade', stockpile_number, stats, configs), + dump=make_stat('dump', stockpile_number, stats, configs), + }) + end + table.sort(data, function(a, b) return a.sort_key < b.sort_key end) + return data +end + +local function print_status() + print(('logistics is %sactively monitoring stockpiles and marking items') + :format(isEnabled() and '' or 'not ')) + + if df.global.gamemode ~= df.game_mode.DWARF or not dfhack.isMapLoaded() then + return + end + + local data = getStockpileData() + + print() + if not data[1] then + print 'No stockpiles defined -- go make some!' + return + end + + local name_len = 12 + for _,sp in ipairs(data) do + name_len = math.min(40, math.max(name_len, #sp.name)) + end + + print('Designated/designatable items in stockpiles:') + print() + local fmt = '%6s %-' .. name_len .. 's %11s %11s %11s'; + print(fmt:format('number', 'name', 'melt', 'trade', 'dump')) + local function uline(len) return ('-'):rep(len) end + print(fmt:format(uline(6), uline(name_len), uline(11), uline(11), uline(11))) + local function get_enab(stats) return ('[%s]'):format(stats.enabled and '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_enab(sp.trade), get_enab(sp.dump))) + print(fmt:format('', '', get_dstat(sp.melt), get_dstat(sp.trade), get_dstat(sp.dump))) + end +end + +local function for_stockpiles(opts, fn) + if not opts.sp then + local selected_sp = dfhack.gui.getSelectedStockpile() + if not selected_sp then qerror('please specify or select a stockpile') end + fn(selected_sp.stockpile_number) + return + end + for _,sp in ipairs(argparse.stringList(opts.sp)) do + fn(sp) + end +end + +local function do_add_stockpile_config(features, opts) + for_stockpiles(opts, function(sp) + local configs = logistics_getStockpileConfigs(tonumber(sp) or sp) + if not configs then + dfhack.printerr('invalid stockpile: '..sp) + else + for _,config in ipairs(configs) do + logistics_setStockpileConfig(config.stockpile_number, + features.melt or config.melt, + features.trade or config.trade, + features.dump or config.dump) + end + end + end) +end + +local function do_clear_stockpile_config(all, opts) + if all then + logistics_clearAllStockpileConfigs() + return + end + for_stockpiles(opts, function(sp) + logistics_clearStockpileConfig(tonumber(sp) or sp) + end) +end + +local function process_args(opts, args) + if args[1] == 'help' then + opts.help = true + return + end + + return argparse.processArgsGetopt(args, { + {'h', 'help', handler=function() opts.help = true end}, + {'s', 'stockpile', hasArg=true, handler=function(arg) opts.sp = arg end}, + }) +end + +function parse_commandline(args) + local opts = {} + local positionals = process_args(opts, args) + + if opts.help or not positionals then + return false + end + + local command = table.remove(positionals, 1) + if not command or command == 'status' then + print_status() + elseif command == 'now' then + logistics_cycle() + elseif command == 'add' then + do_add_stockpile_config(utils.invert(positionals), opts) + elseif command == 'clear' then + do_clear_stockpile_config(utils.invert(positionals).all, opts) + else + return false + end + + return true +end + +return _ENV diff --git a/plugins/lua/stockpiles.lua b/plugins/lua/stockpiles.lua index 3e127de4d..901a65000 100644 --- a/plugins/lua/stockpiles.lua +++ b/plugins/lua/stockpiles.lua @@ -79,6 +79,7 @@ local included_elements = { general=2, categories=4, types=8, + features=16, } function export_stockpile(name, opts) @@ -87,9 +88,7 @@ function export_stockpile(name, opts) local includedElements = 0 for _,inc in ipairs(opts.includes) do - if included_elements[inc] then - includedElements = includedElements | included_elements[inc] - end + includedElements = includedElements | included_elements[inc] end if includedElements == 0 then @@ -124,12 +123,10 @@ function import_route(name, route_id, stop_id, mode, filters) stockpiles_route_import(name, route_id, stop_id, mode, table.concat(filters or {}, ',')) end -local valid_includes = {general=true, categories=true, types=true} - local function parse_include(arg) local includes = argparse.stringList(arg, 'include') for _,v in ipairs(includes) do - if not valid_includes[v] then + if not included_elements[v] then qerror(('invalid included element: "%s"'):format(v)) end end diff --git a/plugins/stockpiles/OrganicMatLookup.cpp b/plugins/stockpiles/OrganicMatLookup.cpp index a9e2210c9..91294809b 100644 --- a/plugins/stockpiles/OrganicMatLookup.cpp +++ b/plugins/stockpiles/OrganicMatLookup.cpp @@ -11,8 +11,9 @@ using namespace DFHack; using namespace df::enums; using df::global::world; -namespace DFHack { - DBG_EXTERN(stockpiles, log); +namespace DFHack +{ +DBG_EXTERN(stockpiles, log); } /** diff --git a/plugins/stockpiles/StockpileSerializer.cpp b/plugins/stockpiles/StockpileSerializer.cpp index b650ec618..b030cd873 100644 --- a/plugins/stockpiles/StockpileSerializer.cpp +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -42,8 +42,9 @@ using df::global::world; using std::placeholders::_1; -namespace DFHack { - DBG_EXTERN(stockpiles, log); +namespace DFHack +{ +DBG_EXTERN(stockpiles, log); } static struct OtherMatsFurniture { @@ -905,6 +906,28 @@ void StockpileSerializer::read_general(DeserializeMode mode) { mPile->use_links_only); } +void StockpileSerializer::write_features() { + DEBUG(log).print("writing feature settings\n"); + mBuffer.set_melt(mPile->use_links_only); + mBuffer.set_trade(mPile->settings.allow_inorganic); + mBuffer.set_dump(mPile->settings.allow_organic); +} + +void StockpileSerializer::read_features(DeserializeMode mode) { + read_elem("use_links_only", mode, + std::bind(&StockpileSettings::has_use_links_only, mBuffer), + std::bind(&StockpileSettings::use_links_only, mBuffer), + mPile->use_links_only); + read_elem("allow_inorganic", mode, + std::bind(&StockpileSettings::has_allow_inorganic, mBuffer), + std::bind(&StockpileSettings::allow_inorganic, mBuffer), + mPile->settings.allow_inorganic); + read_elem("allow_organic", mode, + std::bind(&StockpileSettings::has_allow_organic, mBuffer), + std::bind(&StockpileSettings::allow_organic, mBuffer), + mPile->settings.allow_organic); +} + static bool ammo_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid() && mi.material && mi.material->flags.is_set(material_flags::IS_METAL); } diff --git a/plugins/stockpiles/StockpileSerializer.h b/plugins/stockpiles/StockpileSerializer.h index 4798fba5e..70e8909da 100644 --- a/plugins/stockpiles/StockpileSerializer.h +++ b/plugins/stockpiles/StockpileSerializer.h @@ -168,4 +168,6 @@ private: void write_containers(); void read_containers(DeserializeMode mode); + void write_features(); + void read_features(DeserializeMode mode); }; diff --git a/plugins/stockpiles/proto/stockpiles.proto b/plugins/stockpiles/proto/stockpiles.proto index 681e7d927..0ad89e143 100644 --- a/plugins/stockpiles/proto/stockpiles.proto +++ b/plugins/stockpiles/proto/stockpiles.proto @@ -183,6 +183,11 @@ message StockpileSettings { optional WeaponsSet weapons = 16; optional WoodSet wood = 15; + // DFHack features + optional bool melt = 27; + optional bool trade = 28; + optional bool dump = 29; + // deprecated optional bool corpses = 24; // not marked as deprecated since we still read it optional OreSet ore = 7 [deprecated=true]; diff --git a/plugins/stockpiles/stockpiles.cpp b/plugins/stockpiles/stockpiles.cpp index de57598c4..23ceb849b 100644 --- a/plugins/stockpiles/stockpiles.cpp +++ b/plugins/stockpiles/stockpiles.cpp @@ -11,9 +11,6 @@ #include "df/hauling_route.h" #include "df/hauling_stop.h" -#include -#include - using std::string; using std::vector; @@ -23,14 +20,15 @@ DFHACK_PLUGIN("stockpiles"); REQUIRE_GLOBAL(world); -namespace DFHack { - DBG_DECLARE(stockpiles, log, DebugCategory::LINFO); +namespace DFHack +{ +DBG_DECLARE(stockpiles, log, DebugCategory::LINFO); } -static command_result do_command(color_ostream &out, vector ¶meters); +static command_result do_command(color_ostream& out, vector& parameters); -DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { - DEBUG(log,out).print("initializing %s\n", plugin_name); +DFhackCExport command_result plugin_init(color_ostream &out, vector &commands) { + DEBUG(log, out).print("initializing %s\n", plugin_name); commands.push_back(PluginCommand( plugin_name, From 8eb622633619d97299094b83f15a39b9387318f6 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 22 Apr 2023 14:42:25 -0700 Subject: [PATCH 02/24] remove redundant 'virtual' attributes --- plugins/logistics.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/logistics.cpp b/plugins/logistics.cpp index 77b462bbb..495dc9305 100644 --- a/plugins/logistics.cpp +++ b/plugins/logistics.cpp @@ -252,11 +252,11 @@ public: MeltStockProcessor(int32_t stockpile_number, bool enabled, ProcessorStats &stats) : StockProcessor("melt", stockpile_number, enabled, stats) { } - virtual 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; } - virtual bool can_designate(color_ostream &out, df::item *item) override { + bool can_designate(color_ostream &out, df::item *item) override { MaterialInfo mat(item); if (mat.getCraftClass() != df::craft_material_class::Metal) return false; @@ -290,7 +290,7 @@ public: return true; } - virtual bool designate(color_ostream &out, df::item *item) override { + bool designate(color_ostream &out, df::item *item) override { insert_into_vector(world->items.other.ANY_MELT_DESIGNATED, &df::item::id, item); item->flags.bits.melt = 1; return true; @@ -303,17 +303,17 @@ public: : StockProcessor("trade", stockpile_number, enabled && get_active_trade_depot(), stats), depot(get_active_trade_depot()) { } - virtual bool is_designated(color_ostream& out, df::item* item) override { + bool is_designated(color_ostream& out, df::item* item) override { auto ref = Items::getSpecificRef(item, df::specific_ref_type::JOB); return ref && ref->data.job && ref->data.job->job_type == df::job_type::BringItemToDepot; } - virtual bool can_designate(color_ostream& out, df::item* item) override { + bool can_designate(color_ostream& out, df::item* item) override { return Items::canTradeWithContents(item); } - virtual bool designate(color_ostream& out, df::item* item) override { + bool designate(color_ostream& out, df::item* item) override { if (!depot) return false; @@ -383,15 +383,15 @@ public: DumpStockProcessor(int32_t stockpile_number, bool enabled, ProcessorStats& stats) : StockProcessor("dump", stockpile_number, enabled, stats) { } - virtual bool is_designated(color_ostream& out, df::item* item) override { + bool is_designated(color_ostream& out, df::item* item) override { return item->flags.bits.dump; } - virtual bool can_designate(color_ostream& out, df::item* item) override { + bool can_designate(color_ostream& out, df::item* item) override { return true; } - virtual bool designate(color_ostream& out, df::item* item) override { + bool designate(color_ostream& out, df::item* item) override { item->flags.bits.dump = true; return true; } From 2b2d03b9b18b948d2926a5153a69fdfe0f107946 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 22 Apr 2023 15:05:25 -0700 Subject: [PATCH 03/24] calculate and display global stats --- plugins/logistics.cpp | 33 ++++++++++++++++++++++++++++++++ plugins/lua/logistics.lua | 40 +++++++++++++++++++++++---------------- 2 files changed, 57 insertions(+), 16 deletions(-) diff --git a/plugins/logistics.cpp b/plugins/logistics.cpp index 495dc9305..d4c4cef53 100644 --- a/plugins/logistics.cpp +++ b/plugins/logistics.cpp @@ -663,6 +663,38 @@ static void logistics_clearAllStockpileConfigs(color_ostream &out) { watched_stockpiles.clear(); } +static int logistics_getGlobalCounts(lua_State *L) { + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + DEBUG(status,*out).print("entering logistics_getGlobalCounts\n"); + + size_t num_melt = df::global::world->items.other.ANY_MELT_DESIGNATED.size(); + + size_t num_trade = 0; + for (auto link = world->jobs.list.next; link; link = link->next) { + df::job *job = link->item; + if (job->job_type == df::job_type::BringItemToDepot) + ++num_trade; + } + + size_t num_dump = 0; + for (auto item : world->items.other.IN_PLAY) { + if (item->flags.bits.dump) + ++num_dump; + } + + unordered_map results; + results.emplace("total_melt", num_melt); + results.emplace("total_trade", num_trade); + results.emplace("total_dump", num_dump); + Lua::Push(L, results); + + TRACE(cycle, *out).print("exit logistics_getGlobalCounts\n"); + + return 1; +} + DFHACK_PLUGIN_LUA_FUNCTIONS{ DFHACK_LUA_FUNCTION(logistics_cycle), DFHACK_LUA_FUNCTION(logistics_setStockpileConfig), @@ -673,4 +705,5 @@ DFHACK_PLUGIN_LUA_COMMANDS{ DFHACK_LUA_COMMAND(logistics_getStockpileData), DFHACK_LUA_COMMAND(logistics_getStockpileConfigs), DFHACK_LUA_COMMAND(logistics_clearStockpileConfig), + DFHACK_LUA_COMMAND(logistics_getGlobalCounts), DFHACK_LUA_END}; diff --git a/plugins/lua/logistics.lua b/plugins/lua/logistics.lua index 806d1aa97..4d3ff00be 100644 --- a/plugins/lua/logistics.lua +++ b/plugins/lua/logistics.lua @@ -34,22 +34,7 @@ function getStockpileData() return data end -local function print_status() - print(('logistics is %sactively monitoring stockpiles and marking items') - :format(isEnabled() and '' or 'not ')) - - if df.global.gamemode ~= df.game_mode.DWARF or not dfhack.isMapLoaded() then - return - end - - local data = getStockpileData() - - print() - if not data[1] then - print 'No stockpiles defined -- go make some!' - return - end - +local function print_stockpile_data(data) local name_len = 12 for _,sp in ipairs(data) do name_len = math.min(40, math.max(name_len, #sp.name)) @@ -69,6 +54,29 @@ local function print_status() end end +local function print_status() + print(('logistics is %sactively monitoring stockpiles and marking items') + :format(isEnabled() and '' or 'not ')) + + if df.global.gamemode ~= df.game_mode.DWARF or not dfhack.isMapLoaded() then + return + end + + local data = getStockpileData() + print() + if not data[1] then + print 'No stockpiles defined -- go make some!' + else + print_stockpile_data(data) + end + + local global_stats = logistics_getGlobalCounts() + print() + print(('Total items marked for melting: %5d'):format(global_stats.total_melt)) + print(('Total items marked for trading: %5d'):format(global_stats.total_trade)) + print(('Total items marked for dumping: %5d'):format(global_stats.total_dump)) +end + local function for_stockpiles(opts, fn) if not opts.sp then local selected_sp = dfhack.gui.getSelectedStockpile() From 8ab9739cd643f260906767fd083440b282f15c28 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 22 Apr 2023 15:09:54 -0700 Subject: [PATCH 04/24] case insensitive sorting --- plugins/lua/logistics.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/lua/logistics.lua b/plugins/lua/logistics.lua index 4d3ff00be..477df728d 100644 --- a/plugins/lua/logistics.lua +++ b/plugins/lua/logistics.lua @@ -16,10 +16,10 @@ function getStockpileData() local data = {} for _,bld in ipairs(df.global.world.buildings.other.STOCKPILE) do local stockpile_number, name = bld.stockpile_number, bld.name - local sort_key = name + local sort_key = tostring(name):lower() if #name == 0 then name = ('Stockpile #%d'):format(bld.stockpile_number) - sort_key = ('Stockpile #%09d'):format(bld.stockpile_number) + sort_key = ('stockpile #%09d'):format(bld.stockpile_number) end table.insert(data, { stockpile_number=stockpile_number, From ecbc35b2921fda88ae4b13f5d7a71997b9ff3037 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 22 Apr 2023 15:20:17 -0700 Subject: [PATCH 05/24] one line per stockpile --- plugins/lua/logistics.lua | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/plugins/lua/logistics.lua b/plugins/lua/logistics.lua index 477df728d..9b14fbaac 100644 --- a/plugins/lua/logistics.lua +++ b/plugins/lua/logistics.lua @@ -35,22 +35,21 @@ function getStockpileData() end local function print_stockpile_data(data) - local name_len = 12 + local name_len = 4 for _,sp in ipairs(data) do name_len = math.min(40, math.max(name_len, #sp.name)) end print('Designated/designatable items in stockpiles:') print() - local fmt = '%6s %-' .. name_len .. 's %11s %11s %11s'; - print(fmt:format('number', 'name', 'melt', 'trade', 'dump')) + local fmt = '%6s %-' .. name_len .. 's %4s %10s %5s %11s %4s %10s'; + print(fmt:format('number', 'name', 'melt', 'melt items', 'trade', 'trade items', 'dump', 'dump items')) local function uline(len) return ('-'):rep(len) end - print(fmt:format(uline(6), uline(name_len), uline(11), uline(11), uline(11))) + print(fmt:format(uline(6), uline(name_len), uline(4), uline(10), uline(5), uline(11), uline(4), uline(10))) local function get_enab(stats) return ('[%s]'):format(stats.enabled and '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 - print(fmt:format(sp.stockpile_number, sp.name, get_enab(sp.melt), get_enab(sp.trade), get_enab(sp.dump))) - print(fmt:format('', '', get_dstat(sp.melt), get_dstat(sp.trade), get_dstat(sp.dump))) + 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))) end end From 44f8f84bba154d348161d45847a90ed4b760bbec Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 22 Apr 2023 19:03:58 -0700 Subject: [PATCH 06/24] initial stockpiles overlay --- plugins/lua/logistics.lua | 6 +- plugins/lua/stockpiles.lua | 140 +++++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 3 deletions(-) diff --git a/plugins/lua/logistics.lua b/plugins/lua/logistics.lua index 9b14fbaac..7ef000f28 100644 --- a/plugins/lua/logistics.lua +++ b/plugins/lua/logistics.lua @@ -96,9 +96,9 @@ local function do_add_stockpile_config(features, opts) else for _,config in ipairs(configs) do logistics_setStockpileConfig(config.stockpile_number, - features.melt or config.melt, - features.trade or config.trade, - features.dump or config.dump) + features.melt or config.melt == 1, + features.trade or config.trade == 1, + features.dump or config.dump == 1) end end end) diff --git a/plugins/lua/stockpiles.lua b/plugins/lua/stockpiles.lua index 901a65000..2bac9d01e 100644 --- a/plugins/lua/stockpiles.lua +++ b/plugins/lua/stockpiles.lua @@ -1,10 +1,17 @@ local _ENV = mkmodule('plugins.stockpiles') local argparse = require('argparse') +local gui = require('gui') +local logistics = require('plugins.logistics') +local overlay = require('plugins.overlay') +local widgets = require('gui.widgets') local STOCKPILES_DIR = "dfhack-config/stockpiles"; local STOCKPILES_LIBRARY_DIR = "hack/data/stockpiles"; +-------------------- +-- plugin logic +-------------------- local function get_sp_name(name, num) if #name > 0 then return name end return ('Stockpile %d'):format(num) @@ -189,4 +196,137 @@ function parse_commandline(args) return true end +-------------------- +-- StockpilesOverlay +-------------------- + +StockpilesOverlay = defclass(StockpilesOverlay, overlay.OverlayWidget) +StockpilesOverlay.ATTRS{ + default_pos = {x = 53, y = -6}, + default_enabled = true, + viewscreens = 'dwarfmode/Some/Stockpile', + frame = {w = 27, h = 10}, +} + +function StockpilesOverlay:init() + self.minimized = false + + local main_panel = widgets.Panel{ + frame = {t = 0, l = 0, r = 0, b = 0}, + frame_style = gui.MEDIUM_FRAME, + frame_background = gui.CLEAR_PEN, + visible = function() return not self.minimized end, + subviews = { + widgets.HotkeyLabel{ + frame = {t = 0, l = 0}, + label = 'apply settings', + key = 'CUSTOM_CTRL_I', + auto_width = true, + on_activate = do_import, + }, + widgets.HotkeyLabel{ + frame = {t = 1, l = 0}, + label = 'export settings', + key = 'CUSTOM_CTRL_E', + auto_width = true, + on_activate = do_export, + }, + widgets.Label{ + frame = {t = 3, l = 0}, + text = { + 'Designate items brought', NEWLINE, + 'to this stockpile for:' + }, + }, + widgets.ToggleHotkeyLabel{ + view_id = 'melt', + frame = {t = 5, l = 2}, + label = 'melting', + key = 'CUSTOM_CTRL_M', + on_change = self:callback('toggleLogisticsFeature', 'melt'), + }, + widgets.ToggleHotkeyLabel{ + view_id = 'trade', + frame = {t = 6, l = 2}, + label = 'trading', + key = 'CUSTOM_CTRL_T', + on_change = self:callback('toggleLogisticsFeature', 'trade'), + }, + widgets.ToggleHotkeyLabel{ + view_id = 'dump', + frame = {t = 7, l = 2}, + label = 'dumping', + key = 'CUSTOM_CTRL_D', + on_change = self:callback('toggleLogisticsFeature', 'dump'), + }, + }, + } + + local minimized_panel = widgets.Panel{ + frame = {t = 0, r = 0, w = 3, h = 1}, + subviews = { + widgets.Label{ + frame = {t = 0, l = 0, w = 1, h = 1}, + text = '[', + text_pen = COLOR_RED, + visible = function() return self.minimized end, + }, + widgets.Label{ + frame = {t = 0, l = 1, w = 1, h = 1}, + text = {{text = function() return self.minimized and string.char(31) or string.char(30) end}}, + text_pen = dfhack.pen.parse{fg = COLOR_BLACK, bg = COLOR_GREY}, + text_hpen = dfhack.pen.parse{fg = COLOR_BLACK, bg = COLOR_WHITE}, + on_click = function() self.minimized = not self.minimized end, + }, + widgets.Label{ + frame = {t = 0, r = 0, w = 1, h = 1}, + text = ']', + text_pen = COLOR_RED, + visible = function() return self.minimized end, + }, + }, + } + + self:addviews{ + main_panel, + minimized_panel, + } +end + +function StockpilesOverlay:overlay_onupdate() + self.cur_stockpile = nil +end + +function StockpilesOverlay:onRenderFrame() + local sp = dfhack.gui.getSelectedStockpile() + if self.cur_stockpile ~= sp then + local config = logistics.logistics_getStockpileConfigs(sp.stockpile_number)[1] + self.subviews.melt:setOption(config.melt == 1) + self.subviews.trade:setOption(config.trade == 1) + self.subviews.dump:setOption(config.dump == 1) + self.cur_stockpile = sp + end +end + +function StockpilesOverlay:toggleLogisticsFeature(feature) + local sp = dfhack.gui.getSelectedStockpile() + local config = logistics.logistics_getStockpileConfigs(sp.stockpile_number)[1] + -- logical xor + logistics.logistics_setStockpileConfig(config.stockpile_number, + (feature == 'melt') ~= (config.melt == 1), + (feature == 'trade') ~= (config.trade == 1), + (feature == 'dump') ~= (config.dump == 1)) +end + +function StockpilesOverlay:onInput(keys) + if keys.CUSTOM_ALT_M then + self.minimized = not self.minimized + return true + end + return StockpilesOverlay.super.onInput(self, keys) +end + +OVERLAY_WIDGETS = { + overlay = StockpilesOverlay, +} return _ENV From 7beac6b4c207ecfe5773227d7bcfd5fbdb4bab39 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 22 Apr 2023 23:13:50 -0700 Subject: [PATCH 07/24] add some hover action to the overlay --- plugins/lua/stockpiles.lua | 324 +++++++++++++++++++++++-------------- 1 file changed, 202 insertions(+), 122 deletions(-) diff --git a/plugins/lua/stockpiles.lua b/plugins/lua/stockpiles.lua index 2bac9d01e..56ec88b08 100644 --- a/plugins/lua/stockpiles.lua +++ b/plugins/lua/stockpiles.lua @@ -6,12 +6,13 @@ local logistics = require('plugins.logistics') local overlay = require('plugins.overlay') local widgets = require('gui.widgets') -local STOCKPILES_DIR = "dfhack-config/stockpiles"; -local STOCKPILES_LIBRARY_DIR = "hack/data/stockpiles"; +local STOCKPILES_DIR = 'dfhack-config/stockpiles'; +local STOCKPILES_LIBRARY_DIR = 'hack/data/stockpiles'; -------------------- -- plugin logic -------------------- + local function get_sp_name(name, num) if #name > 0 then return name end return ('Stockpile %d'):format(num) @@ -26,7 +27,7 @@ local function print_status() print(STATUS_FMT:format('ID', 'Name')) print(STATUS_FMT:format('------', '----------')) end - for _,sp in ipairs(sps) do + for _, sp in ipairs(sps) do print(STATUS_FMT:format(sp.id, get_sp_name(sp.name, sp.stockpile_number))) end end @@ -38,16 +39,14 @@ local function list_dir(path, prefix, filters) return end local normalized_filters = {} - for _,filter in ipairs(filters or {}) do - table.insert(normalized_filters, filter:lower()) - end - for _,v in ipairs(paths) do + for _, filter in ipairs(filters or {}) do table.insert(normalized_filters, filter:lower()) end + for _, v in ipairs(paths) do local normalized_path = prefix .. v.path:lower() if v.isdir or not normalized_path:endswith('.dfstock') then goto continue end normalized_path = normalized_path:sub(1, -9) if #normalized_filters > 0 then local matched = false - for _,filter in ipairs(normalized_filters) do + for _, filter in ipairs(normalized_filters) do if normalized_path:find(filter, 1, true) then matched = true break @@ -66,9 +65,7 @@ local function list_settings_files(filters) end local function assert_safe_name(name) - if not name or #name == 0 then - qerror('name missing or empty') - end + if not name or #name == 0 then qerror('name missing or empty') end if name:find('[^%w._]') then qerror('name can only contain numbers, letters, periods, and underscores') end @@ -81,27 +78,19 @@ local function get_sp_id(opts) return nil end -local included_elements = { - containers=1, - general=2, - categories=4, - types=8, - features=16, -} +local included_elements = {containers=1, general=2, categories=4, types=8, features=16} function export_stockpile(name, opts) assert_safe_name(name) name = STOCKPILES_DIR .. '/' .. name local includedElements = 0 - for _,inc in ipairs(opts.includes) do + for _, inc in ipairs(opts.includes) do includedElements = includedElements | included_elements[inc] end if includedElements == 0 then - for _,v in pairs(included_elements) do - includedElements = includedElements | v - end + for _, v in pairs(included_elements) do includedElements = includedElements | v end end stockpiles_export(name, get_sp_id(opts), includedElements) @@ -132,10 +121,8 @@ end local function parse_include(arg) local includes = argparse.stringList(arg, 'include') - for _,v in ipairs(includes) do - if not included_elements[v] then - qerror(('invalid included element: "%s"'):format(v)) - end + for _, v in ipairs(includes) do + if not included_elements[v] then qerror(('invalid included element: "%s"'):format(v)) end end return includes end @@ -143,9 +130,7 @@ end local valid_modes = {set=true, enable=true, disable=true} local function parse_mode(arg) - if not valid_modes[arg] then - qerror(('invalid mode: "%s"'):format(arg)) - end + if not valid_modes[arg] then qerror(('invalid mode: "%s"'):format(arg)) end return arg end @@ -160,25 +145,49 @@ local function process_args(opts, args) opts.filters = {} return argparse.processArgsGetopt(args, { - {'f', 'filter', hasArg=true, - handler=function(arg) opts.filters = argparse.stringList(arg) end}, - {'h', 'help', handler=function() opts.help = true end}, - {'i', 'include', hasArg=true, - handler=function(arg) opts.includes = parse_include(arg) end}, - {'m', 'mode', hasArg=true, - handler=function(arg) opts.mode = parse_mode(arg) end}, - {'s', 'stockpile', hasArg=true, - handler=function(arg) opts.id = argparse.nonnegativeInt(arg, 'stockpile') end}, - }) + { + 'h', + 'help', + handler=function() + opts.help = true + end, + }, { + 'm', + 'mode', + hasArg=true, + handler=function(arg) + opts.mode = parse_mode(arg) + end, + }, { + 'f', + 'filter', + hasArg=true, + handler=function(arg) + opts.filters = argparse.stringList(arg) + end, + }, { + 'i', + 'include', + hasArg=true, + handler=function(arg) + opts.includes = parse_include(arg) + end, + }, { + 's', + 'stockpile', + hasArg=true, + handler=function(arg) + opts.id = argparse.nonnegativeInt(arg, 'stockpile') + end, + }, + }) end function parse_commandline(args) local opts = {} local positionals = process_args(opts, args) - if opts.help or not positionals then - return false - end + if opts.help or not positionals then return false end local command = table.remove(positionals, 1) if not command or command == 'status' then @@ -196,115 +205,182 @@ function parse_commandline(args) return true end +-- +-- dialogs -------------------- + +StockpilesExport = defclass(StockpilesExport, widgets.Window) +StockpilesExport.ATTRS{ + frame_title='My Window', + frame={w=50, h=45}, + resizable=true, + resize_min={w=50, h=20}, -- try to allow users to shrink your windows +} + +function StockpilesExport:init() + self:addviews{ + -- add subview widgets here + } +end + +function StockpilesExport:onInput(keys) + -- if required +end + +StockpilesExportScreen = defclass(StockpilesExportScreen, gui.ZScreenModal) +StockpilesExportScreen.ATTRS{focus_path='stockpiles/export'} + +function StockpilesExportScreen:init() + self:addviews{StockpilesExport{}} +end + +function StockpilesExportScreen:onDismiss() + export_view = nil +end + +local function do_export() + export_view = export_view and export_view:raise() or StockpilesExportScreen{}:show() +end + +-- -- StockpilesOverlay -------------------- StockpilesOverlay = defclass(StockpilesOverlay, overlay.OverlayWidget) StockpilesOverlay.ATTRS{ - default_pos = {x = 53, y = -6}, - default_enabled = true, - viewscreens = 'dwarfmode/Some/Stockpile', - frame = {w = 27, h = 10}, + default_pos={x=53, y=-6}, + default_enabled=true, + viewscreens='dwarfmode/Some/Stockpile', + frame={w=27, h=11}, } function StockpilesOverlay:init() self.minimized = false - - local main_panel = widgets.Panel{ - frame = {t = 0, l = 0, r = 0, b = 0}, - frame_style = gui.MEDIUM_FRAME, - frame_background = gui.CLEAR_PEN, - visible = function() return not self.minimized end, - subviews = { + self.hovered = false + + local main_panel = widgets.ResizingPanel{ + view_id='main', + frame={t=0, l=0, r=0}, + frame_style=gui.MEDIUM_FRAME, + frame_background=gui.CLEAR_PEN, + autoarrange_subviews=true, + visible=function() + return not self.minimized + end, + subviews={ widgets.HotkeyLabel{ - frame = {t = 0, l = 0}, - label = 'apply settings', - key = 'CUSTOM_CTRL_I', - auto_width = true, - on_activate = do_import, - }, - widgets.HotkeyLabel{ - frame = {t = 1, l = 0}, - label = 'export settings', - key = 'CUSTOM_CTRL_E', - auto_width = true, - on_activate = do_export, - }, - widgets.Label{ - frame = {t = 3, l = 0}, - text = { - 'Designate items brought', NEWLINE, - 'to this stockpile for:' + frame={t=0, l=0}, + label='apply settings', + key='CUSTOM_CTRL_I', + on_activate=do_import, + }, widgets.HotkeyLabel{ + frame={t=1, l=0}, + label='export settings', + key='CUSTOM_CTRL_E', + on_activate=do_export, + }, widgets.Panel{ + frame={t=2, h=4}, + subviews={ + widgets.Label{ + frame={t=1, l=0, h=2}, + auto_height=false, + text={'Designate items brought', NEWLINE, 'to this stockpile for:'}, + }, }, - }, - widgets.ToggleHotkeyLabel{ - view_id = 'melt', - frame = {t = 5, l = 2}, - label = 'melting', - key = 'CUSTOM_CTRL_M', - on_change = self:callback('toggleLogisticsFeature', 'melt'), - }, - widgets.ToggleHotkeyLabel{ - view_id = 'trade', - frame = {t = 6, l = 2}, - label = 'trading', - key = 'CUSTOM_CTRL_T', - on_change = self:callback('toggleLogisticsFeature', 'trade'), - }, - widgets.ToggleHotkeyLabel{ - view_id = 'dump', - frame = {t = 7, l = 2}, - label = 'dumping', - key = 'CUSTOM_CTRL_D', - on_change = self:callback('toggleLogisticsFeature', 'dump'), + visible=function() + return self.hovered or self.subviews.melt:getOptionValue() or + self.subviews.trade:getOptionValue() or + self.subviews.dump:getOptionValue() + end, + }, widgets.ToggleHotkeyLabel{ + view_id='melt', + frame={t=6, l=2}, + label='melting', + key='CUSTOM_CTRL_M', + on_change=self:callback('toggleLogisticsFeature', 'melt'), + visible=function() + return self.hovered or self.subviews.melt:getOptionValue() + end, + }, widgets.ToggleHotkeyLabel{ + view_id='trade', + frame={t=7, l=2}, + label='trading', + key='CUSTOM_CTRL_T', + on_change=self:callback('toggleLogisticsFeature', 'trade'), + visible=function() + return self.hovered or self.subviews.trade:getOptionValue() + end, + }, widgets.ToggleHotkeyLabel{ + view_id='dump', + frame={t=8, l=2}, + label='dumping', + key='CUSTOM_CTRL_D', + on_change=self:callback('toggleLogisticsFeature', 'dump'), + visible=function() + return self.hovered or self.subviews.dump:getOptionValue() + end, }, }, } local minimized_panel = widgets.Panel{ - frame = {t = 0, r = 0, w = 3, h = 1}, - subviews = { - widgets.Label{ - frame = {t = 0, l = 0, w = 1, h = 1}, - text = '[', - text_pen = COLOR_RED, - visible = function() return self.minimized end, - }, + frame={t=0, r=0, w=3, h=1}, + subviews={ widgets.Label{ - frame = {t = 0, l = 1, w = 1, h = 1}, - text = {{text = function() return self.minimized and string.char(31) or string.char(30) end}}, - text_pen = dfhack.pen.parse{fg = COLOR_BLACK, bg = COLOR_GREY}, - text_hpen = dfhack.pen.parse{fg = COLOR_BLACK, bg = COLOR_WHITE}, - on_click = function() self.minimized = not self.minimized end, - }, - widgets.Label{ - frame = {t = 0, r = 0, w = 1, h = 1}, - text = ']', - text_pen = COLOR_RED, - visible = function() return self.minimized end, + frame={t=0, l=0, w=1, h=1}, + text='[', + text_pen=COLOR_RED, + visible=function() + return self.minimized + end, + }, widgets.Label{ + frame={t=0, l=1, w=1, h=1}, + text={ + { + text=function() + return self.minimized and string.char(31) or string.char(30) + end, + }, + }, + text_pen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_GREY}, + text_hpen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_WHITE}, + on_click=self:callback('toggleMinimized'), + }, widgets.Label{ + frame={t=0, r=0, w=1, h=1}, + text=']', + text_pen=COLOR_RED, + visible=function() + return self.minimized + end, }, }, } - self:addviews{ - main_panel, - minimized_panel, - } + self:addviews{main_panel, minimized_panel} end function StockpilesOverlay:overlay_onupdate() + -- periodically pick up changes made from other interfaces self.cur_stockpile = nil end function StockpilesOverlay:onRenderFrame() local sp = dfhack.gui.getSelectedStockpile() + local sp_updated = false if self.cur_stockpile ~= sp then local config = logistics.logistics_getStockpileConfigs(sp.stockpile_number)[1] self.subviews.melt:setOption(config.melt == 1) self.subviews.trade:setOption(config.trade == 1) self.subviews.dump:setOption(config.dump == 1) self.cur_stockpile = sp + sp_updated = true + end + local prev_hovered = self.hovered + self.hovered = not not (self.hovered and self:getMouseFramePos() or + self.subviews.main:getMousePos()) + if self.hovered ~= prev_hovered or sp_updated then + self:updateLayout() + df.global.gps.force_full_display_count = 1 end end @@ -313,20 +389,24 @@ function StockpilesOverlay:toggleLogisticsFeature(feature) local config = logistics.logistics_getStockpileConfigs(sp.stockpile_number)[1] -- logical xor logistics.logistics_setStockpileConfig(config.stockpile_number, - (feature == 'melt') ~= (config.melt == 1), - (feature == 'trade') ~= (config.trade == 1), - (feature == 'dump') ~= (config.dump == 1)) + (feature == 'melt') ~= (config.melt == 1), (feature == 'trade') ~= (config.trade == 1), + (feature == 'dump') ~= (config.dump == 1)) + self.cur_stockpile = nil +end + +function StockpilesOverlay:toggleMinimized() + self.minimized = not self.minimized + self.cur_stockpile = nil end function StockpilesOverlay:onInput(keys) if keys.CUSTOM_ALT_M then - self.minimized = not self.minimized + self:toggleMinimized() return true end return StockpilesOverlay.super.onInput(self, keys) end -OVERLAY_WIDGETS = { - overlay = StockpilesOverlay, -} +OVERLAY_WIDGETS = {overlay=StockpilesOverlay} + return _ENV From 1231006f6ce74f98c1fa93fa3f0a43ec2b3e0fe5 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 23 Apr 2023 16:09:31 -0700 Subject: [PATCH 08/24] start of minimize button --- plugins/lua/stockpiles.lua | 52 ++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/plugins/lua/stockpiles.lua b/plugins/lua/stockpiles.lua index 56ec88b08..9c5f5dafa 100644 --- a/plugins/lua/stockpiles.lua +++ b/plugins/lua/stockpiles.lua @@ -205,21 +205,22 @@ function parse_commandline(args) return true end --- +-------------------- -- dialogs -------------------- StockpilesExport = defclass(StockpilesExport, widgets.Window) StockpilesExport.ATTRS{ - frame_title='My Window', + frame_title='Export stockpile settings', frame={w=50, h=45}, resizable=true, - resize_min={w=50, h=20}, -- try to allow users to shrink your windows + resize_min={w=50, h=20}, } function StockpilesExport:init() self:addviews{ - -- add subview widgets here + widgets.EditField{}, widgets.Label{frame={}, text='Include which elements?'}, + widgets.ToggleHotkeyLabel{}, } end @@ -242,10 +243,51 @@ local function do_export() export_view = export_view and export_view:raise() or StockpilesExportScreen{}:show() end --- +-------------------- -- StockpilesOverlay -------------------- +MinimizeButton = defclass(MinimizeButton, widgets.Panel) +MinimizeButton.ATTRS{ + label_pos='left', + get_minimized_fn=DEFAULT_NIL, + on_click=DEFAULT_NIL, +} + +function MinimizeButton:init() + local show_label, hide_label = 'show', 'hide' + local label_width = math.max(#show_label, #hide_label) + + self:addviews{ + widgets.Label{ + frame={t=0, l=0, w=1, h=1}, + text='['..string.char(30)..']', + text_pen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_LIGHTRED}, + text_hpen=dfhack.pen.parse{fg=COLOR_WHITE, bg=COLOR_RED}, + visible=self.get_minimized_fn, + }, widgets.Label{ + frame={t=0, l=1, w=1, h=1}, + text={'[', + { + text=function() + return self.get_minimized_fn() and string.char(31) or string.char(30) + end, + },']', + }, + text_pen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_GREY}, + text_hpen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_WHITE}, + on_click=self:callback('toggleMinimized'), + }, widgets.Label{ + frame={t=0, r=0, w=1, h=1}, + text=']', + text_pen=COLOR_RED, + visible=function() + return self.minimized + end, + }, + } +end + StockpilesOverlay = defclass(StockpilesOverlay, overlay.OverlayWidget) StockpilesOverlay.ATTRS{ default_pos={x=53, y=-6}, From c6d9a426dd6ee909721873dd18b5986cb1b0a77f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 23 Apr 2023 16:10:54 -0700 Subject: [PATCH 09/24] wip --- plugins/lua/stockpiles.lua | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/plugins/lua/stockpiles.lua b/plugins/lua/stockpiles.lua index 9c5f5dafa..abf305fe8 100644 --- a/plugins/lua/stockpiles.lua +++ b/plugins/lua/stockpiles.lua @@ -248,11 +248,7 @@ end -------------------- MinimizeButton = defclass(MinimizeButton, widgets.Panel) -MinimizeButton.ATTRS{ - label_pos='left', - get_minimized_fn=DEFAULT_NIL, - on_click=DEFAULT_NIL, -} +MinimizeButton.ATTRS{label_pos='left', get_minimized_fn=DEFAULT_NIL, on_click=DEFAULT_NIL} function MinimizeButton:init() local show_label, hide_label = 'show', 'hide' @@ -261,22 +257,22 @@ function MinimizeButton:init() self:addviews{ widgets.Label{ frame={t=0, l=0, w=1, h=1}, - text='['..string.char(30)..']', + text='[' .. string.char(30) .. ']', text_pen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_LIGHTRED}, text_hpen=dfhack.pen.parse{fg=COLOR_WHITE, bg=COLOR_RED}, visible=self.get_minimized_fn, }, widgets.Label{ frame={t=0, l=1, w=1, h=1}, - text={'[', - { + text={{width=label_width}, + '[', { text=function() return self.get_minimized_fn() and string.char(31) or string.char(30) end, - },']', + }, ']', }, - text_pen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_GREY}, - text_hpen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_WHITE}, - on_click=self:callback('toggleMinimized'), + text_pen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_LIGHTRED}, + text_hpen=dfhack.pen.parse{fg=COLOR_WHITE, bg=COLOR_RED}, + on_click=self.on_click, }, widgets.Label{ frame={t=0, r=0, w=1, h=1}, text=']', From 2884abf2a75578dfea8a750928e84717827e7a13 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 23 Apr 2023 19:01:32 -0700 Subject: [PATCH 10/24] fix docs -- we do have a command --- docs/plugins/logistics.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/plugins/logistics.rst b/docs/plugins/logistics.rst index 59f71ab8e..466e806be 100644 --- a/docs/plugins/logistics.rst +++ b/docs/plugins/logistics.rst @@ -4,7 +4,6 @@ logistics .. dfhack-tool:: :summary: Automatically mark and route items in monitored stockpiles. :tags: fort productivity items stockpiles - :no-command: Commands act upon the stockpile selected in the UI unless another stockpile identifier is specified on the commandline. From a481520efd7186f43e275efc62ddf726c08ab290 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 23 Apr 2023 19:44:22 -0700 Subject: [PATCH 11/24] implement minimize button --- plugins/lua/stockpiles.lua | 117 +++++++++++++++++++------------------ 1 file changed, 60 insertions(+), 57 deletions(-) diff --git a/plugins/lua/stockpiles.lua b/plugins/lua/stockpiles.lua index abf305fe8..3db680295 100644 --- a/plugins/lua/stockpiles.lua +++ b/plugins/lua/stockpiles.lua @@ -244,46 +244,75 @@ local function do_export() end -------------------- --- StockpilesOverlay +-- MinimizeButton -------------------- -MinimizeButton = defclass(MinimizeButton, widgets.Panel) -MinimizeButton.ATTRS{label_pos='left', get_minimized_fn=DEFAULT_NIL, on_click=DEFAULT_NIL} +MinimizeButton = defclass(MinimizeButton, widgets.Widget) +MinimizeButton.ATTRS{ + label_unminimized='minimize', + label_minimized='restore', + label_pos='left', + get_minimized_fn=DEFAULT_NIL, + on_click=DEFAULT_NIL, +} function MinimizeButton:init() - local show_label, hide_label = 'show', 'hide' - local label_width = math.max(#show_label, #hide_label) + self.hovered = false + + local is_hovered = function() return self.hovered end + local is_not_hovered = function() return not self.hovered end + local get_action_symbol = function() + return self.get_minimized_fn() and string.char(31) or string.char(30) + end + local get_label = function() + local label = self.get_minimized_fn() and self.label_minimized or self.label_unminimized + return (' %s '):format(label) + end + + local hovered_text = {'[', {text=get_action_symbol}, ']'} + table.insert(hovered_text, + self.label_pos == 'left' and 1 or nil, + {text=get_label, hpen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_WHITE}}) self:addviews{ widgets.Label{ - frame={t=0, l=0, w=1, h=1}, - text='[' .. string.char(30) .. ']', + view_id='unhovered_label', + frame={t=0, r=0, w=3, h=1}, + text={'[', {text=get_action_symbol}, ']'}, text_pen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_LIGHTRED}, text_hpen=dfhack.pen.parse{fg=COLOR_WHITE, bg=COLOR_RED}, - visible=self.get_minimized_fn, + on_click=function() self.on_click() self:updateLayout() end, + visible=is_not_hovered, }, widgets.Label{ - frame={t=0, l=1, w=1, h=1}, - text={{width=label_width}, - '[', { - text=function() - return self.get_minimized_fn() and string.char(31) or string.char(30) - end, - }, ']', - }, + view_id='hovered_label', + frame={t=0, r=0, h=1}, + text=hovered_text, + auto_width=true, text_pen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_LIGHTRED}, text_hpen=dfhack.pen.parse{fg=COLOR_WHITE, bg=COLOR_RED}, - on_click=self.on_click, - }, widgets.Label{ - frame={t=0, r=0, w=1, h=1}, - text=']', - text_pen=COLOR_RED, - visible=function() - return self.minimized - end, + on_click=function() self.on_click() self:updateLayout() end, + visible=is_hovered, }, } end +function MinimizeButton:onRenderFrame() + local prev_hovered = self.hovered + if self.hovered then + self.hovered = self.subviews.hovered_label:getMousePos() + else + self.hovered = self.subviews.unhovered_label:getMousePos() + end + if self.hovered ~= prev_hovered then + self:updateLayout() + df.global.gps.force_full_display_count = 1 + end +end + +-------------------- +-- StockpilesOverlay +-------------------- + StockpilesOverlay = defclass(StockpilesOverlay, overlay.OverlayWidget) StockpilesOverlay.ATTRS{ default_pos={x=53, y=-6}, @@ -308,7 +337,7 @@ function StockpilesOverlay:init() subviews={ widgets.HotkeyLabel{ frame={t=0, l=0}, - label='apply settings', + label='import settings', key='CUSTOM_CTRL_I', on_activate=do_import, }, widgets.HotkeyLabel{ @@ -361,40 +390,14 @@ function StockpilesOverlay:init() }, } - local minimized_panel = widgets.Panel{ - frame={t=0, r=0, w=3, h=1}, - subviews={ - widgets.Label{ - frame={t=0, l=0, w=1, h=1}, - text='[', - text_pen=COLOR_RED, - visible=function() - return self.minimized - end, - }, widgets.Label{ - frame={t=0, l=1, w=1, h=1}, - text={ - { - text=function() - return self.minimized and string.char(31) or string.char(30) - end, - }, - }, - text_pen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_GREY}, - text_hpen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_WHITE}, - on_click=self:callback('toggleMinimized'), - }, widgets.Label{ - frame={t=0, r=0, w=1, h=1}, - text=']', - text_pen=COLOR_RED, - visible=function() - return self.minimized - end, - }, + self:addviews{ + main_panel, + MinimizeButton{ + frame={t=0, r=1}, + get_minimized_fn=function() return self.minimized end, + on_click=self:callback('toggleMinimized'), }, } - - self:addviews{main_panel, minimized_panel} end function StockpilesOverlay:overlay_onupdate() From 89dedd78ac13c3bb63955b48ae86767c50fe407a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 23 Apr 2023 20:00:57 -0700 Subject: [PATCH 12/24] move minimize button to the bottom --- plugins/lua/stockpiles.lua | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/plugins/lua/stockpiles.lua b/plugins/lua/stockpiles.lua index 3db680295..acbd6c5c9 100644 --- a/plugins/lua/stockpiles.lua +++ b/plugins/lua/stockpiles.lua @@ -252,6 +252,8 @@ MinimizeButton.ATTRS{ label_unminimized='minimize', label_minimized='restore', label_pos='left', + symbol_minimize=string.char(31), + symbol_restore=string.char(30), get_minimized_fn=DEFAULT_NIL, on_click=DEFAULT_NIL, } @@ -259,10 +261,12 @@ MinimizeButton.ATTRS{ function MinimizeButton:init() self.hovered = false + ensure_key(self, 'frame').h = 1 + local is_hovered = function() return self.hovered end local is_not_hovered = function() return not self.hovered end local get_action_symbol = function() - return self.get_minimized_fn() and string.char(31) or string.char(30) + return self.get_minimized_fn() and self.symbol_minimize or self.symbol_restore end local get_label = function() local label = self.get_minimized_fn() and self.label_minimized or self.label_unminimized @@ -271,13 +275,13 @@ function MinimizeButton:init() local hovered_text = {'[', {text=get_action_symbol}, ']'} table.insert(hovered_text, - self.label_pos == 'left' and 1 or nil, + self.label_pos == 'left' and 1 or #hovered_text + 1, {text=get_label, hpen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_WHITE}}) self:addviews{ widgets.Label{ view_id='unhovered_label', - frame={t=0, r=0, w=3, h=1}, + frame={t=0, w=3, h=1}, text={'[', {text=get_action_symbol}, ']'}, text_pen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_LIGHTRED}, text_hpen=dfhack.pen.parse{fg=COLOR_WHITE, bg=COLOR_RED}, @@ -285,7 +289,7 @@ function MinimizeButton:init() visible=is_not_hovered, }, widgets.Label{ view_id='hovered_label', - frame={t=0, r=0, h=1}, + frame={t=0, h=1}, text=hovered_text, auto_width=true, text_pen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_LIGHTRED}, @@ -294,6 +298,14 @@ function MinimizeButton:init() visible=is_hovered, }, } + + if self.label_pos == 'left' then + self.subviews.unhovered_label.frame.r = 0 + self.subviews.hovered_label.frame.r = 0 + else + self.subviews.unhovered_label.frame.l = 0 + self.subviews.hovered_label.frame.l = 0 + end end function MinimizeButton:onRenderFrame() @@ -327,7 +339,7 @@ function StockpilesOverlay:init() local main_panel = widgets.ResizingPanel{ view_id='main', - frame={t=0, l=0, r=0}, + frame={b=0, l=0, r=0}, frame_style=gui.MEDIUM_FRAME, frame_background=gui.CLEAR_PEN, autoarrange_subviews=true, @@ -393,7 +405,10 @@ function StockpilesOverlay:init() self:addviews{ main_panel, MinimizeButton{ - frame={t=0, r=1}, + frame={b=0, l=1}, + label_pos='right', + symbol_minimize=string.char(30), + symbol_restore=string.char(31), get_minimized_fn=function() return self.minimized end, on_click=self:callback('toggleMinimized'), }, From c8786b21a0115fbdd9412e72701df2bf1b52bfa2 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 24 Apr 2023 11:24:43 -0700 Subject: [PATCH 13/24] export dialog prototype --- plugins/lua/stockpiles.lua | 72 ++++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 19 deletions(-) diff --git a/plugins/lua/stockpiles.lua b/plugins/lua/stockpiles.lua index acbd6c5c9..3590dc1f9 100644 --- a/plugins/lua/stockpiles.lua +++ b/plugins/lua/stockpiles.lua @@ -6,8 +6,10 @@ local logistics = require('plugins.logistics') local overlay = require('plugins.overlay') local widgets = require('gui.widgets') -local STOCKPILES_DIR = 'dfhack-config/stockpiles'; -local STOCKPILES_LIBRARY_DIR = 'hack/data/stockpiles'; +local STOCKPILES_DIR = 'dfhack-config/stockpiles' +local STOCKPILES_LIBRARY_DIR = 'hack/data/stockpiles' + +local BAD_FILENAME_REGEX = '[^%w._]' -------------------- -- plugin logic @@ -66,7 +68,7 @@ end local function assert_safe_name(name) if not name or #name == 0 then qerror('name missing or empty') end - if name:find('[^%w._]') then + if name:find(BAD_FILENAME_REGEX) then qerror('name can only contain numbers, letters, periods, and underscores') end end @@ -212,20 +214,42 @@ end StockpilesExport = defclass(StockpilesExport, widgets.Window) StockpilesExport.ATTRS{ frame_title='Export stockpile settings', - frame={w=50, h=45}, + frame={w=33, h=15}, resizable=true, - resize_min={w=50, h=20}, } function StockpilesExport:init() self:addviews{ - widgets.EditField{}, widgets.Label{frame={}, text='Include which elements?'}, - widgets.ToggleHotkeyLabel{}, + widgets.EditField{ + view_id='edit', + frame={t=0, l=0, r=0}, + label_text='name: ', + on_char=function(ch) + return not ch:match(BAD_FILENAME_REGEX) + end, + }, widgets.Label{frame={t=2, l=0}, text='Include which elements?'}, + widgets.ToggleHotkeyLabel{frame={t=4, l=0}, label='General settings', initial_option=false}, + widgets.ToggleHotkeyLabel{ + frame={t=5, l=0}, + label='Container settings', + initial_option=false, + }, widgets.ToggleHotkeyLabel{frame={t=6, l=0}, label='Categories', initial_option=true}, + widgets.ToggleHotkeyLabel{frame={t=7, l=0}, label='Subtypes', initial_option=true}, + widgets.ToggleHotkeyLabel{frame={t=8, l=0}, label='DFHack features', initial_option=true}, + widgets.HotkeyLabel{ + frame={t=10, l=0}, + label='export', + key='SELECT', + enabled=function() + return #self.subviews.edit.text > 0 + end, + on_activate=self:callback('on_submit'), + }, } end -function StockpilesExport:onInput(keys) - -- if required +function StockpilesExport:on_submit(text) + self:dismiss() end StockpilesExportScreen = defclass(StockpilesExportScreen, gui.ZScreenModal) @@ -263,8 +287,12 @@ function MinimizeButton:init() ensure_key(self, 'frame').h = 1 - local is_hovered = function() return self.hovered end - local is_not_hovered = function() return not self.hovered end + local is_hovered = function() + return self.hovered + end + local is_not_hovered = function() + return not self.hovered + end local get_action_symbol = function() return self.get_minimized_fn() and self.symbol_minimize or self.symbol_restore end @@ -274,9 +302,8 @@ function MinimizeButton:init() end local hovered_text = {'[', {text=get_action_symbol}, ']'} - table.insert(hovered_text, - self.label_pos == 'left' and 1 or #hovered_text + 1, - {text=get_label, hpen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_WHITE}}) + table.insert(hovered_text, self.label_pos == 'left' and 1 or #hovered_text + 1, + {text=get_label, hpen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_WHITE}}) self:addviews{ widgets.Label{ @@ -285,7 +312,10 @@ function MinimizeButton:init() text={'[', {text=get_action_symbol}, ']'}, text_pen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_LIGHTRED}, text_hpen=dfhack.pen.parse{fg=COLOR_WHITE, bg=COLOR_RED}, - on_click=function() self.on_click() self:updateLayout() end, + on_click=function() + self.on_click() + self:updateLayout() + end, visible=is_not_hovered, }, widgets.Label{ view_id='hovered_label', @@ -294,7 +324,10 @@ function MinimizeButton:init() auto_width=true, text_pen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_LIGHTRED}, text_hpen=dfhack.pen.parse{fg=COLOR_WHITE, bg=COLOR_RED}, - on_click=function() self.on_click() self:updateLayout() end, + on_click=function() + self.on_click() + self:updateLayout() + end, visible=is_hovered, }, } @@ -403,13 +436,14 @@ function StockpilesOverlay:init() } self:addviews{ - main_panel, - MinimizeButton{ + main_panel, MinimizeButton{ frame={b=0, l=1}, label_pos='right', symbol_minimize=string.char(30), symbol_restore=string.char(31), - get_minimized_fn=function() return self.minimized end, + get_minimized_fn=function() + return self.minimized + end, on_click=self:callback('toggleMinimized'), }, } From 43fbd89c23ca8a06c713443c56b727a975c355dd Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 3 May 2023 18:01:11 -0700 Subject: [PATCH 14/24] first step at saving features --- docs/plugins/logistics.rst | 2 +- plugins/stockpiles/StockpileSerializer.cpp | 47 +++++++++++++--------- plugins/stockpiles/StockpileSerializer.h | 5 ++- plugins/stockpiles/stockpiles.cpp | 2 +- 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/docs/plugins/logistics.rst b/docs/plugins/logistics.rst index 466e806be..1b36caa21 100644 --- a/docs/plugins/logistics.rst +++ b/docs/plugins/logistics.rst @@ -3,7 +3,7 @@ logistics .. dfhack-tool:: :summary: Automatically mark and route items in monitored stockpiles. - :tags: fort productivity items stockpiles + :tags: fort auto items stockpiles Commands act upon the stockpile selected in the UI unless another stockpile identifier is specified on the commandline. diff --git a/plugins/stockpiles/StockpileSerializer.cpp b/plugins/stockpiles/StockpileSerializer.cpp index b030cd873..2908f3329 100644 --- a/plugins/stockpiles/StockpileSerializer.cpp +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -185,7 +185,7 @@ bool StockpileSettingsSerializer::serialize_to_file(const string& file, uint32_t return serialize_to_ostream(&output, includedElements); } -bool StockpileSettingsSerializer::parse_from_istream(std::istream* input, DeserializeMode mode, const vector& filters) { +bool StockpileSettingsSerializer::parse_from_istream(color_ostream &out, std::istream* input, DeserializeMode mode, const vector& filters) { if (input->fail()) return false; mBuffer.Clear(); @@ -193,18 +193,18 @@ bool StockpileSettingsSerializer::parse_from_istream(std::istream* input, Deseri const bool res = mBuffer.ParseFromZeroCopyStream(&zero_copy_input) && input->eof(); if (res) - read(mode, filters); + read(out, mode, filters); return res; } -bool StockpileSettingsSerializer::unserialize_from_file(const string& file, DeserializeMode mode, const vector& filters) { +bool StockpileSettingsSerializer::unserialize_from_file(color_ostream &out, const string& file, DeserializeMode mode, const vector& filters) { std::fstream input(file, std::ios::in | std::ios::binary); if (input.fail()) { WARN(log).print("failed to open file for reading: '%s'\n", file.c_str()); return false; } - return parse_from_istream(&input, mode, filters); + return parse_from_istream(out, &input, mode, filters); } /** @@ -768,13 +768,15 @@ void StockpileSettingsSerializer::write(uint32_t includedElements) { } void StockpileSerializer::write(uint32_t includedElements) { + if (includedElements & INCLUDED_ELEMENTS_FEATURES) + write_features(); if (includedElements & INCLUDED_ELEMENTS_CONTAINERS) write_containers(); StockpileSettingsSerializer::write(includedElements); } -void StockpileSettingsSerializer::read(DeserializeMode mode, const vector& filters) { +void StockpileSettingsSerializer::read(color_ostream &out, DeserializeMode mode, const vector& filters) { DEBUG(log).print("==READ==\n"); read_general(mode); read_ammo(mode, filters); @@ -803,7 +805,8 @@ void StockpileSettingsSerializer::read(DeserializeMode mode, const vector& filters) { +void StockpileSerializer::read(color_ostream &out, DeserializeMode mode, const vector& filters) { + read_features(out, mode); read_containers(mode); StockpileSettingsSerializer::read(mode, filters); } @@ -913,19 +916,25 @@ void StockpileSerializer::write_features() { mBuffer.set_dump(mPile->settings.allow_organic); } -void StockpileSerializer::read_features(DeserializeMode mode) { - read_elem("use_links_only", mode, - std::bind(&StockpileSettings::has_use_links_only, mBuffer), - std::bind(&StockpileSettings::use_links_only, mBuffer), - mPile->use_links_only); - read_elem("allow_inorganic", mode, - std::bind(&StockpileSettings::has_allow_inorganic, mBuffer), - std::bind(&StockpileSettings::allow_inorganic, mBuffer), - mPile->settings.allow_inorganic); - read_elem("allow_organic", mode, - std::bind(&StockpileSettings::has_allow_organic, mBuffer), - std::bind(&StockpileSettings::allow_organic, mBuffer), - mPile->settings.allow_organic); +void StockpileSerializer::read_features(color_ostream &out, DeserializeMode mode) { + int32_t melt = -1, trade = -1, dump = -1; + read_elem("melt", mode, + std::bind(&StockpileSettings::has_melt, mBuffer), + std::bind(&StockpileSettings::melt, mBuffer), + melt); + read_elem("trade", mode, + std::bind(&StockpileSettings::has_trade, mBuffer), + std::bind(&StockpileSettings::trade, mBuffer), + trade); + read_elem("dump", mode, + std::bind(&StockpileSettings::has_dump, mBuffer), + std::bind(&StockpileSettings::dump, mBuffer), + dump); + + if (melt != -1 || trade != -1 || dump != -1) { + auto &core = Core::getInstance(); + core.runCommand(out, "logistics clear -s " + int_to_string(mPile->stockpile_number)); + } } static bool ammo_mat_is_allowed(const MaterialInfo& mi) { diff --git a/plugins/stockpiles/StockpileSerializer.h b/plugins/stockpiles/StockpileSerializer.h index 70e8909da..f8d122ac2 100644 --- a/plugins/stockpiles/StockpileSerializer.h +++ b/plugins/stockpiles/StockpileSerializer.h @@ -21,6 +21,7 @@ enum IncludedElements { INCLUDED_ELEMENTS_GENERAL = 0x02, INCLUDED_ELEMENTS_CATEGORIES = 0x04, INCLUDED_ELEMENTS_TYPES = 0x08, + INCLUDED_ELEMENTS_FEATURES = 0x10, }; enum DeserializeMode { @@ -82,12 +83,12 @@ public: /** * Again, copied from message.cc */ - bool parse_from_istream(std::istream* input, DeserializeMode mode, const std::vector& filters); + bool parse_from_istream(DFHack::color_ostream &out, std::istream* input, DeserializeMode mode, const std::vector& filters); /** * Read stockpile settings from file */ - bool unserialize_from_file(const std::string& file, DeserializeMode mode, const std::vector& filters); + bool unserialize_from_file(DFHack::color_ostream &out, const std::string& file, DeserializeMode mode, const std::vector& filters); protected: dfstockpiles::StockpileSettings mBuffer; diff --git a/plugins/stockpiles/stockpiles.cpp b/plugins/stockpiles/stockpiles.cpp index 23ceb849b..a289337a9 100644 --- a/plugins/stockpiles/stockpiles.cpp +++ b/plugins/stockpiles/stockpiles.cpp @@ -134,7 +134,7 @@ static bool stockpiles_import(color_ostream& out, string fname, int id, string m try { StockpileSerializer cereal(sp); - if (!cereal.unserialize_from_file(fname, mode, filters)) { + if (!cereal.unserialize_from_file(out, fname, mode, filters)) { out.printerr("deserialization failed: '%s'\n", fname.c_str()); return false; } From b7d4b8fed63e097a3e3abb60a7ce705749fbde19 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 4 May 2023 01:29:07 -0700 Subject: [PATCH 15/24] import and export logistics features --- plugins/logistics.cpp | 2 +- plugins/lua/stockpiles.lua | 5 + plugins/stockpiles/OrganicMatLookup.cpp | 28 +- plugins/stockpiles/OrganicMatLookup.h | 6 +- plugins/stockpiles/StockpileSerializer.cpp | 659 +++++++++++---------- plugins/stockpiles/StockpileSerializer.h | 78 +-- plugins/stockpiles/StockpileUtils.h | 6 + plugins/stockpiles/stockpiles.cpp | 9 +- 8 files changed, 411 insertions(+), 382 deletions(-) diff --git a/plugins/logistics.cpp b/plugins/logistics.cpp index d4c4cef53..804aa5fe3 100644 --- a/plugins/logistics.cpp +++ b/plugins/logistics.cpp @@ -608,7 +608,7 @@ static int logistics_getStockpileConfigs(lua_State *L) { color_ostream *out = Lua::GetOutput(L); if (!out) out = &Core::getInstance().getConsole(); - DEBUG(status, *out).print("entering logistics_getStockpileConfig\n"); + DEBUG(status, *out).print("entering logistics_getStockpileConfigs\n"); unordered_map cache; validate_stockpile_configs(*out, cache); diff --git a/plugins/lua/stockpiles.lua b/plugins/lua/stockpiles.lua index 3590dc1f9..3e03d9eda 100644 --- a/plugins/lua/stockpiles.lua +++ b/plugins/lua/stockpiles.lua @@ -207,6 +207,11 @@ function parse_commandline(args) return true end +function get_stockpile_features(stockpile_number) + local config = logistics.logistics_getStockpileConfigs(stockpile_number)[1] + return config.melt, config.trade, config.dump +end + -------------------- -- dialogs -------------------- diff --git a/plugins/stockpiles/OrganicMatLookup.cpp b/plugins/stockpiles/OrganicMatLookup.cpp index 91294809b..1efdd4df6 100644 --- a/plugins/stockpiles/OrganicMatLookup.cpp +++ b/plugins/stockpiles/OrganicMatLookup.cpp @@ -21,8 +21,8 @@ DBG_EXTERN(stockpiles, log); * and their index in the stockpile_settings structures. */ -void OrganicMatLookup::food_mat_by_idx(organic_mat_category::organic_mat_category mat_category, std::vector::size_type food_idx, FoodMat& food_mat) { - DEBUG(log).print("food_lookup: food_idx(%zd)\n", food_idx); +void OrganicMatLookup::food_mat_by_idx(color_ostream& out, organic_mat_category::organic_mat_category mat_category, std::vector::size_type food_idx, FoodMat& food_mat) { + DEBUG(log, out).print("food_lookup: food_idx(%zd)\n", food_idx); df::world_raws& raws = world->raws; df::special_mat_table table = raws.mat_table; int32_t main_idx = table.organic_indexes[mat_category][food_idx]; @@ -32,16 +32,16 @@ void OrganicMatLookup::food_mat_by_idx(organic_mat_category::organic_mat_categor mat_category == organic_mat_category::Eggs) { food_mat.creature = raws.creatures.all[type]; food_mat.caste = food_mat.creature->caste[main_idx]; - DEBUG(log).print("special creature type(%d) caste(%d)\n", type, main_idx); + DEBUG(log, out).print("special creature type(%d) caste(%d)\n", type, main_idx); } else { food_mat.material.decode(type, main_idx); - DEBUG(log).print("type(%d) index(%d) token(%s)\n", type, main_idx, food_mat.material.getToken().c_str()); + DEBUG(log, out).print("type(%d) index(%d) token(%s)\n", type, main_idx, food_mat.material.getToken().c_str()); } } -std::string OrganicMatLookup::food_token_by_idx(organic_mat_category::organic_mat_category mat_category, std::vector::size_type idx) { +std::string OrganicMatLookup::food_token_by_idx(color_ostream& out, organic_mat_category::organic_mat_category mat_category, std::vector::size_type idx) { FoodMat food_mat; - food_mat_by_idx(mat_category, idx, food_mat); + food_mat_by_idx(out, mat_category, idx, food_mat); if (food_mat.material.isValid()) { return food_mat.material.getToken(); } @@ -72,32 +72,32 @@ void OrganicMatLookup::food_build_map() { index_built = true; } -int16_t OrganicMatLookup::food_idx_by_token(organic_mat_category::organic_mat_category mat_category, const std::string& token) { +int16_t OrganicMatLookup::food_idx_by_token(color_ostream& out, organic_mat_category::organic_mat_category mat_category, const std::string& token) { df::world_raws& raws = world->raws; df::special_mat_table table = raws.mat_table; - DEBUG(log).print("food_idx_by_token:\n"); + DEBUG(log, out).print("food_idx_by_token:\n"); if (mat_category == organic_mat_category::Fish || mat_category == organic_mat_category::UnpreparedFish || mat_category == organic_mat_category::Eggs) { std::vector tokens; split_string(&tokens, token, ":"); if (tokens.size() != 2) { - WARN(log).print("creature invalid CREATURE:CASTE token: %s\n", token.c_str()); + WARN(log, out).print("creature invalid CREATURE:CASTE token: %s\n", token.c_str()); return -1; } int16_t creature_idx = find_creature(tokens[0]); if (creature_idx < 0) { - WARN(log).print("creature invalid token %s\n", tokens[0].c_str()); + WARN(log, out).print("creature invalid token %s\n", tokens[0].c_str()); return -1; } int16_t food_idx = linear_index(table.organic_types[mat_category], creature_idx); if (tokens[1] == "MALE") food_idx += 1; if (table.organic_types[mat_category][food_idx] == creature_idx) { - DEBUG(log).print("creature %s caste %s creature_idx(%d) food_idx(%d)\n", token.c_str(), tokens[1].c_str(), creature_idx, food_idx); + DEBUG(log, out).print("creature %s caste %s creature_idx(%d) food_idx(%d)\n", token.c_str(), tokens[1].c_str(), creature_idx, food_idx); return food_idx; } - WARN(log).print("creature caste not found: %s caste %s creature_idx(%d) food_idx(%d)\n", token.c_str(), tokens[1].c_str(), creature_idx, food_idx); + WARN(log, out).print("creature caste not found: %s caste %s creature_idx(%d) food_idx(%d)\n", token.c_str(), tokens[1].c_str(), creature_idx, food_idx); return -1; } @@ -108,12 +108,12 @@ int16_t OrganicMatLookup::food_idx_by_token(organic_mat_category::organic_mat_ca int32_t index = mat_info.index; auto it = food_index[mat_category].find(std::make_pair(type, index)); if (it != food_index[mat_category].end()) { - DEBUG(log).print("matinfo: %s type(%d) idx(%d) food_idx(%zd)\n", token.c_str(), mat_info.type, mat_info.index, it->second); + DEBUG(log, out).print("matinfo: %s type(%d) idx(%d) food_idx(%zd)\n", token.c_str(), mat_info.type, mat_info.index, it->second); return it->second; } - WARN(log).print("matinfo: %s type(%d) idx(%d) food_idx not found :(\n", token.c_str(), mat_info.type, mat_info.index); + WARN(log, out).print("matinfo: %s type(%d) idx(%d) food_idx not found :(\n", token.c_str(), mat_info.type, mat_info.index); return -1; } diff --git a/plugins/stockpiles/OrganicMatLookup.h b/plugins/stockpiles/OrganicMatLookup.h index 7ae65b17e..37e019b58 100644 --- a/plugins/stockpiles/OrganicMatLookup.h +++ b/plugins/stockpiles/OrganicMatLookup.h @@ -28,13 +28,13 @@ public: FoodMat(): material(-1), creature(0), caste(0) { } }; - static void food_mat_by_idx(df::enums::organic_mat_category::organic_mat_category mat_category, std::vector::size_type food_idx, FoodMat& food_mat); - static std::string food_token_by_idx(df::enums::organic_mat_category::organic_mat_category mat_category, std::vector::size_type idx); + static void food_mat_by_idx(DFHack::color_ostream& out, df::enums::organic_mat_category::organic_mat_category mat_category, std::vector::size_type food_idx, FoodMat& food_mat); + static std::string food_token_by_idx(DFHack::color_ostream& out, df::enums::organic_mat_category::organic_mat_category mat_category, std::vector::size_type idx); static size_t food_max_size(df::enums::organic_mat_category::organic_mat_category mat_category); static void food_build_map(); - static int16_t food_idx_by_token(df::enums::organic_mat_category::organic_mat_category mat_category, const std::string& token); + static int16_t food_idx_by_token(DFHack::color_ostream& out, df::enums::organic_mat_category::organic_mat_category mat_category, const std::string& token); static DFHack::MaterialInfo food_mat_by_token(const std::string& token); diff --git a/plugins/stockpiles/StockpileSerializer.cpp b/plugins/stockpiles/StockpileSerializer.cpp index 2908f3329..f6091b9cd 100644 --- a/plugins/stockpiles/StockpileSerializer.cpp +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -41,6 +41,7 @@ using namespace dfstockpiles; using df::global::world; using std::placeholders::_1; +using std::placeholders::_2; namespace DFHack { @@ -162,11 +163,11 @@ StockpileSerializer::StockpileSerializer(df::building_stockpilest* stockpile) StockpileSerializer::~StockpileSerializer() { } -bool StockpileSettingsSerializer::serialize_to_ostream(std::ostream* output, uint32_t includedElements) { +bool StockpileSettingsSerializer::serialize_to_ostream(color_ostream& out, std::ostream* output, uint32_t includedElements) { if (output->fail()) return false; mBuffer.Clear(); - write(includedElements); + write(out, includedElements); { io::OstreamOutputStream zero_copy_output(output); if (!mBuffer.SerializeToZeroCopyStream(&zero_copy_output)) @@ -175,14 +176,14 @@ bool StockpileSettingsSerializer::serialize_to_ostream(std::ostream* output, uin return output->good(); } -bool StockpileSettingsSerializer::serialize_to_file(const string& file, uint32_t includedElements) { +bool StockpileSettingsSerializer::serialize_to_file(color_ostream& out, const string& file, uint32_t includedElements) { std::fstream output(file, std::ios::out | std::ios::binary | std::ios::trunc); if (output.fail()) { - WARN(log).print("ERROR: failed to open file for writing: '%s'\n", + WARN(log, out).print("ERROR: failed to open file for writing: '%s'\n", file.c_str()); return false; } - return serialize_to_ostream(&output, includedElements); + return serialize_to_ostream(out, &output, includedElements); } bool StockpileSettingsSerializer::parse_from_istream(color_ostream &out, std::istream* input, DeserializeMode mode, const vector& filters) { @@ -200,7 +201,7 @@ bool StockpileSettingsSerializer::parse_from_istream(color_ostream &out, std::is bool StockpileSettingsSerializer::unserialize_from_file(color_ostream &out, const string& file, DeserializeMode mode, const vector& filters) { std::fstream input(file, std::ios::in | std::ios::binary); if (input.fail()) { - WARN(log).print("failed to open file for reading: '%s'\n", + WARN(log, out).print("failed to open file for reading: '%s'\n", file.c_str()); return false; } @@ -229,37 +230,37 @@ static typename df::enum_traits::base_type linear_index(df::enum_traits tr return -1; } -static bool matches_filter(const vector& filters, const string& name) { +static bool matches_filter(color_ostream& out, const vector& filters, const string& name) { for (auto & filter : filters) { - DEBUG(log).print("searching for '%s' in '%s'\n", filter.c_str(), name.c_str()); + DEBUG(log, out).print("searching for '%s' in '%s'\n", filter.c_str(), name.c_str()); if (std::search(name.begin(), name.end(), filter.begin(), filter.end(), [](unsigned char ch1, unsigned char ch2) { return std::toupper(ch1) == std::toupper(ch2); } - ) != name.end()) + ) != name.end()) return true; } return !filters.size(); } -static void set_flag(const char* name, const vector& filters, bool all, char val, bool enabled, bool& elem) { - if ((all || enabled) && matches_filter(filters, name)) { - DEBUG(log).print("setting %s to %d\n", name, val); +static void set_flag(color_ostream& out, const char* name, const vector& filters, bool all, char val, bool enabled, bool& elem) { + if ((all || enabled) && matches_filter(out, filters, name)) { + DEBUG(log, out).print("setting %s to %d\n", name, val); elem = val; } } -static void set_filter_elem(const char* subcat, const vector& filters, char val, +static void set_filter_elem(color_ostream& out, const char* subcat, const vector& filters, char val, const string& name, const string& id, char& elem) { - if (matches_filter(filters, subcat + ((*subcat ? "/" : "") + name))) { - DEBUG(log).print("setting %s (%s) to %d\n", name.c_str(), id.c_str(), val); + if (matches_filter(out, filters, subcat + ((*subcat ? "/" : "") + name))) { + DEBUG(log, out).print("setting %s (%s) to %d\n", name.c_str(), id.c_str(), val); elem = val; } } template -static void set_filter_elem(const char* subcat, const vector& filters, T_val val, +static void set_filter_elem(color_ostream& out, const char* subcat, const vector& filters, T_val val, const string& name, T_id id, T_val& elem) { - if (matches_filter(filters, subcat + ((*subcat ? "/" : "") + name))) { - DEBUG(log).print("setting %s (%d) to %d\n", name.c_str(), (int32_t)id, val); + if (matches_filter(out, filters, subcat + ((*subcat ? "/" : "") + name))) { + DEBUG(log, out).print("setting %s (%d) to %d\n", name.c_str(), (int32_t)id, val); elem = val; } } @@ -277,7 +278,7 @@ static void set_filter_elem(const char* subcat, const vector& filters, T * * The unserialization process is the same in reverse. */ -static bool serialize_list_itemdef(FuncWriteExport add_value, +static bool serialize_list_itemdef(color_ostream& out, FuncWriteExport add_value, vector list, vector items, item_type::item_type type) { @@ -294,13 +295,13 @@ static bool serialize_list_itemdef(FuncWriteExport add_value, ItemTypeInfo ii; if (!ii.decode(type, i)) continue; - DEBUG(log).print("adding itemdef type %s\n", ii.getToken().c_str()); + DEBUG(log, out).print("adding itemdef type %s\n", ii.getToken().c_str()); add_value(ii.getToken()); } return all; } -static void unserialize_list_itemdef(const char* subcat, bool all, char val, const vector& filters, +static void unserialize_list_itemdef(color_ostream& out, const char* subcat, bool all, char val, const vector& filters, FuncReadImport read_value, int32_t list_size, vector& pile_list, item_type::item_type type) { int num_elems = Items::getSubtypeCount(type); pile_list.resize(num_elems, '\0'); @@ -308,7 +309,7 @@ static void unserialize_list_itemdef(const char* subcat, bool all, char val, con for (auto idx = 0; idx < num_elems; ++idx) { ItemTypeInfo ii; ii.decode(type, idx); - set_filter_elem(subcat, filters, val, ii.toString(), idx, pile_list.at(idx)); + set_filter_elem(out, subcat, filters, val, ii.toString(), idx, pile_list.at(idx)); } return; } @@ -319,14 +320,14 @@ static void unserialize_list_itemdef(const char* subcat, bool all, char val, con if (!ii.find(id)) continue; if (ii.subtype < 0 || size_t(ii.subtype) >= pile_list.size()) { - WARN(log).print("item type index invalid: %d\n", ii.subtype); + WARN(log, out).print("item type index invalid: %d\n", ii.subtype); continue; } - set_filter_elem(subcat, filters, val, id, ii.subtype, pile_list.at(ii.subtype)); + set_filter_elem(out, subcat, filters, val, id, ii.subtype, pile_list.at(ii.subtype)); } } -static bool serialize_list_quality(FuncWriteExport add_value, +static bool serialize_list_quality(color_ostream& out, FuncWriteExport add_value, const bool(&quality_list)[7]) { using df::enums::item_quality::item_quality; using quality_traits = df::enum_traits; @@ -339,7 +340,7 @@ static bool serialize_list_quality(FuncWriteExport add_value, } const string f_type(quality_traits::key_table[i]); add_value(f_type); - DEBUG(log).print("adding quality %s\n", f_type.c_str()); + DEBUG(log, out).print("adding quality %s\n", f_type.c_str()); } return all; } @@ -348,12 +349,12 @@ static void quality_clear(bool(&pile_list)[7]) { std::fill(pile_list, pile_list + 7, false); } -static void unserialize_list_quality(const char* subcat, bool all, bool val, const vector& filters, +static void unserialize_list_quality(color_ostream& out, const char* subcat, bool all, bool val, const vector& filters, FuncReadImport read_value, int32_t list_size, bool(&pile_list)[7]) { if (all) { for (auto idx = 0; idx < 7; ++idx) { string id = ENUM_KEY_STR(item_quality, (df::item_quality)idx); - set_filter_elem(subcat, filters, val, id, idx, pile_list[idx]); + set_filter_elem(out, subcat, filters, val, id, idx, pile_list[idx]); } return; } @@ -364,10 +365,10 @@ static void unserialize_list_quality(const char* subcat, bool all, bool val, con const string quality = read_value(i); df::enum_traits::base_type idx = linear_index(quality_traits, quality); if (idx < 0) { - WARN(log).print("invalid quality token: %s\n", quality.c_str()); + WARN(log, out).print("invalid quality token: %s\n", quality.c_str()); continue; } - set_filter_elem(subcat, filters, val, quality, idx, pile_list[idx]); + set_filter_elem(out, subcat, filters, val, quality, idx, pile_list[idx]); } } @@ -388,7 +389,7 @@ static int other_mats_token(const std::map other_mats, return -1; } -static bool serialize_list_other_mats( +static bool serialize_list_other_mats(color_ostream& out, const std::map other_mats, FuncWriteExport add_value, vector list) { @@ -400,23 +401,23 @@ static bool serialize_list_other_mats( } const string token = other_mats_index(other_mats, i); if (token.empty()) { - WARN(log).print("invalid other material with index %zd\n", i); + WARN(log, out).print("invalid other material with index %zd\n", i); continue; } add_value(token); - DEBUG(log).print("other mats %zd is %s\n", i, token.c_str()); + DEBUG(log, out).print("other mats %zd is %s\n", i, token.c_str()); } return all; } -static void unserialize_list_other_mats(const char* subcat, bool all, char val, const vector& filters, +static void unserialize_list_other_mats(color_ostream& out, const char* subcat, bool all, char val, const vector& filters, const std::map other_mats, FuncReadImport read_value, int32_t list_size, vector& pile_list) { size_t num_elems = other_mats.size(); pile_list.resize(num_elems, '\0'); if (all) { for (auto & entry : other_mats) - set_filter_elem(subcat, filters, val, entry.second, entry.first, pile_list.at(entry.first)); + set_filter_elem(out, subcat, filters, val, entry.second, entry.first, pile_list.at(entry.first)); return; } @@ -424,23 +425,23 @@ static void unserialize_list_other_mats(const char* subcat, bool all, char val, const string token = read_value(i); size_t idx = other_mats_token(other_mats, token); if (idx < 0) { - WARN(log).print("invalid other mat with token %s\n", token.c_str()); + WARN(log, out).print("invalid other mat with token %s\n", token.c_str()); continue; } if (idx >= num_elems) { - WARN(log).print("other_mats index too large! idx[%zd] max_size[%zd]\n", idx, num_elems); + WARN(log, out).print("other_mats index too large! idx[%zd] max_size[%zd]\n", idx, num_elems); continue; } - set_filter_elem(subcat, filters, val, token, idx, pile_list.at(idx)); + set_filter_elem(out, subcat, filters, val, token, idx, pile_list.at(idx)); } } -static bool serialize_list_organic_mat(FuncWriteExport add_value, +static bool serialize_list_organic_mat(color_ostream& out, FuncWriteExport add_value, const vector* list, organic_mat_category::organic_mat_category cat) { bool all = true; if (!list) { - DEBUG(log).print("serialize_list_organic_mat: list null\n"); + DEBUG(log, out).print("serialize_list_organic_mat: list null\n"); return all; } for (size_t i = 0; i < list->size(); ++i) { @@ -448,49 +449,49 @@ static bool serialize_list_organic_mat(FuncWriteExport add_value, all = false; continue; } - string token = OrganicMatLookup::food_token_by_idx(cat, i); + string token = OrganicMatLookup::food_token_by_idx(out, cat, i); if (token.empty()) { - DEBUG(log).print("food mat invalid :(\n"); + DEBUG(log, out).print("food mat invalid :(\n"); continue; } - DEBUG(log).print("organic_material %zd is %s\n", i, token.c_str()); + DEBUG(log, out).print("organic_material %zd is %s\n", i, token.c_str()); add_value(token); } return all; } -static void unserialize_list_organic_mat(const char* subcat, bool all, char val, const vector& filters, +static void unserialize_list_organic_mat(color_ostream& out, const char* subcat, bool all, char val, const vector& filters, FuncReadImport read_value, size_t list_size, vector& pile_list, organic_mat_category::organic_mat_category cat) { size_t num_elems = OrganicMatLookup::food_max_size(cat); pile_list.resize(num_elems, '\0'); if (all) { for (size_t idx = 0; idx < num_elems; ++idx) { - string token = OrganicMatLookup::food_token_by_idx(cat, idx); - set_filter_elem(subcat, filters, val, token, idx, pile_list.at(idx)); + string token = OrganicMatLookup::food_token_by_idx(out, cat, idx); + set_filter_elem(out, subcat, filters, val, token, idx, pile_list.at(idx)); } return; } for (size_t i = 0; i < list_size; ++i) { const string token = read_value(i); - int16_t idx = OrganicMatLookup::food_idx_by_token(cat, token); + int16_t idx = OrganicMatLookup::food_idx_by_token(out, cat, token); if (idx < 0 || size_t(idx) >= num_elems) { - WARN(log).print("organic mat index too large! idx[%d] max_size[%zd]\n", idx, num_elems); + WARN(log, out).print("organic mat index too large! idx[%d] max_size[%zd]\n", idx, num_elems); continue; } - set_filter_elem(subcat, filters, val, token, idx, pile_list.at(idx)); + set_filter_elem(out, subcat, filters, val, token, idx, pile_list.at(idx)); } } -static bool serialize_list_item_type(FuncItemAllowed is_allowed, +static bool serialize_list_item_type(color_ostream& out, FuncItemAllowed is_allowed, FuncWriteExport add_value, const vector& list) { using df::enums::item_type::item_type; using type_traits = df::enum_traits; bool all = true; size_t num_item_types = list.size(); - DEBUG(log).print("item_type size = %zd size limit = %d typecasted: %zd\n", + DEBUG(log, out).print("item_type size = %zd size limit = %d typecasted: %zd\n", num_item_types, type_traits::last_item_value, (size_t)type_traits::last_item_value); for (size_t i = 0; i <= (size_t)type_traits::last_item_value; ++i) { @@ -503,12 +504,12 @@ static bool serialize_list_item_type(FuncItemAllowed is_allowed, if (!is_allowed(type)) continue; add_value(r_type); - DEBUG(log).print("item_type key_table[%zd] type[%d] is %s\n", i + 1, (int16_t)type, r_type.c_str()); + DEBUG(log, out).print("item_type key_table[%zd] type[%d] is %s\n", i + 1, (int16_t)type, r_type.c_str()); } return all; } -static void unserialize_list_item_type(const char* subcat, bool all, char val, const vector& filters, +static void unserialize_list_item_type(color_ostream& out, const char* subcat, bool all, char val, const vector& filters, FuncItemAllowed is_allowed, FuncReadImport read_value, int32_t list_size, vector& pile_list) { // TODO can we remove the hardcoded list size? size_t num_elems = 112; @@ -521,7 +522,7 @@ static void unserialize_list_item_type(const char* subcat, bool all, char val, c if (all) { for (size_t idx = 0; idx < num_elems; ++idx) { string id = ENUM_KEY_STR(item_type, (df::item_type)idx); - set_filter_elem(subcat, filters, val, id, idx, pile_list.at(idx)); + set_filter_elem(out, subcat, filters, val, id, idx, pile_list.at(idx)); } return; } @@ -535,14 +536,14 @@ static void unserialize_list_item_type(const char* subcat, bool all, char val, c if (!is_allowed((item_type)idx)) continue; if (idx < 0 || size_t(idx) >= num_elems) { - WARN(log).print("error item_type index too large! idx[%d] max_size[%zd]\n", idx, num_elems); + WARN(log, out).print("error item_type index too large! idx[%d] max_size[%zd]\n", idx, num_elems); continue; } - set_filter_elem(subcat, filters, val, token, idx, pile_list.at(idx)); + set_filter_elem(out, subcat, filters, val, token, idx, pile_list.at(idx)); } } -static bool serialize_list_material(FuncMaterialAllowed is_allowed, +static bool serialize_list_material(color_ostream& out, FuncMaterialAllowed is_allowed, FuncWriteExport add_value, const vector& list) { bool all = true; MaterialInfo mi; @@ -554,13 +555,13 @@ static bool serialize_list_material(FuncMaterialAllowed is_allowed, mi.decode(0, i); if (!is_allowed(mi)) continue; - DEBUG(log).print("adding material %s\n", mi.getToken().c_str()); + DEBUG(log, out).print("adding material %s\n", mi.getToken().c_str()); add_value(mi.getToken()); } return all; } -static void unserialize_list_material(const char* subcat, bool all, char val, const vector& filters, +static void unserialize_list_material(color_ostream& out, const char* subcat, bool all, char val, const vector& filters, FuncMaterialAllowed is_allowed, FuncReadImport read_value, int32_t list_size, vector& pile_list) { // we initialize all disallowed values to 1 @@ -577,7 +578,7 @@ static void unserialize_list_material(const char* subcat, bool all, char val, co for (size_t idx = 0; idx < num_elems; ++idx) { MaterialInfo mi; mi.decode(0, idx); - set_filter_elem(subcat, filters, val, mi.toString(), idx, pile_list.at(idx)); + set_filter_elem(out, subcat, filters, val, mi.toString(), idx, pile_list.at(idx)); } return; } @@ -588,14 +589,14 @@ static void unserialize_list_material(const char* subcat, bool all, char val, co if (!mi.find(id) || !is_allowed(mi)) continue; if (mi.index < 0 || size_t(mi.index) >= pile_list.size()) { - WARN(log).print("material type index invalid: %d\n", mi.index); + WARN(log, out).print("material type index invalid: %d\n", mi.index); continue; } - set_filter_elem(subcat, filters, val, id, mi.index, pile_list.at(mi.index)); + set_filter_elem(out, subcat, filters, val, id, mi.index, pile_list.at(mi.index)); } } -static bool serialize_list_creature(FuncWriteExport add_value, const vector& list) { +static bool serialize_list_creature(color_ostream& out, FuncWriteExport add_value, const vector& list) { bool all = true; for (size_t i = 0; i < list.size(); ++i) { @@ -607,7 +608,7 @@ static bool serialize_list_creature(FuncWriteExport add_value, const vectorflags.is_set(creature_raw_flags::GENERATED) || r->creature_id == "EQUIPMENT_WAGON") continue; - DEBUG(log).print("adding creature %s\n", r->creature_id.c_str()); + DEBUG(log, out).print("adding creature %s\n", r->creature_id.c_str()); add_value(r->creature_id); } return all; @@ -619,14 +620,14 @@ static string get_filter_string(df::creature_raw *r) { return r->name[0] + "/tameable"; } -static void unserialize_list_creature(const char* subcat, bool all, char val, const vector& filters, +static void unserialize_list_creature(color_ostream& out, const char* subcat, bool all, char val, const vector& filters, FuncReadImport read_value, int32_t list_size, vector& pile_list) { size_t num_elems = world->raws.creatures.all.size(); pile_list.resize(num_elems, '\0'); if (all) { for (size_t idx = 0; idx < num_elems; ++idx) { auto r = find_creature(idx); - set_filter_elem(subcat, filters, val, get_filter_string(r), r->creature_id, pile_list.at(idx)); + set_filter_elem(out, subcat, filters, val, get_filter_string(r), r->creature_id, pile_list.at(idx)); } return; } @@ -635,42 +636,43 @@ static void unserialize_list_creature(const char* subcat, bool all, char val, co string id = read_value(i); int idx = find_creature(id); if (idx < 0 || size_t(idx) >= num_elems) { - WARN(log).print("animal index invalid: %d\n", idx); + WARN(log, out).print("animal index invalid: %d\n", idx); continue; } auto r = find_creature(idx); - set_filter_elem(subcat, filters, val, get_filter_string(r), r->creature_id, pile_list.at(idx)); + set_filter_elem(out, subcat, filters, val, get_filter_string(r), r->creature_id, pile_list.at(idx)); } } template -static void write_cat(const char *name, bool include_types, uint32_t cat_flags, - enum df::stockpile_group_set::Mask cat_mask, - std::function mutable_cat_fn, - std::function write_cat_fn) { +static void write_cat(color_ostream& out, const char* name, bool include_types, uint32_t cat_flags, + enum df::stockpile_group_set::Mask cat_mask, + std::function mutable_cat_fn, + std::function write_cat_fn) { + if (!(cat_flags & cat_mask)) return; T_cat_set* cat_set = mutable_cat_fn(); if (!include_types) { - DEBUG(log).print("including all for %s since only category is being recorded\n", name); + DEBUG(log, out).print("including all for %s since only category is being recorded\n", name); cat_set->set_all(true); return; } - if (write_cat_fn(cat_set)) { + if (write_cat_fn(out, cat_set)) { // all fields were set. clear them and use the "all" flag instead so "all" can be applied // to other worlds with other generated types - DEBUG(log).print("including all for %s since all fields were enabled\n", name); + DEBUG(log, out).print("including all for %s since all fields were enabled\n", name); cat_set->Clear(); cat_set->set_all(true); } } -void StockpileSettingsSerializer::write(uint32_t includedElements) { +void StockpileSettingsSerializer::write(color_ostream& out, uint32_t includedElements) { if (includedElements & INCLUDED_ELEMENTS_GENERAL) - write_general(); + write_general(out); if (!(includedElements & INCLUDED_ELEMENTS_CATEGORIES)) return; @@ -767,11 +769,11 @@ void StockpileSettingsSerializer::write(uint32_t includedElements) { std::bind(&StockpileSettingsSerializer::write_wood, this, _1)); } -void StockpileSerializer::write(uint32_t includedElements) { +void StockpileSerializer::write(color_ostream& out, uint32_t includedElements) { if (includedElements & INCLUDED_ELEMENTS_FEATURES) - write_features(); + write_features(out); if (includedElements & INCLUDED_ELEMENTS_CONTAINERS) - write_containers(); + write_containers(out); StockpileSettingsSerializer::write(includedElements); } @@ -796,30 +798,30 @@ void StockpileSettingsSerializer::read(color_ostream &out, DeserializeMode mode, StockpileSettings::CorpsesSet* corpses = mBuffer.mutable_corpses_v50(); corpses->set_all(true); } - read_corpses(mode, filters); + read_corpses(out, mode, filters); - read_refuse(mode, filters); - read_sheet(mode, filters); - read_stone(mode, filters); - read_weapons(mode, filters); - read_wood(mode, filters); + read_refuse(out, mode, filters); + read_sheet(out, mode, filters); + read_stone(out, mode, filters); + read_weapons(out, mode, filters); + read_wood(out, mode, filters); } void StockpileSerializer::read(color_ostream &out, DeserializeMode mode, const vector& filters) { read_features(out, mode); - read_containers(mode); - StockpileSettingsSerializer::read(mode, filters); + read_containers(out, mode); + StockpileSettingsSerializer::read(out, mode, filters); } -void StockpileSerializer::write_containers() { - DEBUG(log).print("writing container settings\n"); +void StockpileSerializer::write_containers(color_ostream& out) { + DEBUG(log, out).print("writing container settings\n"); mBuffer.set_max_bins(mPile->max_bins); mBuffer.set_max_barrels(mPile->max_barrels); mBuffer.set_max_wheelbarrows(mPile->max_wheelbarrows); } template -static void read_elem(const char *name, DeserializeMode mode, +static void read_elem(color_ostream& out, const char* name, DeserializeMode mode, std::function has_elem_fn, std::function elem_fn, T_elem &setting) { @@ -829,13 +831,13 @@ static void read_elem(const char *name, DeserializeMode mode, bool is_set = elem_fn() != 0; if (mode == DESERIALIZE_MODE_SET || is_set) { T_elem val = (mode == DESERIALIZE_MODE_DISABLE) ? 0 : elem_fn(); - DEBUG(log).print("setting %s to %d\n", name, val); + DEBUG(log, out).print("setting %s to %d\n", name, val); setting = val; } } template -static void read_category(const char *name, DeserializeMode mode, +static void read_category(color_ostream& out, const char* name, DeserializeMode mode, std::function has_cat_fn, std::function cat_fn, uint32_t & cat_flags, @@ -843,7 +845,7 @@ static void read_category(const char *name, DeserializeMode mode, std::function clear_fn, std::function set_fn) { if (mode == DESERIALIZE_MODE_SET) { - DEBUG(log).print("clearing %s\n", name); + DEBUG(log, out).print("clearing %s\n", name); cat_flags &= ~cat_mask; clear_fn(); } @@ -859,81 +861,97 @@ static void read_category(const char *name, DeserializeMode mode, bool all = cat_fn().all(); char val = (mode == DESERIALIZE_MODE_DISABLE) ? (char)0 : (char)1; - DEBUG(log).print("setting %s %s elements to %d\n", + DEBUG(log, out).print("setting %s %s elements to %d\n", all ? "all" : "marked", name, val); set_fn(all, val); } -void StockpileSerializer::read_containers(DeserializeMode mode) { - read_elem("max_bins", mode, +void StockpileSerializer::read_containers(color_ostream& out, DeserializeMode mode) { + read_elem(out, "max_bins", mode, std::bind(&StockpileSettings::has_max_bins, mBuffer), std::bind(&StockpileSettings::max_bins, mBuffer), mPile->max_bins); - read_elem("max_barrels", mode, + read_elem(out, "max_barrels", mode, std::bind(&StockpileSettings::has_max_barrels, mBuffer), std::bind(&StockpileSettings::max_barrels, mBuffer), mPile->max_barrels); - read_elem("max_wheelbarrows", mode, + read_elem(out, "max_wheelbarrows", mode, std::bind(&StockpileSettings::has_max_wheelbarrows, mBuffer), std::bind(&StockpileSettings::max_wheelbarrows, mBuffer), mPile->max_wheelbarrows); } -void StockpileSettingsSerializer::write_general() { - DEBUG(log).print("writing general settings\n"); +void StockpileSettingsSerializer::write_general(color_ostream& out) { + DEBUG(log, out).print("writing general settings\n"); mBuffer.set_allow_inorganic(mSettings->allow_inorganic); mBuffer.set_allow_organic(mSettings->allow_organic); } -void StockpileSerializer::write_general() { - StockpileSettingsSerializer::write_general(); +void StockpileSerializer::write_general(color_ostream& out) { + StockpileSettingsSerializer::write_general(out); mBuffer.set_use_links_only(mPile->use_links_only); } -void StockpileSettingsSerializer::read_general(DeserializeMode mode) { - read_elem("allow_inorganic", mode, +void StockpileSettingsSerializer::read_general(color_ostream& out, DeserializeMode mode) { + read_elem(out, "allow_inorganic", mode, std::bind(&StockpileSettings::has_allow_inorganic, mBuffer), std::bind(&StockpileSettings::allow_inorganic, mBuffer), mSettings->allow_inorganic); - read_elem("allow_organic", mode, + read_elem(out, "allow_organic", mode, std::bind(&StockpileSettings::has_allow_organic, mBuffer), std::bind(&StockpileSettings::allow_organic, mBuffer), mSettings->allow_organic); } -void StockpileSerializer::read_general(DeserializeMode mode) { - StockpileSettingsSerializer::read_general(mode); - read_elem("use_links_only", mode, +void StockpileSerializer::read_general(color_ostream& out, DeserializeMode mode) { + StockpileSettingsSerializer::read_general(out, mode); + read_elem(out, "use_links_only", mode, std::bind(&StockpileSettings::has_use_links_only, mBuffer), std::bind(&StockpileSettings::use_links_only, mBuffer), mPile->use_links_only); } -void StockpileSerializer::write_features() { - DEBUG(log).print("writing feature settings\n"); - mBuffer.set_melt(mPile->use_links_only); - mBuffer.set_trade(mPile->settings.allow_inorganic); - mBuffer.set_dump(mPile->settings.allow_organic); +void StockpileSerializer::write_features(color_ostream& out) { + DEBUG(log, out).print("writing feature settings\n"); + if (!call_stockpiles_lua(&out, "get_stockpile_features", 1, 3, + [&](lua_State* L) { + Lua::Push(L, mPile->stockpile_number); + }, + [&](lua_State* L) { + mBuffer.set_melt(0 != lua_toboolean(L, -1)); + mBuffer.set_trade(0 != lua_toboolean(L, -2)); + mBuffer.set_dump(0 != lua_toboolean(L, -3)); + })) { + + WARN(log, out).print("failed to get logistics features of stockpile number %d\n", mPile->stockpile_number); + } } void StockpileSerializer::read_features(color_ostream &out, DeserializeMode mode) { int32_t melt = -1, trade = -1, dump = -1; - read_elem("melt", mode, + read_elem(out, "melt", mode, std::bind(&StockpileSettings::has_melt, mBuffer), std::bind(&StockpileSettings::melt, mBuffer), melt); - read_elem("trade", mode, + read_elem(out, "trade", mode, std::bind(&StockpileSettings::has_trade, mBuffer), std::bind(&StockpileSettings::trade, mBuffer), trade); - read_elem("dump", mode, + read_elem(out, "dump", mode, std::bind(&StockpileSettings::has_dump, mBuffer), std::bind(&StockpileSettings::dump, mBuffer), dump); if (melt != -1 || trade != -1 || dump != -1) { - auto &core = Core::getInstance(); - core.runCommand(out, "logistics clear -s " + int_to_string(mPile->stockpile_number)); + if (!call_stockpiles_lua(&out, "set_stockpile_features", 4, 0, + [&](lua_State* L) { + Lua::Push(L, mPile->stockpile_number); + Lua::Push(L, melt == 1); + Lua::Push(L, trade == 1); + Lua::Push(L, dump == 1); + })) { + WARN(log, out).print("failed to set logistics features of stockpile number %d\n", mPile->stockpile_number); + } } } @@ -941,21 +959,21 @@ static bool ammo_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid() && mi.material && mi.material->flags.is_set(material_flags::IS_METAL); } -bool StockpileSettingsSerializer::write_ammo(StockpileSettings::AmmoSet* ammo) { - bool all = serialize_list_itemdef( +bool StockpileSettingsSerializer::write_ammo(color_ostream& out, StockpileSettings::AmmoSet* ammo) { + bool all = serialize_list_itemdef(out, [&](const string& token) { ammo->add_type(token); }, mSettings->ammo.type, vector(world->raws.itemdefs.ammo.begin(), world->raws.itemdefs.ammo.end()), item_type::AMMO); - all = serialize_list_material( + all = serialize_list_material(out, ammo_mat_is_allowed, [&](const string& token) { ammo->add_mats(token); }, mSettings->ammo.mats) && all; if (mSettings->ammo.other_mats.size() > 2) { - WARN(log).print("ammo other materials > 2: %zd\n", - mSettings->ammo.other_mats.size()); + WARN(log, out).print("ammo other materials > 2: %zd\n", + mPile->settings.ammo.other_mats.size()); } size_t num_other_mats = std::min(size_t(2), @@ -967,23 +985,23 @@ bool StockpileSettingsSerializer::write_ammo(StockpileSettings::AmmoSet* ammo) { } const string token = i == 0 ? "WOOD" : "BONE"; ammo->add_other_mats(token); - DEBUG(log).print("other mats %zd is %s\n", i, token.c_str()); + DEBUG(log, out).print("other mats %zd is %s\n", i, token.c_str()); } - all = serialize_list_quality( + all = serialize_list_quality(out, [&](const string& token) { ammo->add_quality_core(token); }, mSettings->ammo.quality_core) && all; - all = serialize_list_quality( + all = serialize_list_quality(out, [&](const string& token) { ammo->add_quality_total(token); }, mSettings->ammo.quality_total) && all; return all; } -void StockpileSettingsSerializer::read_ammo(DeserializeMode mode, const vector& filters) { +void StockpileSettingsSerializer::read_ammo(color_ostream& out, DeserializeMode mode, const vector& filters) { auto & pammo = mSettings->ammo; - read_category("ammo", mode, + read_category(out, "ammo", mode, std::bind(&StockpileSettings::has_ammo, mBuffer), std::bind(&StockpileSettings::ammo, mBuffer), mSettings->flags.whole, @@ -998,18 +1016,18 @@ void StockpileSettingsSerializer::read_ammo(DeserializeMode mode, const vector const string& { return bammo.type(idx); }, bammo.type_size(), pammo.type, item_type::AMMO); - unserialize_list_material("mats", all, val, filters, ammo_mat_is_allowed, + unserialize_list_material(out, "mats", all, val, filters, ammo_mat_is_allowed, [&](const size_t& idx) -> const string& { return bammo.mats(idx); }, bammo.mats_size(), pammo.mats); pammo.other_mats.resize(2, '\0'); if (all) { - set_filter_elem("other", filters, val, "WOOD", 0, pammo.other_mats.at(0)); - set_filter_elem("other", filters, val, "BONE", 1, pammo.other_mats.at(1)); + set_filter_elem(out, "other", filters, val, "WOOD", 0, pammo.other_mats.at(0)); + set_filter_elem(out, "other", filters, val, "BONE", 1, pammo.other_mats.at(1)); } else { // TODO can we un-hardcode the values? for (int i = 0; i < bammo.other_mats_size(); ++i) { @@ -1017,35 +1035,35 @@ void StockpileSettingsSerializer::read_ammo(DeserializeMode mode, const vector const string& { return bammo.quality_core(idx); }, bammo.quality_core_size(), pammo.quality_core); - unserialize_list_quality("total", all, val, filters, + unserialize_list_quality(out, "total", all, val, filters, [&](const size_t& idx) -> const string& { return bammo.quality_total(idx); }, bammo.quality_total_size(), pammo.quality_total); }); } -bool StockpileSettingsSerializer::write_animals(StockpileSettings::AnimalsSet* animals) { +bool StockpileSettingsSerializer::write_animals(color_ostream& out, StockpileSettings::AnimalsSet* animals) { auto & panimals = mSettings->animals; bool all = panimals.empty_cages && panimals.empty_traps; animals->set_empty_cages(panimals.empty_cages); animals->set_empty_traps(panimals.empty_traps); - return serialize_list_creature( + return serialize_list_creature(out, [&](const string& token) { animals->add_enabled(token); }, panimals.enabled) && all; } -void StockpileSettingsSerializer::read_animals(DeserializeMode mode, const vector& filters) { +void StockpileSettingsSerializer::read_animals(color_ostream& out, DeserializeMode mode, const vector& filters) { auto & panimals = mSettings->animals; - read_category("animals", mode, + read_category(out, "animals", mode, std::bind(&StockpileSettings::has_animals, mBuffer), std::bind(&StockpileSettings::animals, mBuffer), mSettings->flags.whole, @@ -1058,10 +1076,10 @@ void StockpileSettingsSerializer::read_animals(DeserializeMode mode, const vecto [&](bool all, char val) { auto & banimals = mBuffer.animals(); - set_flag("cages", filters, all, val, banimals.empty_cages(), panimals.empty_cages); - set_flag("traps", filters, all, val, banimals.empty_traps(), panimals.empty_traps); + set_flag(out, "cages", filters, all, val, banimals.empty_cages(), panimals.empty_cages); + set_flag(out, "traps", filters, all, val, banimals.empty_traps(), panimals.empty_traps); - unserialize_list_creature("", all, val, filters, + unserialize_list_creature(out, "", all, val, filters, [&](const size_t& idx) -> const string& { return banimals.enabled(idx); }, banimals.enabled_size(), panimals.enabled); }); @@ -1071,7 +1089,7 @@ static bool armor_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid() && mi.material && mi.material->flags.is_set(material_flags::IS_METAL); } -bool StockpileSettingsSerializer::write_armor(StockpileSettings::ArmorSet* armor) { +bool StockpileSettingsSerializer::write_armor(color_ostream& out, StockpileSettings::ArmorSet* armor) { auto & parmor = mSettings->armor; bool all = parmor.unusable && parmor.usable; @@ -1080,72 +1098,72 @@ bool StockpileSettingsSerializer::write_armor(StockpileSettings::ArmorSet* armor armor->set_usable(parmor.usable); // armor type - all = serialize_list_itemdef( + all = serialize_list_itemdef(out, [&](const string& token) { armor->add_body(token); }, parmor.body, vector(world->raws.itemdefs.armor.begin(), world->raws.itemdefs.armor.end()), item_type::ARMOR) && all; // helm type - all = serialize_list_itemdef( + all = serialize_list_itemdef(out, [&](const string& token) { armor->add_head(token); }, parmor.head, vector(world->raws.itemdefs.helms.begin(), world->raws.itemdefs.helms.end()), item_type::HELM) && all; // shoes type - all = serialize_list_itemdef( + all = serialize_list_itemdef(out, [&](const string& token) { armor->add_feet(token); }, parmor.feet, vector(world->raws.itemdefs.shoes.begin(), world->raws.itemdefs.shoes.end()), item_type::SHOES) && all; // gloves type - all = serialize_list_itemdef( + all = serialize_list_itemdef(out, [&](const string& token) { armor->add_hands(token); }, parmor.hands, vector(world->raws.itemdefs.gloves.begin(), world->raws.itemdefs.gloves.end()), item_type::GLOVES) && all; // pant type - all = serialize_list_itemdef( + all = serialize_list_itemdef(out, [&](const string& token) { armor->add_legs(token); }, parmor.legs, vector(world->raws.itemdefs.pants.begin(), world->raws.itemdefs.pants.end()), item_type::PANTS) && all; // shield type - all = serialize_list_itemdef( + all = serialize_list_itemdef(out, [&](const string& token) { armor->add_shield(token); }, parmor.shield, vector(world->raws.itemdefs.shields.begin(), world->raws.itemdefs.shields.end()), item_type::SHIELD) && all; // materials - all = serialize_list_material( + all = serialize_list_material(out, armor_mat_is_allowed, [&](const string& token) { armor->add_mats(token); }, parmor.mats) && all; // other mats - all = serialize_list_other_mats( + all = serialize_list_other_mats(out, mOtherMatsWeaponsArmor.mats, [&](const string& token) { armor->add_other_mats(token); }, parmor.other_mats) && all; // quality core - all = serialize_list_quality([&](const string& token) { armor->add_quality_core(token); }, + all = serialize_list_quality(out, [&](const string& token) { armor->add_quality_core(token); }, parmor.quality_core) && all; // quality total - all = serialize_list_quality([&](const string& token) { armor->add_quality_total(token); }, + all = serialize_list_quality(out, [&](const string& token) { armor->add_quality_total(token); }, parmor.quality_total) && all; return all; } -void StockpileSettingsSerializer::read_armor(DeserializeMode mode, const vector& filters) { +void StockpileSettingsSerializer::read_armor(color_ostream& out, DeserializeMode mode, const vector& filters) { auto & parmor = mSettings->armor; - read_category("armor", mode, + read_category(out, "armor", mode, std::bind(&StockpileSettings::has_armor, mBuffer), std::bind(&StockpileSettings::armor, mBuffer), mSettings->flags.whole, @@ -1167,47 +1185,47 @@ void StockpileSettingsSerializer::read_armor(DeserializeMode mode, const vector< [&](bool all, char val) { auto & barmor = mBuffer.armor(); - set_flag("nouse", filters, all, val, barmor.unusable(), parmor.unusable); - set_flag("canuse", filters, all, val, barmor.usable(), parmor.usable); + set_flag(out, "nouse", filters, all, val, barmor.unusable(), parmor.unusable); + set_flag(out, "canuse", filters, all, val, barmor.usable(), parmor.usable); - unserialize_list_itemdef("body", all, val, filters, + unserialize_list_itemdef(out, "body", all, val, filters, [&](const size_t& idx) -> const string& { return barmor.body(idx); }, barmor.body_size(), parmor.body, item_type::ARMOR); - unserialize_list_itemdef("head", all, val, filters, + unserialize_list_itemdef(out, "head", all, val, filters, [&](const size_t& idx) -> const string& { return barmor.head(idx); }, barmor.head_size(), parmor.head, item_type::HELM); - unserialize_list_itemdef("feet", all, val, filters, + unserialize_list_itemdef(out, "feet", all, val, filters, [&](const size_t& idx) -> const string& { return barmor.feet(idx); }, barmor.feet_size(), parmor.feet, item_type::SHOES); - unserialize_list_itemdef("hands", all, val, filters, + unserialize_list_itemdef(out, "hands", all, val, filters, [&](const size_t& idx) -> const string& { return barmor.hands(idx); }, barmor.hands_size(), parmor.hands, item_type::GLOVES); - unserialize_list_itemdef("legs", all, val, filters, + unserialize_list_itemdef(out, "legs", all, val, filters, [&](const size_t& idx) -> const string& { return barmor.legs(idx); }, barmor.legs_size(), parmor.legs, item_type::PANTS); - unserialize_list_itemdef("shield", all, val, filters, + unserialize_list_itemdef(out, "shield", all, val, filters, [&](const size_t& idx) -> const string& { return barmor.shield(idx); }, barmor.shield_size(), parmor.shield, item_type::SHIELD); - unserialize_list_material("mats", all, val, filters, + unserialize_list_material(out, "mats", all, val, filters, armor_mat_is_allowed, [&](const size_t& idx) -> const string& { return barmor.mats(idx); }, barmor.mats_size(), parmor.mats); - unserialize_list_other_mats("other", all, val, filters, + unserialize_list_other_mats(out, "other", all, val, filters, mOtherMatsWeaponsArmor.mats, [&](const size_t& idx) -> const string& { return barmor.other_mats(idx); }, barmor.other_mats_size(), parmor.other_mats); - unserialize_list_quality("core", all, val, filters, + unserialize_list_quality(out, "core", all, val, filters, [&](const size_t& idx) -> const string& { return barmor.quality_core(idx); }, barmor.quality_core_size(), parmor.quality_core); - unserialize_list_quality("total", all, val, filters, + unserialize_list_quality(out, "total", all, val, filters, [&](const size_t& idx) -> const string& { return barmor.quality_total(idx); }, barmor.quality_total_size(), parmor.quality_total); }); @@ -1221,31 +1239,32 @@ static bool blocks_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_METAL) || mi.material->flags.is_set(material_flags::IS_STONE)); } -bool StockpileSettingsSerializer::write_bars_blocks(StockpileSettings::BarsBlocksSet* bars_blocks) { - bool all = serialize_list_material( +bool StockpileSettingsSerializer::write_bars_blocks(color_ostream& out, StockpileSettings::BarsBlocksSet* bars_blocks) { + bool all = serialize_list_material(out, bars_mat_is_allowed, [&](const string& token) { bars_blocks->add_bars_mats(token); }, mSettings->bars_blocks.bars_mats); - all = serialize_list_material( + all = serialize_list_material(out, blocks_mat_is_allowed, [&](const string& token) { bars_blocks->add_blocks_mats(token); }, mSettings->bars_blocks.blocks_mats) && all; - all = serialize_list_other_mats( + all = serialize_list_other_mats(out, mOtherMatsBars.mats, [&](const string& token) { bars_blocks->add_bars_other_mats(token); }, mSettings->bars_blocks.bars_other_mats) && all; - all = serialize_list_other_mats( + all = serialize_list_other_mats(out, mOtherMatsBlocks.mats, [&](const string& token) { bars_blocks->add_blocks_other_mats(token); }, mSettings->bars_blocks.blocks_other_mats) && all; return all; } -void StockpileSettingsSerializer::read_bars_blocks(DeserializeMode mode, const vector& filters) { +void StockpileSettingsSerializer::read_bars_blocks(color_ostream& out, DeserializeMode mode, const vector& filters) { auto & pbarsblocks = mSettings->bars_blocks; - read_category("bars_blocks", mode, + read_category(out, "bars_blocks", mode, +>>>>>>> 7a1aacdca (import and export logistics features) std::bind(&StockpileSettings::has_barsblocks, mBuffer), std::bind(&StockpileSettings::barsblocks, mBuffer), mSettings->flags.whole, @@ -1259,68 +1278,69 @@ void StockpileSettingsSerializer::read_bars_blocks(DeserializeMode mode, const v [&](bool all, char val) { auto & bbarsblocks = mBuffer.barsblocks(); - unserialize_list_material("mats/bars", all, val, filters, bars_mat_is_allowed, + unserialize_list_material(out, "mats/bars", all, val, filters, bars_mat_is_allowed, [&](const size_t& idx) -> const string& { return bbarsblocks.bars_mats(idx); }, bbarsblocks.bars_mats_size(), pbarsblocks.bars_mats); - unserialize_list_other_mats("other/bars", all, val, filters, + unserialize_list_other_mats(out, "other/bars", all, val, filters, mOtherMatsBars.mats, [&](const size_t& idx) -> const string& { return bbarsblocks.bars_other_mats(idx); }, bbarsblocks.bars_other_mats_size(), pbarsblocks.bars_other_mats); - unserialize_list_material("mats/blocks", all, val, filters, + unserialize_list_material(out, "mats/blocks", all, val, filters, blocks_mat_is_allowed, [&](const size_t& idx) -> const string& { return bbarsblocks.blocks_mats(idx); }, bbarsblocks.blocks_mats_size(), pbarsblocks.blocks_mats); - unserialize_list_other_mats("other/blocks", all, val, filters, + unserialize_list_other_mats(out, "other/blocks", all, val, filters, mOtherMatsBlocks.mats, [&](const size_t& idx) -> const string& { return bbarsblocks.blocks_other_mats(idx); }, bbarsblocks.blocks_other_mats_size(), pbarsblocks.blocks_other_mats); }); } -bool StockpileSettingsSerializer::write_cloth(StockpileSettings::ClothSet* cloth) { +bool StockpileSettingsSerializer::write_cloth(color_ostream& out, StockpileSettings::ClothSet* cloth) { bool all = true; - all = serialize_list_organic_mat( + all = serialize_list_organic_mat(out, [&](const string& token) { cloth->add_thread_silk(token); }, &mSettings->cloth.thread_silk, organic_mat_category::Silk) && all; - all = serialize_list_organic_mat( + all = serialize_list_organic_mat(out, [&](const string& token) { cloth->add_thread_plant(token); }, &mSettings->cloth.thread_plant, organic_mat_category::PlantFiber) && all; - all = serialize_list_organic_mat( + all = serialize_list_organic_mat(out, [&](const string& token) { cloth->add_thread_yarn(token); }, &mSettings->cloth.thread_yarn, organic_mat_category::Yarn) && all; - all = serialize_list_organic_mat( + all = serialize_list_organic_mat(out, [&](const string& token) { cloth->add_thread_metal(token); }, &mSettings->cloth.thread_metal, organic_mat_category::MetalThread) && all; - all = serialize_list_organic_mat( + all = serialize_list_organic_mat(out, [&](const string& token) { cloth->add_cloth_silk(token); }, &mSettings->cloth.cloth_silk, organic_mat_category::Silk) && all; - all = serialize_list_organic_mat( + all = serialize_list_organic_mat(out, [&](const string& token) { cloth->add_cloth_plant(token); }, &mSettings->cloth.cloth_plant, organic_mat_category::PlantFiber) && all; - all = serialize_list_organic_mat( + all = serialize_list_organic_mat(out, [&](const string& token) { cloth->add_cloth_yarn(token); }, &mSettings->cloth.cloth_yarn, organic_mat_category::Yarn) && all; - all = serialize_list_organic_mat( + all = serialize_list_organic_mat(out, [&](const string& token) { cloth->add_cloth_metal(token); }, &mSettings->cloth.cloth_metal, organic_mat_category::MetalThread) && all; return all; } -void StockpileSettingsSerializer::read_cloth(DeserializeMode mode, const vector& filters) { +void StockpileSettingsSerializer::read_cloth(color_ostream& out, DeserializeMode mode, const vector& filters) { auto & pcloth = mSettings->cloth; - read_category("cloth", mode, + read_category(out, "cloth", mode, +>>>>>>> 7a1aacdca (import and export logistics features) std::bind(&StockpileSettings::has_cloth, mBuffer), std::bind(&StockpileSettings::cloth, mBuffer), mSettings->flags.whole, @@ -1338,35 +1358,35 @@ void StockpileSettingsSerializer::read_cloth(DeserializeMode mode, const vector< [&](bool all, char val) { auto & bcloth = mBuffer.cloth(); - unserialize_list_organic_mat("thread/silk", all, val, filters, + unserialize_list_organic_mat(out, "thread/silk", all, val, filters, [&](size_t idx) -> string { return bcloth.thread_silk(idx); }, bcloth.thread_silk_size(), pcloth.thread_silk, organic_mat_category::Silk); - unserialize_list_organic_mat("thread/plant", all, val, filters, + unserialize_list_organic_mat(out, "thread/plant", all, val, filters, [&](size_t idx) -> string { return bcloth.thread_plant(idx); }, bcloth.thread_plant_size(), pcloth.thread_plant, organic_mat_category::PlantFiber); - unserialize_list_organic_mat("thread/yarn", all, val, filters, + unserialize_list_organic_mat(out, "thread/yarn", all, val, filters, [&](size_t idx) -> string { return bcloth.thread_yarn(idx); }, bcloth.thread_yarn_size(), pcloth.thread_yarn, organic_mat_category::Yarn); - unserialize_list_organic_mat("thread/metal", all, val, filters, + unserialize_list_organic_mat(out, "thread/metal", all, val, filters, [&](size_t idx) -> string { return bcloth.thread_metal(idx); }, bcloth.thread_metal_size(), pcloth.thread_metal, organic_mat_category::MetalThread); - unserialize_list_organic_mat("cloth/silk", all, val, filters, + unserialize_list_organic_mat(out, "cloth/silk", all, val, filters, [&](size_t idx) -> string { return bcloth.cloth_silk(idx); }, bcloth.cloth_silk_size(), pcloth.cloth_silk, organic_mat_category::Silk); - unserialize_list_organic_mat("cloth/plant", all, val, filters, + unserialize_list_organic_mat(out, "cloth/plant", all, val, filters, [&](size_t idx) -> string { return bcloth.cloth_plant(idx); }, bcloth.cloth_plant_size(), pcloth.cloth_plant, organic_mat_category::PlantFiber); - unserialize_list_organic_mat("cloth/yarn", all, val, filters, + unserialize_list_organic_mat(out, "cloth/yarn", all, val, filters, [&](size_t idx) -> string { return bcloth.cloth_yarn(idx); }, bcloth.cloth_yarn_size(), pcloth.cloth_yarn, organic_mat_category::Yarn); - unserialize_list_organic_mat("cloth/metal", all, val, filters, + unserialize_list_organic_mat(out, "cloth/metal", all, val, filters, [&](size_t idx) -> string { return bcloth.cloth_metal(idx); }, bcloth.cloth_metal_size(), pcloth.cloth_metal, organic_mat_category::MetalThread); }); @@ -1376,16 +1396,16 @@ static bool coins_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid(); } -bool StockpileSettingsSerializer::write_coins(StockpileSettings::CoinSet* coins) { - return serialize_list_material( +bool StockpileSettingsSerializer::write_coins(color_ostream& out, StockpileSettings::CoinSet* coins) { + return serialize_list_material(out, coins_mat_is_allowed, [&](const string& token) { coins->add_mats(token); }, mSettings->coins.mats); } -void StockpileSettingsSerializer::read_coins(DeserializeMode mode, const vector& filters) { +void StockpileSettingsSerializer::read_coins(color_ostream& out, DeserializeMode mode, const vector& filters) { auto & pcoins = mSettings->coins; - read_category("coin", mode, + read_category(out, "coin", mode, std::bind(&StockpileSettings::has_coin, mBuffer), std::bind(&StockpileSettings::coin, mBuffer), mSettings->flags.whole, @@ -1396,7 +1416,7 @@ void StockpileSettingsSerializer::read_coins(DeserializeMode mode, const vector< [&](bool all, char val) { auto & bcoin = mBuffer.coin(); - unserialize_list_material("", all, val, filters, coins_mat_is_allowed, + unserialize_list_material(out, "", all, val, filters, coins_mat_is_allowed, [&](const size_t& idx) -> const string& { return bcoin.mats(idx); }, bcoin.mats_size(), pcoins.mats); }); @@ -1439,33 +1459,33 @@ static bool finished_goods_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_GEM) || mi.material->flags.is_set(material_flags::IS_METAL) || mi.material->flags.is_set(material_flags::IS_STONE)); } -bool StockpileSettingsSerializer::write_finished_goods(StockpileSettings::FinishedGoodsSet* finished_goods) { - bool all = serialize_list_item_type( +bool StockpileSettingsSerializer::write_finished_goods(color_ostream& out, StockpileSettings::FinishedGoodsSet* finished_goods) { + bool all = serialize_list_item_type(out, finished_goods_type_is_allowed, [&](const string& token) { finished_goods->add_type(token); }, mSettings->finished_goods.type); - all = serialize_list_material( + all = serialize_list_material(out, finished_goods_mat_is_allowed, [&](const string& token) { finished_goods->add_mats(token); }, mSettings->finished_goods.mats) && all; - all = serialize_list_other_mats( + all = serialize_list_other_mats(out, mOtherMatsFinishedGoods.mats, [&](const string& token) { finished_goods->add_other_mats(token); }, mSettings->finished_goods.other_mats) && all; - all = serialize_list_quality([&](const string& token) { finished_goods->add_quality_core(token); }, + all = serialize_list_quality([&](out, const string& token) { finished_goods->add_quality_core(token); }, mSettings->finished_goods.quality_core) && all; - all = serialize_list_quality([&](const string& token) { finished_goods->add_quality_total(token); }, + all = serialize_list_quality([&](out, const string& token) { finished_goods->add_quality_total(token); }, mSettings->finished_goods.quality_total) && all; return all; } -void StockpileSettingsSerializer::read_finished_goods(DeserializeMode mode, const vector& filters) { +void StockpileSettingsSerializer::read_finished_goods(color_ostream& out, DeserializeMode mode, const vector& filters) { auto & pfinished_goods = mSettings->finished_goods; - read_category("finished_goods", mode, + read_category(out, "finished_goods", mode, std::bind(&StockpileSettings::has_finished_goods, mBuffer), std::bind(&StockpileSettings::finished_goods, mBuffer), mSettings->flags.whole, @@ -1480,23 +1500,23 @@ void StockpileSettingsSerializer::read_finished_goods(DeserializeMode mode, cons [&](bool all, char val) { auto & bfinished_goods = mBuffer.finished_goods(); - unserialize_list_item_type("type", all, val, filters, finished_goods_type_is_allowed, + unserialize_list_item_type(out, "type", all, val, filters, finished_goods_type_is_allowed, [&](const size_t& idx) -> const string& { return bfinished_goods.type(idx); }, bfinished_goods.type_size(), pfinished_goods.type); - unserialize_list_material("mats", all, val, filters, finished_goods_mat_is_allowed, + unserialize_list_material(out, "mats", all, val, filters, finished_goods_mat_is_allowed, [&](const size_t& idx) -> const string& { return bfinished_goods.mats(idx); }, bfinished_goods.mats_size(), pfinished_goods.mats); - unserialize_list_other_mats("other", all, val, filters, mOtherMatsFinishedGoods.mats, + unserialize_list_other_mats(out, "other", all, val, filters, mOtherMatsFinishedGoods.mats, [&](const size_t& idx) -> const string& { return bfinished_goods.other_mats(idx); }, bfinished_goods.other_mats_size(), pfinished_goods.other_mats); - unserialize_list_quality("core", all, val, filters, + unserialize_list_quality(out, "core", all, val, filters, [&](const size_t& idx) -> const string& { return bfinished_goods.quality_core(idx); }, bfinished_goods.quality_core_size(), pfinished_goods.quality_core); - unserialize_list_quality("total", all, val, filters, + unserialize_list_quality(out, "total", all, val, filters, [&](const size_t& idx) -> const string& { return bfinished_goods.quality_total(idx); }, bfinished_goods.quality_total_size(), pfinished_goods.quality_total); }); @@ -1684,7 +1704,7 @@ food_pair StockpileSettingsSerializer::food_map(organic_mat_category::organic_ma return food_pair(); } -bool StockpileSettingsSerializer::write_food(StockpileSettings::FoodSet* food) { +bool StockpileSettingsSerializer::write_food(color_ostream& out, StockpileSettings::FoodSet* food) { auto & pfood = mSettings->food; bool all = pfood.prepared_meals; @@ -1696,19 +1716,19 @@ bool StockpileSettingsSerializer::write_food(StockpileSettings::FoodSet* food) { food_pair p = food_map((organic_mat_category)mat_category); if (!p.valid) continue; - all = serialize_list_organic_mat(p.set_value, p.stockpile_values, + all = serialize_list_organic_mat(out, p.set_value, p.stockpile_values, (organic_mat_category)mat_category) && all; } return all; } -void StockpileSettingsSerializer::read_food(DeserializeMode mode, const vector& filters) { +void StockpileSettingsSerializer::read_food(color_ostream& out, DeserializeMode mode, const vector& filters) { using df::enums::organic_mat_category::organic_mat_category; using traits = df::enum_traits; auto & pfood = mSettings->food; - read_category("food", mode, + read_category(out, "food", mode, std::bind(&StockpileSettings::has_food, mBuffer), std::bind(&StockpileSettings::food, mBuffer), mSettings->flags.whole, @@ -1725,25 +1745,24 @@ void StockpileSettingsSerializer::read_food(DeserializeMode mode, const vectorflags.is_set(material_flags::IS_METAL) || mi.material->flags.is_set(material_flags::IS_STONE)); } -bool StockpileSettingsSerializer::write_furniture(StockpileSettings::FurnitureSet* furniture) { +bool StockpileSettingsSerializer::write_furniture(color_ostream& out, StockpileSettings::FurnitureSet* furniture) { using df::enums::furniture_type::furniture_type; using type_traits = df::enum_traits; @@ -1757,30 +1776,30 @@ bool StockpileSettingsSerializer::write_furniture(StockpileSettings::FurnitureSe } string f_type(type_traits::key_table[i]); furniture->add_type(f_type); - DEBUG(log).print("furniture_type %zd is %s\n", i, f_type.c_str()); + DEBUG(log, out).print("furniture_type %zd is %s\n", i, f_type.c_str()); } - all = serialize_list_material( + all = serialize_list_material(out, furniture_mat_is_allowed, [&](const string& token) { furniture->add_mats(token); }, pfurniture.mats) && all; - all = serialize_list_other_mats( + all = serialize_list_other_mats(out, mOtherMatsFurniture.mats, [&](const string& token) { furniture->add_other_mats(token); }, pfurniture.other_mats) && all; - all = serialize_list_quality( + all = serialize_list_quality(out, [&](const string& token) { furniture->add_quality_core(token); }, pfurniture.quality_core) && all; - all = serialize_list_quality( + all = serialize_list_quality(out, [&](const string& token) { furniture->add_quality_total(token); }, pfurniture.quality_total) && all; return all; } -void StockpileSettingsSerializer::read_furniture(DeserializeMode mode, const vector& filters) { +void StockpileSettingsSerializer::read_furniture(color_ostream& out, DeserializeMode mode, const vector& filters) { auto & pfurniture = mSettings->furniture; - read_category("furniture", mode, + read_category(out, "furniture", mode, std::bind(&StockpileSettings::has_furniture, mBuffer), std::bind(&StockpileSettings::furniture, mBuffer), mSettings->flags.whole, @@ -1803,33 +1822,33 @@ void StockpileSettingsSerializer::read_furniture(DeserializeMode mode, const vec if (all) { for (size_t idx = 0; idx < num_elems; ++idx) { string id = ENUM_KEY_STR(furniture_type, (df::furniture_type)idx); - set_filter_elem("type", filters, val, id, idx, pfurniture.type.at(idx)); + set_filter_elem(out, "type", filters, val, id, idx, pfurniture.type.at(idx)); } } else { for (int i = 0; i < bfurniture.type_size(); ++i) { const string token = bfurniture.type(i); df::enum_traits::base_type idx = linear_index(type_traits, token); if (idx < 0 || size_t(idx) >= pfurniture.type.size()) { - WARN(log).print("furniture type index invalid %s, idx=%d\n", token.c_str(), idx); + WARN(log, out).print("furniture type index invalid %s, idx=%d\n", token.c_str(), idx); continue; } - set_filter_elem("type", filters, val, token, idx, pfurniture.type.at(idx)); + set_filter_elem(out, "type", filters, val, token, idx, pfurniture.type.at(idx)); } } - unserialize_list_material("mats", all, val, filters, furniture_mat_is_allowed, + unserialize_list_material(out, "mats", all, val, filters, furniture_mat_is_allowed, [&](const size_t& idx) -> const string& { return bfurniture.mats(idx); }, bfurniture.mats_size(), pfurniture.mats); - unserialize_list_other_mats("other", all, val, filters, + unserialize_list_other_mats(out, "other", all, val, filters, mOtherMatsFurniture.mats, [&](const size_t& idx) -> const string& { return bfurniture.other_mats(idx); }, bfurniture.other_mats_size(), pfurniture.other_mats); - unserialize_list_quality("core", all, val, filters, + unserialize_list_quality(out, "core", all, val, filters, [&](const size_t& idx) -> const string& { return bfurniture.quality_core(idx); }, bfurniture.quality_core_size(), pfurniture.quality_core); - unserialize_list_quality("total", all, val, filters, + unserialize_list_quality(out, "total", all, val, filters, [&](const size_t& idx) -> const string& { return bfurniture.quality_total(idx); }, bfurniture.quality_total_size(), pfurniture.quality_total); }); @@ -1847,17 +1866,17 @@ static bool gem_other_mat_is_allowed(MaterialInfo& mi) { return mi.isValid() && (mi.getToken() == "GLASS_GREEN" || mi.getToken() == "GLASS_CLEAR" || mi.getToken() == "GLASS_CRYSTAL"); } -bool StockpileSettingsSerializer::write_gems(StockpileSettings::GemsSet* gems) { +bool StockpileSettingsSerializer::write_gems(color_ostream& out, StockpileSettings::GemsSet* gems) { MaterialInfo mi; auto & pgems = mSettings->gems; - bool all = serialize_list_material( + bool all = serialize_list_material(out, gem_mat_is_allowed, [&](const string& token) { gems->add_rough_mats(token); }, pgems.rough_mats); - all = serialize_list_material( + all = serialize_list_material(out, gem_cut_mat_is_allowed, [&](const string& token) { gems->add_cut_mats(token); }, pgems.cut_mats) && all; @@ -1870,7 +1889,7 @@ bool StockpileSettingsSerializer::write_gems(StockpileSettings::GemsSet* gems) { mi.decode(i, -1); if (!gem_other_mat_is_allowed(mi)) continue; - DEBUG(log).print("gem rough_other mat %zd is %s\n", i, mi.getToken().c_str()); + DEBUG(log, out).print("gem rough_other mat %zd is %s\n", i, mi.getToken().c_str()); gems->add_rough_other_mats(mi.getToken()); } @@ -1884,16 +1903,16 @@ bool StockpileSettingsSerializer::write_gems(StockpileSettings::GemsSet* gems) { mi.decode(0, i); if (!gem_other_mat_is_allowed(mi)) continue; - DEBUG(log).print("gem cut_other mat %zd is %s\n", i, mi.getToken().c_str()); + DEBUG(log, out).print("gem cut_other mat %zd is %s\n", i, mi.getToken().c_str()); gems->add_cut_other_mats(mi.getToken()); } return all; } -void StockpileSettingsSerializer::read_gems(DeserializeMode mode, const vector& filters) { +void StockpileSettingsSerializer::read_gems(color_ostream& out, DeserializeMode mode, const vector& filters) { auto & pgems = mSettings->gems; - read_category("gems", mode, + read_category(out, "gems", mode, std::bind(&StockpileSettings::has_gems, mBuffer), std::bind(&StockpileSettings::gems, mBuffer), mSettings->flags.whole, @@ -1907,11 +1926,11 @@ void StockpileSettingsSerializer::read_gems(DeserializeMode mode, const vector const string& { return bgems.rough_mats(idx); }, bgems.rough_mats_size(), pgems.rough_mats); - unserialize_list_material("mats/cut", all, val, filters, gem_cut_mat_is_allowed, + unserialize_list_material(out, "mats/cut", all, val, filters, gem_cut_mat_is_allowed, [&](const size_t& idx) -> const string& { return bgems.cut_mats(idx); }, bgems.cut_mats_size(), pgems.cut_mats); @@ -1923,11 +1942,11 @@ void StockpileSettingsSerializer::read_gems(DeserializeMode mode, const vectoradd_mats(id); }, &mSettings->leather.mats, organic_mat_category::Leather); } -void StockpileSettingsSerializer::read_leather(DeserializeMode mode, const vector& filters) { +void StockpileSettingsSerializer::read_leather(color_ostream& out, DeserializeMode mode, const vector& filters) { auto & pleather = mSettings->leather; - read_category("leather", mode, + read_category(out, "leather", mode, std::bind(&StockpileSettings::has_leather, mBuffer), std::bind(&StockpileSettings::leather, mBuffer), mSettings->flags.whole, @@ -1967,21 +1986,21 @@ void StockpileSettingsSerializer::read_leather(DeserializeMode mode, const vecto [&](bool all, char val) { auto & bleather = mBuffer.leather(); - unserialize_list_organic_mat("", all, val, filters, + unserialize_list_organic_mat(out, "", all, val, filters, [&](size_t idx) -> string { return bleather.mats(idx); }, bleather.mats_size(), pleather.mats, organic_mat_category::Leather); }); } -bool StockpileSettingsSerializer::write_corpses(StockpileSettings::CorpsesSet* corpses) { - return serialize_list_creature( +bool StockpileSettingsSerializer::write_corpses(color_ostream& out, StockpileSettings::CorpsesSet* corpses) { + return serialize_list_creature(out, [&](const string& token) { corpses->add_corpses(token); }, mSettings->corpses.corpses); } -void StockpileSettingsSerializer::read_corpses(DeserializeMode mode, const vector& filters) { +void StockpileSettingsSerializer::read_corpses(color_ostream& out, DeserializeMode mode, const vector& filters) { auto & pcorpses = mSettings->corpses; - read_category("corpses", mode, + read_category(out, "corpses", mode, std::bind(&StockpileSettings::has_corpses_v50, mBuffer), std::bind(&StockpileSettings::corpses_v50, mBuffer), mSettings->flags.whole, @@ -1991,7 +2010,7 @@ void StockpileSettingsSerializer::read_corpses(DeserializeMode mode, const vecto }, [&](bool all, char val) { auto & bcorpses = mBuffer.corpses_v50(); - unserialize_list_creature("", all, val, filters, + unserialize_list_creature(out, "", all, val, filters, [&](const size_t& idx) -> const string& { return bcorpses.corpses(idx); }, bcorpses.corpses_size(), pcorpses.corpses); }); @@ -2006,48 +2025,48 @@ static bool refuse_type_is_allowed(item_type::item_type type) { return true; } -bool StockpileSettingsSerializer::write_refuse(StockpileSettings::RefuseSet* refuse) { +bool StockpileSettingsSerializer::write_refuse(color_ostream& out, StockpileSettings::RefuseSet* refuse) { auto & prefuse = mSettings->refuse; bool all = prefuse.fresh_raw_hide && prefuse.rotten_raw_hide; refuse->set_fresh_raw_hide(prefuse.fresh_raw_hide); refuse->set_rotten_raw_hide(prefuse.rotten_raw_hide); - all = serialize_list_item_type(refuse_type_is_allowed, + all = serialize_list_item_type(out, refuse_type_is_allowed, [&](const string& token) { refuse->add_type(token); }, prefuse.type) && all; - all = serialize_list_creature( + all = serialize_list_creature(out, [&](const string& token) { refuse->add_corpses(token); }, prefuse.corpses) && all; - all = serialize_list_creature( + all = serialize_list_creature(out, [&](const string& token) { refuse->add_body_parts(token); }, prefuse.body_parts) && all; - all = serialize_list_creature( + all = serialize_list_creature(out, [&](const string& token) { refuse->add_skulls(token); }, prefuse.skulls) && all; - all = serialize_list_creature( + all = serialize_list_creature(out, [&](const string& token) { refuse->add_bones(token); }, prefuse.bones) && all; - all = serialize_list_creature( + all = serialize_list_creature(out, [&](const string& token) { refuse->add_hair(token); }, prefuse.hair) && all; - all = serialize_list_creature( + all = serialize_list_creature(out, [&](const string& token) { refuse->add_shells(token); }, prefuse.shells) && all; - all = serialize_list_creature( + all = serialize_list_creature(out, [&](const string& token) { refuse->add_teeth(token); }, prefuse.teeth) && all; - all = serialize_list_creature( + all = serialize_list_creature(out, [&](const string& token) { refuse->add_horns(token); }, prefuse.horns) && all; return all; } -void StockpileSettingsSerializer::read_refuse(DeserializeMode mode, const vector& filters) { +void StockpileSettingsSerializer::read_refuse(color_ostream& out, DeserializeMode mode, const vector& filters) { auto & prefuse = mSettings->refuse; - read_category("refuse", mode, + read_category(out, "refuse", mode, std::bind(&StockpileSettings::has_refuse, mBuffer), std::bind(&StockpileSettings::refuse, mBuffer), mSettings->flags.whole, @@ -2068,56 +2087,56 @@ void StockpileSettingsSerializer::read_refuse(DeserializeMode mode, const vector [&](bool all, char val) { auto & brefuse = mBuffer.refuse(); - set_flag("rawhide/fresh", filters, all, val, brefuse.fresh_raw_hide(), prefuse.fresh_raw_hide); - set_flag("rawhide/rotten", filters, all, val, brefuse.rotten_raw_hide(), prefuse.rotten_raw_hide); + set_flag(out, "rawhide/fresh", filters, all, val, brefuse.fresh_raw_hide(), prefuse.fresh_raw_hide); + set_flag(out, "rawhide/rotten", filters, all, val, brefuse.rotten_raw_hide(), prefuse.rotten_raw_hide); - unserialize_list_item_type("type", all, val, filters, refuse_type_is_allowed, + unserialize_list_item_type(out, "type", all, val, filters, refuse_type_is_allowed, [&](const size_t& idx) -> const string& { return brefuse.type(idx); }, brefuse.type_size(), prefuse.type); - unserialize_list_creature("corpses", all, val, filters, + unserialize_list_creature(out, "corpses", all, val, filters, [&](const size_t& idx) -> const string& { return brefuse.corpses(idx); }, brefuse.corpses_size(), prefuse.corpses); - unserialize_list_creature("bodyparts", all, val, filters, + unserialize_list_creature(out, "bodyparts", all, val, filters, [&](const size_t& idx) -> const string& { return brefuse.body_parts(idx); }, brefuse.body_parts_size(), prefuse.body_parts); - unserialize_list_creature("skulls", all, val, filters, + unserialize_list_creature(out, "skulls", all, val, filters, [&](const size_t& idx) -> const string& { return brefuse.skulls(idx); }, brefuse.skulls_size(), prefuse.skulls); - unserialize_list_creature("bones", all, val, filters, + unserialize_list_creature(out, "bones", all, val, filters, [&](const size_t& idx) -> const string& { return brefuse.bones(idx); }, brefuse.bones_size(), prefuse.bones); - unserialize_list_creature("hair", all, val, filters, + unserialize_list_creature(out, "hair", all, val, filters, [&](const size_t& idx) -> const string& { return brefuse.hair(idx); }, brefuse.hair_size(), prefuse.hair); - unserialize_list_creature("shells", all, val, filters, + unserialize_list_creature(out, "shells", all, val, filters, [&](const size_t& idx) -> const string& { return brefuse.shells(idx); }, brefuse.shells_size(), prefuse.shells); - unserialize_list_creature("teeth", all, val, filters, + unserialize_list_creature(out, "teeth", all, val, filters, [&](const size_t& idx) -> const string& { return brefuse.teeth(idx); }, brefuse.teeth_size(), prefuse.teeth); - unserialize_list_creature("horns", all, val, filters, + unserialize_list_creature(out, "horns", all, val, filters, [&](const size_t& idx) -> const string& { return brefuse.horns(idx); }, brefuse.horns_size(), prefuse.horns); }); } -bool StockpileSettingsSerializer::write_sheet(StockpileSettings::SheetSet* sheet) { - bool all = serialize_list_organic_mat( +bool StockpileSettingsSerializer::write_sheet(color_ostream& out, StockpileSettings::SheetSet* sheet) { + bool all = serialize_list_organic_mat(out, [&](const string& token) { sheet->add_paper(token); }, &mSettings->sheet.paper, organic_mat_category::Paper); - all = serialize_list_organic_mat( + all = serialize_list_organic_mat(out, [&](const string& token) { sheet->add_parchment(token); }, &mSettings->sheet.parchment, organic_mat_category::Parchment) && all; return all; } -void StockpileSettingsSerializer::read_sheet(DeserializeMode mode, const vector& filters) { +void StockpileSettingsSerializer::read_sheet(color_ostream& out, DeserializeMode mode, const vector& filters) { auto & psheet = mSettings->sheet; - read_category("sheet", mode, + read_category(out, "sheet", mode, std::bind(&StockpileSettings::has_sheet, mBuffer), std::bind(&StockpileSettings::sheet, mBuffer), mSettings->flags.whole, @@ -2129,11 +2148,11 @@ void StockpileSettingsSerializer::read_sheet(DeserializeMode mode, const vector< [&](bool all, char val) { auto & bsheet = mBuffer.sheet(); - unserialize_list_organic_mat("paper", all, val, filters, + unserialize_list_organic_mat(out, "paper", all, val, filters, [&](size_t idx) -> string { return bsheet.paper(idx); }, bsheet.paper_size(), psheet.paper, organic_mat_category::Paper); - unserialize_list_organic_mat("parchment", all, val, filters, + unserialize_list_organic_mat(out, "parchment", all, val, filters, [&](size_t idx) -> string { return bsheet.parchment(idx); }, bsheet.parchment_size(), psheet.parchment, organic_mat_category::Parchment); }); @@ -2147,16 +2166,16 @@ static bool stone_is_allowed(const MaterialInfo& mi) { return is_allowed_soil || is_allowed_stone; } -bool StockpileSettingsSerializer::write_stone(StockpileSettings::StoneSet* stone) { - return serialize_list_material( +bool StockpileSettingsSerializer::write_stone(color_ostream& out, StockpileSettings::StoneSet* stone) { + return serialize_list_material(out, stone_is_allowed, [&](const string& token) { stone->add_mats(token); }, mSettings->stone.mats); } -void StockpileSettingsSerializer::read_stone(DeserializeMode mode, const vector& filters) { +void StockpileSettingsSerializer::read_stone(color_ostream& out, DeserializeMode mode, const vector& filters) { auto & pstone = mSettings->stone; - read_category("stone", mode, + read_category(out, "stone", mode, std::bind(&StockpileSettings::has_stone, mBuffer), std::bind(&StockpileSettings::stone, mBuffer), mSettings->flags.whole, @@ -2167,7 +2186,7 @@ void StockpileSettingsSerializer::read_stone(DeserializeMode mode, const vector< [&](bool all, char val) { auto & bstone = mBuffer.stone(); - unserialize_list_material("", all, val, filters, stone_is_allowed, + unserialize_list_material(out, "", all, val, filters, stone_is_allowed, [&](const size_t& idx) -> const string& { return bstone.mats(idx); }, bstone.mats_size(), pstone.mats); }); @@ -2177,49 +2196,49 @@ static bool weapons_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_METAL) || mi.material->flags.is_set(material_flags::IS_STONE)); } -bool StockpileSettingsSerializer::write_weapons(StockpileSettings::WeaponsSet* weapons) { +bool StockpileSettingsSerializer::write_weapons(color_ostream& out, StockpileSettings::WeaponsSet* weapons) { auto & pweapons = mSettings->weapons; bool all = pweapons.unusable && pweapons.usable; weapons->set_unusable(pweapons.unusable); weapons->set_usable(pweapons.usable); - all = serialize_list_itemdef( + all = serialize_list_itemdef(out, [&](const string& token) { weapons->add_weapon_type(token); }, pweapons.weapon_type, vector(world->raws.itemdefs.weapons.begin(), world->raws.itemdefs.weapons.end()), item_type::WEAPON) && all; - all = serialize_list_itemdef( + all = serialize_list_itemdef(out, [&](const string& token) { weapons->add_trapcomp_type(token); }, pweapons.trapcomp_type, vector(world->raws.itemdefs.trapcomps.begin(), world->raws.itemdefs.trapcomps.end()), item_type::TRAPCOMP) && all; - all = serialize_list_material( + all = serialize_list_material(out, weapons_mat_is_allowed, [&](const string& token) { weapons->add_mats(token); }, pweapons.mats) && all; - all = serialize_list_other_mats( + all = serialize_list_other_mats(out, mOtherMatsWeaponsArmor.mats, [&](const string& token) { weapons->add_other_mats(token); }, pweapons.other_mats) && all; - all = serialize_list_quality( + all = serialize_list_quality(out, [&](const string& token) { weapons->add_quality_core(token); }, pweapons.quality_core) && all; - all = serialize_list_quality( + all = serialize_list_quality(out, [&](const string& token) { weapons->add_quality_total(token); }, pweapons.quality_total) && all; return all; } -void StockpileSettingsSerializer::read_weapons(DeserializeMode mode, const vector& filters) { +void StockpileSettingsSerializer::read_weapons(color_ostream& out, DeserializeMode mode, const vector& filters) { auto & pweapons = mSettings->weapons; - read_category("weapons", mode, + read_category(out, "weapons", mode, std::bind(&StockpileSettings::has_weapons, mBuffer), std::bind(&StockpileSettings::weapons, mBuffer), mSettings->flags.whole, @@ -2237,30 +2256,30 @@ void StockpileSettingsSerializer::read_weapons(DeserializeMode mode, const vecto [&](bool all, char val) { auto & bweapons = mBuffer.weapons(); - set_flag("nouse", filters, all, val, bweapons.unusable(), pweapons.unusable); - set_flag("canuse", filters, all, val, bweapons.usable(), pweapons.usable); + set_flag(out, "nouse", filters, all, val, bweapons.unusable(), pweapons.unusable); + set_flag(out, "canuse", filters, all, val, bweapons.usable(), pweapons.usable); - unserialize_list_itemdef("type/weapon", all, val, filters, + unserialize_list_itemdef(out, "type/weapon", all, val, filters, [&](const size_t& idx) -> const string& { return bweapons.weapon_type(idx); }, bweapons.weapon_type_size(), pweapons.weapon_type, item_type::WEAPON); - unserialize_list_itemdef("type/trapcomp", all, val, filters, + unserialize_list_itemdef(out, "type/trapcomp", all, val, filters, [&](const size_t& idx) -> const string& { return bweapons.trapcomp_type(idx); }, bweapons.trapcomp_type_size(), pweapons.trapcomp_type, item_type::TRAPCOMP); - unserialize_list_material("mats", all, val, filters, weapons_mat_is_allowed, + unserialize_list_material(out, "mats", all, val, filters, weapons_mat_is_allowed, [&](const size_t& idx) -> const string& { return bweapons.mats(idx); }, bweapons.mats_size(), pweapons.mats); - unserialize_list_other_mats("other", all, val, filters, mOtherMatsWeaponsArmor.mats, + unserialize_list_other_mats(out, "other", all, val, filters, mOtherMatsWeaponsArmor.mats, [&](const size_t& idx) -> const string& { return bweapons.other_mats(idx); }, bweapons.other_mats_size(), pweapons.other_mats); - unserialize_list_quality("core", all, val, filters, + unserialize_list_quality(out, "core", all, val, filters, [&](const size_t& idx) -> const string& { return bweapons.quality_core(idx); }, bweapons.quality_core_size(), pweapons.quality_core); - unserialize_list_quality("total", all, val, filters, + unserialize_list_quality(out, "total", all, val, filters, [&](const size_t& idx) -> const string& { return bweapons.quality_total(idx); }, bweapons.quality_total_size(), pweapons.quality_total); }); @@ -2270,7 +2289,7 @@ static bool wood_mat_is_allowed(const df::plant_raw* plant) { return plant && plant->flags.is_set(plant_raw_flags::TREE); } -bool StockpileSettingsSerializer::write_wood(StockpileSettings::WoodSet* wood) { +bool StockpileSettingsSerializer::write_wood(color_ostream& out, StockpileSettings::WoodSet* wood) { bool all = true; for (size_t i = 0; i < mSettings->wood.mats.size(); ++i) { if (!mSettings->wood.mats.at(i)) { @@ -2281,14 +2300,14 @@ bool StockpileSettingsSerializer::write_wood(StockpileSettings::WoodSet* wood) { if (!wood_mat_is_allowed(plant)) continue; wood->add_mats(plant->id); - DEBUG(log).print("plant %zd is %s\n", i, plant->id.c_str()); + DEBUG(log, out).print("plant %zd is %s\n", i, plant->id.c_str()); } return all; } -void StockpileSettingsSerializer::read_wood(DeserializeMode mode, const vector& filters) { +void StockpileSettingsSerializer::read_wood(color_ostream& out, DeserializeMode mode, const vector& filters) { auto & pwood = mSettings->wood; - read_category("wood", mode, + read_category(out, "wood", mode, std::bind(&StockpileSettings::has_wood, mBuffer), std::bind(&StockpileSettings::wood, mBuffer), mSettings->flags.whole, @@ -2305,17 +2324,17 @@ void StockpileSettingsSerializer::read_wood(DeserializeMode mode, const vectorraws.plants.all[idx]->id; - set_filter_elem("", filters, val, id, idx, pwood.mats.at(idx)); + set_filter_elem(out, "", filters, val, id, idx, pwood.mats.at(idx)); } } else { for (int i = 0; i < bwood.mats_size(); ++i) { const string token = bwood.mats(i); const size_t idx = find_plant(token); if (idx < 0 || (size_t)idx >= num_elems) { - WARN(log).print("wood mat index invalid %s idx=%zd\n", token.c_str(), idx); + WARN(log, out).print("wood mat index invalid %s idx=%zd\n", token.c_str(), idx); continue; } - set_filter_elem("", filters, val, token, idx, pwood.mats.at(idx)); + set_filter_elem(out, "", filters, val, token, idx, pwood.mats.at(idx)); } } }); diff --git a/plugins/stockpiles/StockpileSerializer.h b/plugins/stockpiles/StockpileSerializer.h index f8d122ac2..115b21c9f 100644 --- a/plugins/stockpiles/StockpileSerializer.h +++ b/plugins/stockpiles/StockpileSerializer.h @@ -72,13 +72,13 @@ public: * Since we depend on protobuf-lite, not the full lib, we copy this function from * protobuf message.cc */ - bool serialize_to_ostream(std::ostream* output, uint32_t includedElements); + bool serialize_to_ostream(DFHack::color_ostream& out, std::ostream* output, uint32_t includedElements); /** * Will serialize stockpile settings to a file (overwrites existing files) * @return success/failure */ - bool serialize_to_file(const std::string& file, uint32_t includedElements); + bool serialize_to_file(DFHack::color_ostream& out, const std::string& file, uint32_t includedElements); /** * Again, copied from message.cc @@ -94,7 +94,7 @@ protected: dfstockpiles::StockpileSettings mBuffer; // read memory structures and serialize to protobuf - virtual void write(uint32_t includedElements); + virtual void write(DFHack::color_ostream& out, uint32_t includedElements); // parse serialized data into ui indices virtual void read(DeserializeMode mode, const std::vector& filters); @@ -105,41 +105,41 @@ protected: private: df::stockpile_settings *mSettings; - bool write_ammo(dfstockpiles::StockpileSettings::AmmoSet* ammo); - void read_ammo(DeserializeMode mode, const std::vector& filters); - bool write_animals(dfstockpiles::StockpileSettings::AnimalsSet* animals); - void read_animals(DeserializeMode mode, const std::vector& filters); - bool write_armor(dfstockpiles::StockpileSettings::ArmorSet* armor); - void read_armor(DeserializeMode mode, const std::vector& filters); - bool write_bars_blocks(dfstockpiles::StockpileSettings::BarsBlocksSet* bars_blocks); - void read_bars_blocks(DeserializeMode mode, const std::vector& filters); - bool write_cloth(dfstockpiles::StockpileSettings::ClothSet* cloth); - void read_cloth(DeserializeMode mode, const std::vector& filters); - bool write_coins(dfstockpiles::StockpileSettings::CoinSet* coins); - void read_coins(DeserializeMode mode, const std::vector& filters); - bool write_finished_goods(dfstockpiles::StockpileSettings::FinishedGoodsSet* finished_goods); - void read_finished_goods(DeserializeMode mode, const std::vector& filters); + bool write_ammo(DFHack::color_ostream& out, dfstockpiles::StockpileSettings::AmmoSet* ammo); + void read_ammo(DFHack::color_ostream& out, DeserializeMode mode, const std::vector& filters); + bool write_animals(DFHack::color_ostream& out, dfstockpiles::StockpileSettings::AnimalsSet* animals); + void read_animals(DFHack::color_ostream& out, DeserializeMode mode, const std::vector& filters); + bool write_armor(DFHack::color_ostream& out, dfstockpiles::StockpileSettings::ArmorSet* armor); + void read_armor(DFHack::color_ostream& out, DeserializeMode mode, const std::vector& filters); + bool write_bars_blocks(DFHack::color_ostream& out, dfstockpiles::StockpileSettings::BarsBlocksSet* bars_blocks); + void read_bars_blocks(DFHack::color_ostream& out, DeserializeMode mode, const std::vector& filters); + bool write_cloth(DFHack::color_ostream& out, dfstockpiles::StockpileSettings::ClothSet* cloth); + void read_cloth(DFHack::color_ostream& out, DeserializeMode mode, const std::vector& filters); + bool write_coins(DFHack::color_ostream& out, dfstockpiles::StockpileSettings::CoinSet* coins); + void read_coins(DFHack::color_ostream& out, DeserializeMode mode, const std::vector& filters); + bool write_finished_goods(DFHack::color_ostream& out, dfstockpiles::StockpileSettings::FinishedGoodsSet* finished_goods); + void read_finished_goods(DFHack::color_ostream& out, DeserializeMode mode, const std::vector& filters); food_pair food_map(df::enums::organic_mat_category::organic_mat_category cat); - bool write_food(dfstockpiles::StockpileSettings::FoodSet* food); - void read_food(DeserializeMode mode, const std::vector& filters); - bool write_furniture(dfstockpiles::StockpileSettings::FurnitureSet* furniture); - void read_furniture(DeserializeMode mode, const std::vector& filters); - bool write_gems(dfstockpiles::StockpileSettings::GemsSet* gems); - void read_gems(DeserializeMode mode, const std::vector& filters); - bool write_leather(dfstockpiles::StockpileSettings::LeatherSet* leather); - void read_leather(DeserializeMode mode, const std::vector& filters); - bool write_corpses(dfstockpiles::StockpileSettings::CorpsesSet* corpses); - void read_corpses(DeserializeMode mode, const std::vector& filters); - bool write_refuse(dfstockpiles::StockpileSettings::RefuseSet* refuse); - void read_refuse(DeserializeMode mode, const std::vector& filters); - bool write_sheet(dfstockpiles::StockpileSettings::SheetSet* sheet); - void read_sheet(DeserializeMode mode, const std::vector& filters); - bool write_stone(dfstockpiles::StockpileSettings::StoneSet* stone); - void read_stone(DeserializeMode mode, const std::vector& filters); - bool write_weapons(dfstockpiles::StockpileSettings::WeaponsSet* weapons); - void read_weapons(DeserializeMode mode, const std::vector& filters); - bool write_wood(dfstockpiles::StockpileSettings::WoodSet* wood); - void read_wood(DeserializeMode mode, const std::vector& filters); + bool write_food(DFHack::color_ostream& out, dfstockpiles::StockpileSettings::FoodSet* food); + void read_food(DFHack::color_ostream& out, DeserializeMode mode, const std::vector& filters); + bool write_furniture(DFHack::color_ostream& out, dfstockpiles::StockpileSettings::FurnitureSet* furniture); + void read_furniture(DFHack::color_ostream& out, DeserializeMode mode, const std::vector& filters); + bool write_gems(DFHack::color_ostream& out, dfstockpiles::StockpileSettings::GemsSet* gems); + void read_gems(DFHack::color_ostream& out, DeserializeMode mode, const std::vector& filters); + bool write_leather(DFHack::color_ostream& out, dfstockpiles::StockpileSettings::LeatherSet* leather); + void read_leather(DFHack::color_ostream& out, DeserializeMode mode, const std::vector& filters); + bool write_corpses(DFHack::color_ostream& out, dfstockpiles::StockpileSettings::CorpsesSet* corpses); + void read_corpses(DFHack::color_ostream& out, DeserializeMode mode, const std::vector& filters); + bool write_refuse(DFHack::color_ostream& out, dfstockpiles::StockpileSettings::RefuseSet* refuse); + void read_refuse(DFHack::color_ostream& out, DeserializeMode mode, const std::vector& filters); + bool write_sheet(DFHack::color_ostream& out, dfstockpiles::StockpileSettings::SheetSet* sheet); + void read_sheet(DFHack::color_ostream& out, DeserializeMode mode, const std::vector& filters); + bool write_stone(DFHack::color_ostream& out, dfstockpiles::StockpileSettings::StoneSet* stone); + void read_stone(DFHack::color_ostream& out, DeserializeMode mode, const std::vector& filters); + bool write_weapons(DFHack::color_ostream& out, dfstockpiles::StockpileSettings::WeaponsSet* weapons); + void read_weapons(DFHack::color_ostream& out, DeserializeMode mode, const std::vector& filters); + bool write_wood(DFHack::color_ostream& out, dfstockpiles::StockpileSettings::WoodSet* wood); + void read_wood(DFHack::color_ostream& out, DeserializeMode mode, const std::vector& filters); }; /** @@ -169,6 +169,6 @@ private: void write_containers(); void read_containers(DeserializeMode mode); - void write_features(); - void read_features(DeserializeMode mode); + void write_features(DFHack::color_ostream& out); + void read_features(DFHack::color_ostream &out, DeserializeMode mode); }; diff --git a/plugins/stockpiles/StockpileUtils.h b/plugins/stockpiles/StockpileUtils.h index 7dcd8c3a2..e78ffccd8 100644 --- a/plugins/stockpiles/StockpileUtils.h +++ b/plugins/stockpiles/StockpileUtils.h @@ -1,5 +1,6 @@ #pragma once +#include "LuaTools.h" #include "MiscUtils.h" #include "df/world.h" @@ -9,6 +10,11 @@ // Utility Functions {{{ // A set of convenience functions for doing common lookups +bool call_stockpiles_lua(DFHack::color_ostream* out, const char* fn_name, + int nargs = 0, int nres = 0, + DFHack::Lua::LuaLambda&& args_lambda = DFHack::Lua::DEFAULT_LUA_LAMBDA, + DFHack::Lua::LuaLambda&& res_lambda = DFHack::Lua::DEFAULT_LUA_LAMBDA); + /** * Retrieve creature raw from index */ diff --git a/plugins/stockpiles/stockpiles.cpp b/plugins/stockpiles/stockpiles.cpp index a289337a9..b9d6e9d37 100644 --- a/plugins/stockpiles/stockpiles.cpp +++ b/plugins/stockpiles/stockpiles.cpp @@ -38,10 +38,9 @@ DFhackCExport command_result plugin_init(color_ostream &out, vector Date: Thu, 4 May 2023 02:39:49 -0700 Subject: [PATCH 16/24] redesign stockpiles overlay --- plugins/lua/stockpiles.lua | 112 ++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 64 deletions(-) diff --git a/plugins/lua/stockpiles.lua b/plugins/lua/stockpiles.lua index 3e03d9eda..230cfb332 100644 --- a/plugins/lua/stockpiles.lua +++ b/plugins/lua/stockpiles.lua @@ -75,7 +75,7 @@ end local function get_sp_id(opts) if opts.id then return opts.id end - local sp = dfhack.gui.getSelectedStockpile() + local sp = dfhack.gui.getSelectedStockpile(true) if sp then return sp.id end return nil end @@ -212,16 +212,16 @@ function get_stockpile_features(stockpile_number) return config.melt, config.trade, config.dump end +function set_stockpile_features(stockpile_number, melt, trade, dump) + logistics.logistics_setStockpileConfig(stockpile_number, melt, trade, dump) +end + -------------------- -- dialogs -------------------- StockpilesExport = defclass(StockpilesExport, widgets.Window) -StockpilesExport.ATTRS{ - frame_title='Export stockpile settings', - frame={w=33, h=15}, - resizable=true, -} +StockpilesExport.ATTRS{frame_title='Export stockpile settings', frame={w=33, h=15}, resizable=true} function StockpilesExport:init() self:addviews{ @@ -365,22 +365,19 @@ end StockpilesOverlay = defclass(StockpilesOverlay, overlay.OverlayWidget) StockpilesOverlay.ATTRS{ - default_pos={x=53, y=-6}, + default_pos={x=24, y=-6}, default_enabled=true, viewscreens='dwarfmode/Some/Stockpile', - frame={w=27, h=11}, + frame={w=73, h=4}, } function StockpilesOverlay:init() self.minimized = false - self.hovered = false - local main_panel = widgets.ResizingPanel{ + local main_panel = widgets.Panel{ view_id='main', - frame={b=0, l=0, r=0}, frame_style=gui.MEDIUM_FRAME, frame_background=gui.CLEAR_PEN, - autoarrange_subviews=true, visible=function() return not self.minimized end, @@ -388,64 +385,59 @@ function StockpilesOverlay:init() widgets.HotkeyLabel{ frame={t=0, l=0}, label='import settings', + auto_width=true, key='CUSTOM_CTRL_I', on_activate=do_import, }, widgets.HotkeyLabel{ frame={t=1, l=0}, label='export settings', + auto_width=true, key='CUSTOM_CTRL_E', on_activate=do_export, }, widgets.Panel{ - frame={t=2, h=4}, + frame={t=0, l=25}, subviews={ widgets.Label{ - frame={t=1, l=0, h=2}, + frame={t=0, l=0, h=1}, auto_height=false, - text={'Designate items brought', NEWLINE, 'to this stockpile for:'}, + text={'Designate items brought to this stockpile for:'}, + text_pen=COLOR_DARKGREY, + }, widgets.ToggleHotkeyLabel{ + view_id='melt', + frame={t=1, l=0}, + auto_width=true, + key='CUSTOM_CTRL_M', + option_gap=-1, + options={{label='Melting', value=true, pen=COLOR_RED}, + {label='Melting', value=false}}, + on_change=self:callback('toggleLogisticsFeature', 'melt'), + }, widgets.ToggleHotkeyLabel{ + view_id='trade', + frame={t=1, l=16}, + auto_width=true, + key='CUSTOM_CTRL_T', + option_gap=-1, + options={{label='Trading', value=true, pen=COLOR_YELLOW}, + {label='Trading', value=false}}, + on_change=self:callback('toggleLogisticsFeature', 'trade'), + }, widgets.ToggleHotkeyLabel{ + view_id='dump', + frame={t=1, l=32}, + auto_width=true, + key='CUSTOM_CTRL_U', + option_gap=-1, + options={{label='Dumping', value=true, pen=COLOR_LIGHTMAGENTA}, + {label='Dumping', value=false}}, + on_change=self:callback('toggleLogisticsFeature', 'dump'), }, }, - visible=function() - return self.hovered or self.subviews.melt:getOptionValue() or - self.subviews.trade:getOptionValue() or - self.subviews.dump:getOptionValue() - end, - }, widgets.ToggleHotkeyLabel{ - view_id='melt', - frame={t=6, l=2}, - label='melting', - key='CUSTOM_CTRL_M', - on_change=self:callback('toggleLogisticsFeature', 'melt'), - visible=function() - return self.hovered or self.subviews.melt:getOptionValue() - end, - }, widgets.ToggleHotkeyLabel{ - view_id='trade', - frame={t=7, l=2}, - label='trading', - key='CUSTOM_CTRL_T', - on_change=self:callback('toggleLogisticsFeature', 'trade'), - visible=function() - return self.hovered or self.subviews.trade:getOptionValue() - end, - }, widgets.ToggleHotkeyLabel{ - view_id='dump', - frame={t=8, l=2}, - label='dumping', - key='CUSTOM_CTRL_D', - on_change=self:callback('toggleLogisticsFeature', 'dump'), - visible=function() - return self.hovered or self.subviews.dump:getOptionValue() - end, }, }, } self:addviews{ main_panel, MinimizeButton{ - frame={b=0, l=1}, - label_pos='right', - symbol_minimize=string.char(30), - symbol_restore=string.char(31), + frame={t=0, r=1}, get_minimized_fn=function() return self.minimized end, @@ -460,33 +452,25 @@ function StockpilesOverlay:overlay_onupdate() end function StockpilesOverlay:onRenderFrame() - local sp = dfhack.gui.getSelectedStockpile() - local sp_updated = false - if self.cur_stockpile ~= sp then + local sp = dfhack.gui.getSelectedStockpile(true) + if sp and self.cur_stockpile ~= sp then local config = logistics.logistics_getStockpileConfigs(sp.stockpile_number)[1] self.subviews.melt:setOption(config.melt == 1) self.subviews.trade:setOption(config.trade == 1) self.subviews.dump:setOption(config.dump == 1) self.cur_stockpile = sp - sp_updated = true - end - local prev_hovered = self.hovered - self.hovered = not not (self.hovered and self:getMouseFramePos() or - self.subviews.main:getMousePos()) - if self.hovered ~= prev_hovered or sp_updated then - self:updateLayout() - df.global.gps.force_full_display_count = 1 end end function StockpilesOverlay:toggleLogisticsFeature(feature) - local sp = dfhack.gui.getSelectedStockpile() + self.cur_stockpile = nil + local sp = dfhack.gui.getSelectedStockpile(true) + if not sp then return end local config = logistics.logistics_getStockpileConfigs(sp.stockpile_number)[1] -- logical xor logistics.logistics_setStockpileConfig(config.stockpile_number, (feature == 'melt') ~= (config.melt == 1), (feature == 'trade') ~= (config.trade == 1), (feature == 'dump') ~= (config.dump == 1)) - self.cur_stockpile = nil end function StockpilesOverlay:toggleMinimized() From eaf97c620a43ad6e3e14090f8bdf3aeb251785a8 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 11 Jun 2023 22:59:54 -0700 Subject: [PATCH 17/24] fix merge error --- plugins/stockpiles/StockpileSerializer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/stockpiles/StockpileSerializer.cpp b/plugins/stockpiles/StockpileSerializer.cpp index f6091b9cd..0cfc7284c 100644 --- a/plugins/stockpiles/StockpileSerializer.cpp +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -1264,7 +1264,6 @@ bool StockpileSettingsSerializer::write_bars_blocks(color_ostream& out, Stockpil void StockpileSettingsSerializer::read_bars_blocks(color_ostream& out, DeserializeMode mode, const vector& filters) { auto & pbarsblocks = mSettings->bars_blocks; read_category(out, "bars_blocks", mode, ->>>>>>> 7a1aacdca (import and export logistics features) std::bind(&StockpileSettings::has_barsblocks, mBuffer), std::bind(&StockpileSettings::barsblocks, mBuffer), mSettings->flags.whole, From ff7a2945eb108ce8d84276e24036db61584f271f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 11 Jun 2023 23:15:03 -0700 Subject: [PATCH 18/24] finish merge --- plugins/stockpiles/StockpileSerializer.cpp | 117 +++++++++++---------- plugins/stockpiles/StockpileSerializer.h | 18 ++-- plugins/stockpiles/proto/stockpiles.proto | 1 + plugins/stockpiles/stockpiles.cpp | 2 +- 4 files changed, 73 insertions(+), 65 deletions(-) diff --git a/plugins/stockpiles/StockpileSerializer.cpp b/plugins/stockpiles/StockpileSerializer.cpp index 0cfc7284c..ebee71520 100644 --- a/plugins/stockpiles/StockpileSerializer.cpp +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -677,96 +677,96 @@ void StockpileSettingsSerializer::write(color_ostream& out, uint32_t includedEle if (!(includedElements & INCLUDED_ELEMENTS_CATEGORIES)) return; - DEBUG(log).print("GROUP SET %s\n", + DEBUG(log, out).print("GROUP SET %s\n", bitfield_to_string(mSettings->flags).c_str()); bool include_types = 0 != (includedElements & INCLUDED_ELEMENTS_TYPES); - write_cat("ammo", include_types, + write_cat(out, "ammo", include_types, mSettings->flags.whole, mSettings->flags.mask_ammo, std::bind(&StockpileSettings::mutable_ammo, &mBuffer), - std::bind(&StockpileSettingsSerializer::write_ammo, this, _1)); - write_cat("animals", include_types, + std::bind(&StockpileSettingsSerializer::write_ammo, this, _1, _2)); + write_cat(out, "animals", include_types, mSettings->flags.whole, mSettings->flags.mask_animals, std::bind(&StockpileSettings::mutable_animals, &mBuffer), - std::bind(&StockpileSettingsSerializer::write_animals, this, _1)); - write_cat("armor", include_types, + std::bind(&StockpileSettingsSerializer::write_animals, this, _1, _2)); + write_cat(out, "armor", include_types, mSettings->flags.whole, mSettings->flags.mask_armor, std::bind(&StockpileSettings::mutable_armor, &mBuffer), - std::bind(&StockpileSettingsSerializer::write_armor, this, _1)); - write_cat("bars_blocks", include_types, + std::bind(&StockpileSettingsSerializer::write_armor, this, _1, _2)); + write_cat(out, "bars_blocks", include_types, mSettings->flags.whole, mSettings->flags.mask_bars_blocks, std::bind(&StockpileSettings::mutable_barsblocks, &mBuffer), - std::bind(&StockpileSettingsSerializer::write_bars_blocks, this, _1)); - write_cat("cloth", include_types, + std::bind(&StockpileSettingsSerializer::write_bars_blocks, this, _1, _2)); + write_cat(out, "cloth", include_types, mSettings->flags.whole, mSettings->flags.mask_cloth, std::bind(&StockpileSettings::mutable_cloth, &mBuffer), - std::bind(&StockpileSettingsSerializer::write_cloth, this, _1)); - write_cat("coin", include_types, + std::bind(&StockpileSettingsSerializer::write_cloth, this, _1, _2)); + write_cat(out, "coin", include_types, mSettings->flags.whole, mSettings->flags.mask_coins, std::bind(&StockpileSettings::mutable_coin, &mBuffer), - std::bind(&StockpileSettingsSerializer::write_coins, this, _1)); - write_cat("finished_goods", include_types, + std::bind(&StockpileSettingsSerializer::write_coins, this, _1, _2)); + write_cat(out, "finished_goods", include_types, mSettings->flags.whole, mSettings->flags.mask_finished_goods, std::bind(&StockpileSettings::mutable_finished_goods, &mBuffer), - std::bind(&StockpileSettingsSerializer::write_finished_goods, this, _1)); - write_cat("food", include_types, + std::bind(&StockpileSettingsSerializer::write_finished_goods, this, _1, _2)); + write_cat(out, "food", include_types, mSettings->flags.whole, mSettings->flags.mask_food, std::bind(&StockpileSettings::mutable_food, &mBuffer), - std::bind(&StockpileSettingsSerializer::write_food, this, _1)); - write_cat("furniture", include_types, + std::bind(&StockpileSettingsSerializer::write_food, this, _1, _2)); + write_cat(out, "furniture", include_types, mSettings->flags.whole, mSettings->flags.mask_furniture, std::bind(&StockpileSettings::mutable_furniture, &mBuffer), - std::bind(&StockpileSettingsSerializer::write_furniture, this, _1)); - write_cat("gems", include_types, + std::bind(&StockpileSettingsSerializer::write_furniture, this, _1, _2)); + write_cat(out, "gems", include_types, mSettings->flags.whole, mSettings->flags.mask_gems, std::bind(&StockpileSettings::mutable_gems, &mBuffer), - std::bind(&StockpileSettingsSerializer::write_gems, this, _1)); - write_cat("leather", include_types, + std::bind(&StockpileSettingsSerializer::write_gems, this, _1, _2)); + write_cat(out, "leather", include_types, mSettings->flags.whole, mSettings->flags.mask_leather, std::bind(&StockpileSettings::mutable_leather, &mBuffer), - std::bind(&StockpileSettingsSerializer::write_leather, this, _1)); - write_cat("corpses", include_types, + std::bind(&StockpileSettingsSerializer::write_leather, this, _1, _2)); + write_cat(out, "corpses", include_types, mSettings->flags.whole, mSettings->flags.mask_corpses, std::bind(&StockpileSettings::mutable_corpses_v50, &mBuffer), - std::bind(&StockpileSettingsSerializer::write_corpses, this, _1)); - write_cat("refuse", include_types, + std::bind(&StockpileSettingsSerializer::write_corpses, this, _1, _2)); + write_cat(out, "refuse", include_types, mSettings->flags.whole, mSettings->flags.mask_refuse, std::bind(&StockpileSettings::mutable_refuse, &mBuffer), - std::bind(&StockpileSettingsSerializer::write_refuse, this, _1)); - write_cat("sheet", include_types, + std::bind(&StockpileSettingsSerializer::write_refuse, this, _1, _2)); + write_cat(out, "sheet", include_types, mSettings->flags.whole, mSettings->flags.mask_sheet, std::bind(&StockpileSettings::mutable_sheet, &mBuffer), - std::bind(&StockpileSettingsSerializer::write_sheet, this, _1)); - write_cat("stone", include_types, + std::bind(&StockpileSettingsSerializer::write_sheet, this, _1, _2)); + write_cat(out, "stone", include_types, mSettings->flags.whole, mSettings->flags.mask_stone, std::bind(&StockpileSettings::mutable_stone, &mBuffer), - std::bind(&StockpileSettingsSerializer::write_stone, this, _1)); - write_cat("weapons", include_types, + std::bind(&StockpileSettingsSerializer::write_stone, this, _1, _2)); + write_cat(out, "weapons", include_types, mSettings->flags.whole, mSettings->flags.mask_weapons, std::bind(&StockpileSettings::mutable_weapons, &mBuffer), - std::bind(&StockpileSettingsSerializer::write_weapons, this, _1)); - write_cat("wood", include_types, + std::bind(&StockpileSettingsSerializer::write_weapons, this, _1, _2)); + write_cat(out, "wood", include_types, mSettings->flags.whole, mSettings->flags.mask_wood, std::bind(&StockpileSettings::mutable_wood, &mBuffer), - std::bind(&StockpileSettingsSerializer::write_wood, this, _1)); + std::bind(&StockpileSettingsSerializer::write_wood, this, _1, _2)); } void StockpileSerializer::write(color_ostream& out, uint32_t includedElements) { @@ -775,23 +775,23 @@ void StockpileSerializer::write(color_ostream& out, uint32_t includedElements) { if (includedElements & INCLUDED_ELEMENTS_CONTAINERS) write_containers(out); - StockpileSettingsSerializer::write(includedElements); + StockpileSettingsSerializer::write(out, includedElements); } void StockpileSettingsSerializer::read(color_ostream &out, DeserializeMode mode, const vector& filters) { DEBUG(log).print("==READ==\n"); - read_general(mode); - read_ammo(mode, filters); - read_animals(mode, filters); - read_armor(mode, filters); - read_bars_blocks(mode, filters); - read_cloth(mode, filters); - read_coins(mode, filters); - read_finished_goods(mode, filters); - read_food(mode, filters); - read_furniture(mode, filters); - read_gems(mode, filters); - read_leather(mode, filters); + read_general(out, mode); + read_ammo(out, mode, filters); + read_animals(out, mode, filters); + read_armor(out, mode, filters); + read_bars_blocks(out, mode, filters); + read_cloth(out, mode, filters); + read_coins(out, mode, filters); + read_finished_goods(out, mode, filters); + read_food(out, mode, filters); + read_furniture(out, mode, filters); + read_gems(out, mode, filters); + read_leather(out, mode, filters); // support for old versions before corpses had a set if (mBuffer.has_corpses()) { @@ -913,7 +913,7 @@ void StockpileSerializer::read_general(color_ostream& out, DeserializeMode mode) void StockpileSerializer::write_features(color_ostream& out) { DEBUG(log, out).print("writing feature settings\n"); - if (!call_stockpiles_lua(&out, "get_stockpile_features", 1, 3, + if (!call_stockpiles_lua(&out, "get_stockpile_features", 1, 4, [&](lua_State* L) { Lua::Push(L, mPile->stockpile_number); }, @@ -921,6 +921,7 @@ void StockpileSerializer::write_features(color_ostream& out) { mBuffer.set_melt(0 != lua_toboolean(L, -1)); mBuffer.set_trade(0 != lua_toboolean(L, -2)); mBuffer.set_dump(0 != lua_toboolean(L, -3)); + mBuffer.set_train(0 != lua_toboolean(L, -4)); })) { WARN(log, out).print("failed to get logistics features of stockpile number %d\n", mPile->stockpile_number); @@ -928,7 +929,7 @@ void StockpileSerializer::write_features(color_ostream& out) { } void StockpileSerializer::read_features(color_ostream &out, DeserializeMode mode) { - int32_t melt = -1, trade = -1, dump = -1; + int32_t melt = -1, trade = -1, dump = -1, train = -1; read_elem(out, "melt", mode, std::bind(&StockpileSettings::has_melt, mBuffer), std::bind(&StockpileSettings::melt, mBuffer), @@ -941,14 +942,19 @@ void StockpileSerializer::read_features(color_ostream &out, DeserializeMode mode std::bind(&StockpileSettings::has_dump, mBuffer), std::bind(&StockpileSettings::dump, mBuffer), dump); + read_elem(out, "train", mode, + std::bind(&StockpileSettings::has_train, mBuffer), + std::bind(&StockpileSettings::train, mBuffer), + train); - if (melt != -1 || trade != -1 || dump != -1) { + if (melt != -1 || trade != -1 || dump != -1 || train != -1) { if (!call_stockpiles_lua(&out, "set_stockpile_features", 4, 0, [&](lua_State* L) { Lua::Push(L, mPile->stockpile_number); Lua::Push(L, melt == 1); Lua::Push(L, trade == 1); Lua::Push(L, dump == 1); + Lua::Push(L, train == 1); })) { WARN(log, out).print("failed to set logistics features of stockpile number %d\n", mPile->stockpile_number); } @@ -973,7 +979,7 @@ bool StockpileSettingsSerializer::write_ammo(color_ostream& out, StockpileSettin if (mSettings->ammo.other_mats.size() > 2) { WARN(log, out).print("ammo other materials > 2: %zd\n", - mPile->settings.ammo.other_mats.size()); + mSettings->ammo.other_mats.size()); } size_t num_other_mats = std::min(size_t(2), @@ -1339,7 +1345,6 @@ bool StockpileSettingsSerializer::write_cloth(color_ostream& out, StockpileSetti void StockpileSettingsSerializer::read_cloth(color_ostream& out, DeserializeMode mode, const vector& filters) { auto & pcloth = mSettings->cloth; read_category(out, "cloth", mode, ->>>>>>> 7a1aacdca (import and export logistics features) std::bind(&StockpileSettings::has_cloth, mBuffer), std::bind(&StockpileSettings::cloth, mBuffer), mSettings->flags.whole, @@ -1473,10 +1478,12 @@ bool StockpileSettingsSerializer::write_finished_goods(color_ostream& out, Stock mOtherMatsFinishedGoods.mats, [&](const string& token) { finished_goods->add_other_mats(token); }, mSettings->finished_goods.other_mats) && all; - all = serialize_list_quality([&](out, const string& token) { finished_goods->add_quality_core(token); }, + all = serialize_list_quality(out, + [&](const string& token) { finished_goods->add_quality_core(token); }, mSettings->finished_goods.quality_core) && all; - all = serialize_list_quality([&](out, const string& token) { finished_goods->add_quality_total(token); }, + all = serialize_list_quality(out, + [&](const string& token) { finished_goods->add_quality_total(token); }, mSettings->finished_goods.quality_total) && all; return all; diff --git a/plugins/stockpiles/StockpileSerializer.h b/plugins/stockpiles/StockpileSerializer.h index 115b21c9f..c707eab85 100644 --- a/plugins/stockpiles/StockpileSerializer.h +++ b/plugins/stockpiles/StockpileSerializer.h @@ -97,10 +97,10 @@ protected: virtual void write(DFHack::color_ostream& out, uint32_t includedElements); // parse serialized data into ui indices - virtual void read(DeserializeMode mode, const std::vector& filters); + virtual void read(DFHack::color_ostream& out, DeserializeMode mode, const std::vector& filters); - virtual void write_general(); - virtual void read_general(DeserializeMode mode); + virtual void write_general(DFHack::color_ostream& out); + virtual void read_general(DFHack::color_ostream& out, DeserializeMode mode); private: df::stockpile_settings *mSettings; @@ -156,19 +156,19 @@ public: protected: // read memory structures and serialize to protobuf - virtual void write(uint32_t includedElements); + virtual void write(DFHack::color_ostream& out, uint32_t includedElements); // parse serialized data into ui indices - virtual void read(DeserializeMode mode, const std::vector& filters); + virtual void read(DFHack::color_ostream& out, DeserializeMode mode, const std::vector& filters); - virtual void write_general(); - virtual void read_general(DeserializeMode mode); + virtual void write_general(DFHack::color_ostream& out); + virtual void read_general(DFHack::color_ostream& out, DeserializeMode mode); private: df::building_stockpilest* mPile; - void write_containers(); - void read_containers(DeserializeMode mode); + void write_containers(DFHack::color_ostream& out); + void read_containers(DFHack::color_ostream& out, DeserializeMode mode); void write_features(DFHack::color_ostream& out); void read_features(DFHack::color_ostream &out, DeserializeMode mode); }; diff --git a/plugins/stockpiles/proto/stockpiles.proto b/plugins/stockpiles/proto/stockpiles.proto index 0ad89e143..4030de0cd 100644 --- a/plugins/stockpiles/proto/stockpiles.proto +++ b/plugins/stockpiles/proto/stockpiles.proto @@ -187,6 +187,7 @@ message StockpileSettings { optional bool melt = 27; optional bool trade = 28; optional bool dump = 29; + optional bool train = 30; // deprecated optional bool corpses = 24; // not marked as deprecated since we still read it diff --git a/plugins/stockpiles/stockpiles.cpp b/plugins/stockpiles/stockpiles.cpp index b9d6e9d37..6e3caa61d 100644 --- a/plugins/stockpiles/stockpiles.cpp +++ b/plugins/stockpiles/stockpiles.cpp @@ -178,7 +178,7 @@ static bool stockpiles_route_import(color_ostream& out, string fname, int route_ try { StockpileSettingsSerializer cereal(&stop->settings); - if (!cereal.unserialize_from_file(fname, mode, filters)) { + if (!cereal.unserialize_from_file(out, fname, mode, filters)) { out.printerr("deserialization failed: '%s'\n", fname.c_str()); return false; } From 37b07bcee6c436e27f3c002632554966caf246e3 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 11 Jun 2023 23:31:54 -0700 Subject: [PATCH 19/24] add skeleton for autotrain --- plugins/logistics.cpp | 62 ++++++++++++++++++++++++++++++-------- plugins/lua/logistics.lua | 11 ++++--- plugins/lua/stockpiles.lua | 18 ++++++++--- 3 files changed, 71 insertions(+), 20 deletions(-) diff --git a/plugins/logistics.cpp b/plugins/logistics.cpp index 804aa5fe3..ceb5aa0ce 100644 --- a/plugins/logistics.cpp +++ b/plugins/logistics.cpp @@ -41,6 +41,7 @@ enum StockpileConfigValues { STOCKPILE_CONFIG_MELT = 1, STOCKPILE_CONFIG_TRADE = 2, STOCKPILE_CONFIG_DUMP = 3, + STOCKPILE_CONFIG_TRAIN = 4, }; static int get_config_val(PersistentDataItem& c, int index) { @@ -77,6 +78,7 @@ static PersistentDataItem& ensure_stockpile_config(color_ostream& out, int stock set_config_bool(c, STOCKPILE_CONFIG_MELT, false); set_config_bool(c, STOCKPILE_CONFIG_TRADE, false); set_config_bool(c, STOCKPILE_CONFIG_DUMP, false); + set_config_bool(c, STOCKPILE_CONFIG_TRAIN, false); return c; } @@ -92,7 +94,7 @@ static const int32_t CYCLE_TICKS = 600; static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle static command_result do_command(color_ostream &out, vector ¶meters); -static void do_cycle(color_ostream &out, int32_t &melt_count, int32_t &trade_count, int32_t &dump_count); +static void do_cycle(color_ostream& out, int32_t& melt_count, int32_t& trade_count, int32_t& dump_count, int32_t& train_count); DFhackCExport command_result plugin_init(color_ostream &out, vector &commands) { DEBUG(status, out).print("initializing %s\n", plugin_name); @@ -125,7 +127,8 @@ static void validate_stockpile_configs(color_ostream& out, if (!bld || ( !get_config_bool(c, STOCKPILE_CONFIG_MELT) && !get_config_bool(c, STOCKPILE_CONFIG_TRADE) && - !get_config_bool(c, STOCKPILE_CONFIG_DUMP))) { + !get_config_bool(c, STOCKPILE_CONFIG_DUMP) && + !get_config_bool(c, STOCKPILE_CONFIG_TRAIN))) { remove_stockpile_config(out, stockpile_number); continue; } @@ -172,14 +175,16 @@ DFhackCExport command_result plugin_onupdate(color_ostream &out) { if (!is_enabled || !Core::getInstance().isWorldLoaded()) return CR_OK; if (world->frame_counter - cycle_timestamp >= CYCLE_TICKS) { - int32_t melt_count = 0, trade_count = 0, dump_count = 0; - do_cycle(out, melt_count, trade_count, dump_count); + int32_t melt_count = 0, trade_count = 0, dump_count = 0, train_count = 0; + do_cycle(out, melt_count, trade_count, dump_count, train_count); if (0 < melt_count) out.print("logistics: marked %d item(s) for melting\n", melt_count); if (0 < trade_count) out.print("logistics: marked %d item(s) for trading\n", trade_count); if (0 < dump_count) out.print("logistics: marked %d item(s) for dumping\n", dump_count); + if (0 < train_count) + out.print("logistics: marked %d animal(s) for training\n", dump_count); } return CR_OK; } @@ -397,6 +402,24 @@ public: } }; +class TrainStockProcessor : public StockProcessor { +public: + TrainStockProcessor(int32_t stockpile_number, bool enabled, ProcessorStats& stats) + : StockProcessor("train", stockpile_number, enabled, stats) {} + + bool is_designated(color_ostream& out, df::item* item) override { + return false; + } + + bool can_designate(color_ostream& out, df::item* item) override { + return false; + } + + bool designate(color_ostream& out, df::item* item) override { + return false; + } +}; + static const struct BadFlags { const uint32_t whole; @@ -474,11 +497,11 @@ static void scan_stockpile(color_ostream &out, df::building_stockpilest *bld, } } -static void do_cycle(color_ostream &out, int32_t &melt_count, int32_t &trade_count, int32_t &dump_count) { +static void do_cycle(color_ostream& out, int32_t& melt_count, int32_t& trade_count, int32_t& dump_count, int32_t& train_count) { DEBUG(cycle,out).print("running %s cycle\n", plugin_name); cycle_timestamp = world->frame_counter; - ProcessorStats melt_stats, trade_stats, dump_stats; + ProcessorStats melt_stats, trade_stats, dump_stats, train_stats; unordered_map cache; validate_stockpile_configs(out, cache); @@ -490,10 +513,12 @@ static void do_cycle(color_ostream &out, int32_t &melt_count, int32_t &trade_cou bool melt = get_config_bool(c, STOCKPILE_CONFIG_MELT); 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); 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); scan_stockpile(out, bld, melt_stock_processor, trade_stock_processor, dump_stock_processor); @@ -518,13 +543,14 @@ static int logistics_getStockpileData(lua_State *L) { unordered_map cache; validate_stockpile_configs(*out, cache); - ProcessorStats melt_stats, trade_stats, dump_stats; + ProcessorStats melt_stats, trade_stats, dump_stats, train_stats; 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); 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); scan_stockpile(*out, bld, melt_stock_processor, trade_stock_processor, dump_stock_processor); @@ -537,6 +563,8 @@ static int logistics_getStockpileData(lua_State *L) { stats.emplace("trade_can_designate", trade_stats.can_designate_counts); stats.emplace("dump_designated", dump_stats.designated_counts); stats.emplace("dump_can_designate", dump_stats.can_designate_counts); + stats.emplace("train_designated", train_stats.designated_counts); + stats.emplace("train_can_designate", train_stats.can_designate_counts); Lua::Push(L, stats); unordered_map> configs; @@ -547,11 +575,13 @@ static int logistics_getStockpileData(lua_State *L) { bool melt = get_config_bool(c, STOCKPILE_CONFIG_MELT); 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("trade", trade ? "true" : "false"); config.emplace("dump", dump ? "true" : "false"); + config.emplace("train", train ? "true" : "false"); configs.emplace(bld->stockpile_number, config); } @@ -564,11 +594,12 @@ static int logistics_getStockpileData(lua_State *L) { static void logistics_cycle(color_ostream &out) { DEBUG(status, out).print("entering logistics_cycle\n"); - int32_t melt_count = 0, trade_count = 0, dump_count = 0; - do_cycle(out, melt_count, trade_count, dump_count); + int32_t melt_count = 0, trade_count = 0, dump_count = 0, train_count = 0; + do_cycle(out, melt_count, trade_count, dump_count, train_count); out.print("logistics: marked %d item(s) for melting\n", melt_count); out.print("logistics: marked %d item(s) for trading\n", trade_count); out.print("logistics: marked %d item(s) for dumping\n", dump_count); + out.print("logistics: marked %d animal(s) for train\n", train_count); } static void find_stockpiles(lua_State *L, int idx, @@ -596,10 +627,12 @@ static unordered_map get_stockpile_config(int32_t stockpile_number) stockpile_config.emplace("melt", get_config_bool(c, STOCKPILE_CONFIG_MELT)); 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("trade", false); stockpile_config.emplace("dump", false); + stockpile_config.emplace("train", false); } return stockpile_config; } @@ -625,9 +658,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) { - DEBUG(status, out).print("entering logistics_setStockpileConfig stockpile_number=%d, melt=%d, trade=%d, dump=%d\n", - stockpile_number, melt, trade, dump); +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); if (!find_stockpile(stockpile_number)) { out.printerr("invalid stockpile number: %d\n", stockpile_number); @@ -638,6 +671,7 @@ static void logistics_setStockpileConfig(color_ostream &out, int stockpile_numbe set_config_bool(c, STOCKPILE_CONFIG_MELT, melt); set_config_bool(c, STOCKPILE_CONFIG_TRADE, trade); set_config_bool(c, STOCKPILE_CONFIG_DUMP, dump); + set_config_bool(c, STOCKPILE_CONFIG_TRAIN, train); } static int logistics_clearStockpileConfig(lua_State *L) { @@ -684,10 +718,14 @@ static int logistics_getGlobalCounts(lua_State *L) { ++num_dump; } + size_t num_train = 0; + // TODO + unordered_map results; results.emplace("total_melt", num_melt); results.emplace("total_trade", num_trade); results.emplace("total_dump", num_dump); + results.emplace("total_train", num_train); Lua::Push(L, results); TRACE(cycle, *out).print("exit logistics_getGlobalCounts\n"); diff --git a/plugins/lua/logistics.lua b/plugins/lua/logistics.lua index 7ef000f28..ad7bf77c5 100644 --- a/plugins/lua/logistics.lua +++ b/plugins/lua/logistics.lua @@ -28,6 +28,7 @@ function getStockpileData() melt=make_stat('melt', stockpile_number, stats, configs), trade=make_stat('trade', stockpile_number, stats, configs), dump=make_stat('dump', stockpile_number, stats, configs), + train=make_stat('train', stockpile_number, stats, configs), }) end table.sort(data, function(a, b) return a.sort_key < b.sort_key end) @@ -42,14 +43,14 @@ local function print_stockpile_data(data) print('Designated/designatable items in stockpiles:') print() - local fmt = '%6s %-' .. name_len .. 's %4s %10s %5s %11s %4s %10s'; - print(fmt:format('number', 'name', 'melt', 'melt items', 'trade', 'trade items', 'dump', 'dump items')) + 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))) local function get_enab(stats) return ('[%s]'):format(stats.enabled and '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))) + 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))) end end @@ -74,6 +75,7 @@ local function print_status() print(('Total items marked for melting: %5d'):format(global_stats.total_melt)) print(('Total items marked for trading: %5d'):format(global_stats.total_trade)) print(('Total items marked for dumping: %5d'):format(global_stats.total_dump)) + print(('Total animals marked for training: %5d'):format(global_stats.total_train)) end local function for_stockpiles(opts, fn) @@ -98,7 +100,8 @@ local function do_add_stockpile_config(features, opts) logistics_setStockpileConfig(config.stockpile_number, features.melt or config.melt == 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) end end end) diff --git a/plugins/lua/stockpiles.lua b/plugins/lua/stockpiles.lua index 230cfb332..b5fd72745 100644 --- a/plugins/lua/stockpiles.lua +++ b/plugins/lua/stockpiles.lua @@ -209,11 +209,11 @@ end function get_stockpile_features(stockpile_number) local config = logistics.logistics_getStockpileConfigs(stockpile_number)[1] - return config.melt, config.trade, config.dump + return config.melt, config.trade, config.dump, config.train end -function set_stockpile_features(stockpile_number, melt, trade, dump) - logistics.logistics_setStockpileConfig(stockpile_number, melt, trade, dump) +function set_stockpile_features(stockpile_number, melt, trade, dump, train) + logistics.logistics_setStockpileConfig(stockpile_number, melt, trade, dump, train) end -------------------- @@ -429,6 +429,15 @@ function StockpilesOverlay:init() options={{label='Dumping', value=true, pen=COLOR_LIGHTMAGENTA}, {label='Dumping', value=false}}, on_change=self:callback('toggleLogisticsFeature', 'dump'), + }, widgets.ToggleHotkeyLabel{ + view_id='train', + frame={t=1, l=48}, + auto_width=true, + key='CUSTOM_CTRL_A', + option_gap=-1, + options={{label='Training', value=true, pen=COLOR_LIGHTBLUE}, + {label='Training', value=false}}, + on_change=self:callback('toggleLogisticsFeature', 'train'), }, }, }, @@ -458,6 +467,7 @@ function StockpilesOverlay:onRenderFrame() self.subviews.melt:setOption(config.melt == 1) self.subviews.trade:setOption(config.trade == 1) self.subviews.dump:setOption(config.dump == 1) + self.subviews.train:setOption(config.train == 1) self.cur_stockpile = sp end end @@ -470,7 +480,7 @@ 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 == 'dump') ~= (config.dump == 1), (feature == 'train') ~= (config.train == 1)) end function StockpilesOverlay:toggleMinimized() From e90de61cc15b6423f15e7995a208f3450af0b83d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 12 Jun 2023 10:59:13 -0700 Subject: [PATCH 20/24] remove the features feature; we can do it with quickfort properties --- docs/plugins/stockpiles.rst | 3 +- plugins/lua/stockpiles.lua | 12 +---- plugins/stockpiles/StockpileSerializer.cpp | 53 ---------------------- plugins/stockpiles/StockpileSerializer.h | 3 -- plugins/stockpiles/proto/stockpiles.proto | 6 --- plugins/stockpiles/stockpiles.cpp | 2 +- 6 files changed, 3 insertions(+), 76 deletions(-) diff --git a/docs/plugins/stockpiles.rst b/docs/plugins/stockpiles.rst index 0fed3e25f..da9b77dc5 100644 --- a/docs/plugins/stockpiles.rst +++ b/docs/plugins/stockpiles.rst @@ -2,7 +2,7 @@ stockpiles ========== .. dfhack-tool:: - :summary: Import, export, or modify stockpile settings and features. + :summary: Import, export, or modify stockpile settings. :tags: fort design productivity stockpiles Commands act upon the stockpile selected in the UI unless another stockpile @@ -94,7 +94,6 @@ file are: :types: The elements below the categories, which include the sub-categories, the specific item types, and any toggles the category might have (like Prepared meals for the Food category). -:features: DFHack `logistics` features. .. _stockpiles-library: diff --git a/plugins/lua/stockpiles.lua b/plugins/lua/stockpiles.lua index b5fd72745..8e5359dc9 100644 --- a/plugins/lua/stockpiles.lua +++ b/plugins/lua/stockpiles.lua @@ -80,7 +80,7 @@ local function get_sp_id(opts) return nil end -local included_elements = {containers=1, general=2, categories=4, types=8, features=16} +local included_elements = {containers=1, general=2, categories=4, types=8} function export_stockpile(name, opts) assert_safe_name(name) @@ -207,15 +207,6 @@ function parse_commandline(args) return true end -function get_stockpile_features(stockpile_number) - local config = logistics.logistics_getStockpileConfigs(stockpile_number)[1] - return config.melt, config.trade, config.dump, config.train -end - -function set_stockpile_features(stockpile_number, melt, trade, dump, train) - logistics.logistics_setStockpileConfig(stockpile_number, melt, trade, dump, train) -end - -------------------- -- dialogs -------------------- @@ -240,7 +231,6 @@ function StockpilesExport:init() initial_option=false, }, widgets.ToggleHotkeyLabel{frame={t=6, l=0}, label='Categories', initial_option=true}, widgets.ToggleHotkeyLabel{frame={t=7, l=0}, label='Subtypes', initial_option=true}, - widgets.ToggleHotkeyLabel{frame={t=8, l=0}, label='DFHack features', initial_option=true}, widgets.HotkeyLabel{ frame={t=10, l=0}, label='export', diff --git a/plugins/stockpiles/StockpileSerializer.cpp b/plugins/stockpiles/StockpileSerializer.cpp index ebee71520..4e2806e22 100644 --- a/plugins/stockpiles/StockpileSerializer.cpp +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -770,8 +770,6 @@ void StockpileSettingsSerializer::write(color_ostream& out, uint32_t includedEle } void StockpileSerializer::write(color_ostream& out, uint32_t includedElements) { - if (includedElements & INCLUDED_ELEMENTS_FEATURES) - write_features(out); if (includedElements & INCLUDED_ELEMENTS_CONTAINERS) write_containers(out); @@ -808,7 +806,6 @@ void StockpileSettingsSerializer::read(color_ostream &out, DeserializeMode mode, } void StockpileSerializer::read(color_ostream &out, DeserializeMode mode, const vector& filters) { - read_features(out, mode); read_containers(out, mode); StockpileSettingsSerializer::read(out, mode, filters); } @@ -911,56 +908,6 @@ void StockpileSerializer::read_general(color_ostream& out, DeserializeMode mode) mPile->use_links_only); } -void StockpileSerializer::write_features(color_ostream& out) { - DEBUG(log, out).print("writing feature settings\n"); - if (!call_stockpiles_lua(&out, "get_stockpile_features", 1, 4, - [&](lua_State* L) { - Lua::Push(L, mPile->stockpile_number); - }, - [&](lua_State* L) { - mBuffer.set_melt(0 != lua_toboolean(L, -1)); - mBuffer.set_trade(0 != lua_toboolean(L, -2)); - mBuffer.set_dump(0 != lua_toboolean(L, -3)); - mBuffer.set_train(0 != lua_toboolean(L, -4)); - })) { - - WARN(log, out).print("failed to get logistics features of stockpile number %d\n", mPile->stockpile_number); - } -} - -void StockpileSerializer::read_features(color_ostream &out, DeserializeMode mode) { - int32_t melt = -1, trade = -1, dump = -1, train = -1; - read_elem(out, "melt", mode, - std::bind(&StockpileSettings::has_melt, mBuffer), - std::bind(&StockpileSettings::melt, mBuffer), - melt); - read_elem(out, "trade", mode, - std::bind(&StockpileSettings::has_trade, mBuffer), - std::bind(&StockpileSettings::trade, mBuffer), - trade); - read_elem(out, "dump", mode, - std::bind(&StockpileSettings::has_dump, mBuffer), - std::bind(&StockpileSettings::dump, mBuffer), - dump); - read_elem(out, "train", mode, - std::bind(&StockpileSettings::has_train, mBuffer), - std::bind(&StockpileSettings::train, mBuffer), - train); - - if (melt != -1 || trade != -1 || dump != -1 || train != -1) { - if (!call_stockpiles_lua(&out, "set_stockpile_features", 4, 0, - [&](lua_State* L) { - Lua::Push(L, mPile->stockpile_number); - Lua::Push(L, melt == 1); - Lua::Push(L, trade == 1); - Lua::Push(L, dump == 1); - Lua::Push(L, train == 1); - })) { - WARN(log, out).print("failed to set logistics features of stockpile number %d\n", mPile->stockpile_number); - } - } -} - static bool ammo_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid() && mi.material && mi.material->flags.is_set(material_flags::IS_METAL); } diff --git a/plugins/stockpiles/StockpileSerializer.h b/plugins/stockpiles/StockpileSerializer.h index c707eab85..a29fe3906 100644 --- a/plugins/stockpiles/StockpileSerializer.h +++ b/plugins/stockpiles/StockpileSerializer.h @@ -21,7 +21,6 @@ enum IncludedElements { INCLUDED_ELEMENTS_GENERAL = 0x02, INCLUDED_ELEMENTS_CATEGORIES = 0x04, INCLUDED_ELEMENTS_TYPES = 0x08, - INCLUDED_ELEMENTS_FEATURES = 0x10, }; enum DeserializeMode { @@ -169,6 +168,4 @@ private: void write_containers(DFHack::color_ostream& out); void read_containers(DFHack::color_ostream& out, DeserializeMode mode); - void write_features(DFHack::color_ostream& out); - void read_features(DFHack::color_ostream &out, DeserializeMode mode); }; diff --git a/plugins/stockpiles/proto/stockpiles.proto b/plugins/stockpiles/proto/stockpiles.proto index 4030de0cd..681e7d927 100644 --- a/plugins/stockpiles/proto/stockpiles.proto +++ b/plugins/stockpiles/proto/stockpiles.proto @@ -183,12 +183,6 @@ message StockpileSettings { optional WeaponsSet weapons = 16; optional WoodSet wood = 15; - // DFHack features - optional bool melt = 27; - optional bool trade = 28; - optional bool dump = 29; - optional bool train = 30; - // deprecated optional bool corpses = 24; // not marked as deprecated since we still read it optional OreSet ore = 7 [deprecated=true]; diff --git a/plugins/stockpiles/stockpiles.cpp b/plugins/stockpiles/stockpiles.cpp index 6e3caa61d..840e4adc7 100644 --- a/plugins/stockpiles/stockpiles.cpp +++ b/plugins/stockpiles/stockpiles.cpp @@ -32,7 +32,7 @@ DFhackCExport command_result plugin_init(color_ostream &out, vector Date: Mon, 12 Jun 2023 12:28:39 -0700 Subject: [PATCH 21/24] implement autotrain --- docs/plugins/logistics.rst | 12 +++++------ plugins/logistics.cpp | 43 ++++++++++++++++++++++++++++++++------ plugins/lua/logistics.lua | 2 +- 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/docs/plugins/logistics.rst b/docs/plugins/logistics.rst index 1b36caa21..40cead184 100644 --- a/docs/plugins/logistics.rst +++ b/docs/plugins/logistics.rst @@ -3,16 +3,16 @@ logistics .. dfhack-tool:: :summary: Automatically mark and route items in monitored stockpiles. - :tags: fort auto items stockpiles + :tags: fort auto animals items stockpiles Commands act upon the stockpile selected in the UI unless another stockpile identifier is specified on the commandline. When the plugin is enabled, it checks stockpiles marked with automelt, -autotrade, and/or autodump features twice every in-game day, and will mark valid -items in those stockpiles for melting, trading, and/or dumping, respectively. -Note that items will only be marked for trading if a caravan is approaching or -is already at the trade depot. +autotrade, autodump, and/or autotrain features twice every in-game day, and +will mark valid items/animals in those stockpiles for melting, trading, +dumping, and/or training, respectively. Note that items will only be marked for +trading if a caravan is approaching or is already at the trade depot. Please see `gui/logistics` for the interactive status and configuration dialog. @@ -24,7 +24,7 @@ Usage enable logistics logistics [status] logistics now - logistics add [melt] [trade] [dump] [] + logistics add [melt] [trade] [dump] [train] [] logistics clear [all] [] Examples diff --git a/plugins/logistics.cpp b/plugins/logistics.cpp index ceb5aa0ce..d62ee8c36 100644 --- a/plugins/logistics.cpp +++ b/plugins/logistics.cpp @@ -5,6 +5,7 @@ #include "modules/Buildings.h" #include "modules/Job.h" #include "modules/Persistence.h" +#include "modules/Units.h" #include "modules/World.h" #include "df/building.h" @@ -13,6 +14,7 @@ #include "df/caravan_state.h" #include "df/general_ref_building_holderst.h" #include "df/plotinfost.h" +#include "df/training_assignment.h" #include "df/world.h" using std::string; @@ -408,15 +410,42 @@ public: : StockProcessor("train", stockpile_number, enabled, stats) {} bool is_designated(color_ostream& out, df::item* item) override { - return false; + auto unit = get_caged_unit(item); + return unit && has_training_assignment(unit); } bool can_designate(color_ostream& out, df::item* item) override { - return false; + auto unit = get_caged_unit(item); + return unit && Units::isTamable(unit) && !Units::isTame(unit) + && !has_training_assignment(unit); } bool designate(color_ostream& out, df::item* item) override { - return false; + auto unit = get_caged_unit(item); + if (!unit) + return false; + df::training_assignment *assignment = new df::training_assignment(); + assignment->animal_id = unit->id; + assignment->trainer_id = -1; + assignment->flags.bits.any_trainer = true; + insert_into_vector(df::global::plotinfo->equipment.training_assignments, + &df::training_assignment::animal_id, assignment); + return true; + } + +private: + static df::unit* get_caged_unit(df::item* item) { + if (item->getType() != df::item_type::CAGE) + return NULL; + auto gref = Items::getGeneralRef(item, df::general_ref_type::CONTAINS_UNIT); + if (!gref) + return NULL; + return gref->getUnit(); + } + + static bool has_training_assignment(df::unit* unit) { + return binsearch_index(df::global::plotinfo->equipment.training_assignments, + &df::training_assignment::animal_id, unit->id) > -1; } }; @@ -475,7 +504,8 @@ static void scan_item(color_ostream &out, df::item *item, StockProcessor &proces static void scan_stockpile(color_ostream &out, df::building_stockpilest *bld, MeltStockProcessor &melt_stock_processor, TradeStockProcessor &trade_stock_processor, - DumpStockProcessor &dump_stock_processor) { + DumpStockProcessor &dump_stock_processor, + TrainStockProcessor &train_stock_processor) { auto id = bld->id; Buildings::StockpileIterator items; for (items.begin(bld); !items.done(); ++items) { @@ -494,6 +524,7 @@ static void scan_stockpile(color_ostream &out, df::building_stockpilest *bld, } scan_item(out, item, melt_stock_processor); scan_item(out, item, dump_stock_processor); + scan_item(out, item, train_stock_processor); } } @@ -521,7 +552,7 @@ static void do_cycle(color_ostream& out, int32_t& melt_count, int32_t& trade_cou TrainStockProcessor train_stock_processor(stockpile_number, train, train_stats); scan_stockpile(out, bld, melt_stock_processor, - trade_stock_processor, dump_stock_processor); + trade_stock_processor, dump_stock_processor, train_stock_processor); } melt_count = melt_stats.newly_designated; @@ -553,7 +584,7 @@ static int logistics_getStockpileData(lua_State *L) { TrainStockProcessor train_stock_processor(stockpile_number, false, train_stats); scan_stockpile(*out, bld, melt_stock_processor, - trade_stock_processor, dump_stock_processor); + trade_stock_processor, dump_stock_processor, train_stock_processor); } unordered_map stats; diff --git a/plugins/lua/logistics.lua b/plugins/lua/logistics.lua index ad7bf77c5..891332236 100644 --- a/plugins/lua/logistics.lua +++ b/plugins/lua/logistics.lua @@ -46,7 +46,7 @@ local function print_stockpile_data(data) 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))) + 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_dstat(stats) return ('%d/%d'):format(stats.designated, stats.designated + stats.can_designate) end for _,sp in ipairs(data) do From 53efb47c68cb8542c3bb1048e3da0cd68259d07f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 12 Jun 2023 12:31:52 -0700 Subject: [PATCH 22/24] remove reference to gui/logistics until it is ready --- docs/plugins/logistics.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/plugins/logistics.rst b/docs/plugins/logistics.rst index 40cead184..7ffc97c49 100644 --- a/docs/plugins/logistics.rst +++ b/docs/plugins/logistics.rst @@ -14,8 +14,6 @@ will mark valid items/animals in those stockpiles for melting, trading, dumping, and/or training, respectively. Note that items will only be marked for trading if a caravan is approaching or is already at the trade depot. -Please see `gui/logistics` for the interactive status and configuration dialog. - Usage ----- From 856237cfd3e92124b7cf3dab3e76ede850457d4c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 12 Jun 2023 12:52:47 -0700 Subject: [PATCH 23/24] just display logistics options in stockpiles overlay for now; until stockpile import/export dialogs are complete --- plugins/lua/stockpiles.lua | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/plugins/lua/stockpiles.lua b/plugins/lua/stockpiles.lua index 8e5359dc9..661c6a86d 100644 --- a/plugins/lua/stockpiles.lua +++ b/plugins/lua/stockpiles.lua @@ -358,7 +358,7 @@ StockpilesOverlay.ATTRS{ default_pos={x=24, y=-6}, default_enabled=true, viewscreens='dwarfmode/Some/Stockpile', - frame={w=73, h=4}, + frame={w=65, h=4}, } function StockpilesOverlay:init() @@ -372,25 +372,26 @@ function StockpilesOverlay:init() return not self.minimized end, subviews={ - widgets.HotkeyLabel{ + -- widgets.HotkeyLabel{ + -- frame={t=0, l=0}, + -- label='import settings', + -- auto_width=true, + -- key='CUSTOM_CTRL_I', + -- on_activate=do_import, + -- }, widgets.HotkeyLabel{ + -- frame={t=1, l=0}, + -- label='export settings', + -- auto_width=true, + -- key='CUSTOM_CTRL_E', + -- on_activate=do_export, + -- }, + widgets.Panel{ frame={t=0, l=0}, - label='import settings', - auto_width=true, - key='CUSTOM_CTRL_I', - on_activate=do_import, - }, widgets.HotkeyLabel{ - frame={t=1, l=0}, - label='export settings', - auto_width=true, - key='CUSTOM_CTRL_E', - on_activate=do_export, - }, widgets.Panel{ - frame={t=0, l=25}, subviews={ widgets.Label{ frame={t=0, l=0, h=1}, auto_height=false, - text={'Designate items brought to this stockpile for:'}, + text={'Designate items/animals brought to this stockpile for:'}, text_pen=COLOR_DARKGREY, }, widgets.ToggleHotkeyLabel{ view_id='melt', @@ -400,6 +401,7 @@ function StockpilesOverlay:init() option_gap=-1, options={{label='Melting', value=true, pen=COLOR_RED}, {label='Melting', value=false}}, + initial_option=false, on_change=self:callback('toggleLogisticsFeature', 'melt'), }, widgets.ToggleHotkeyLabel{ view_id='trade', @@ -409,6 +411,7 @@ function StockpilesOverlay:init() option_gap=-1, options={{label='Trading', value=true, pen=COLOR_YELLOW}, {label='Trading', value=false}}, + initial_option=false, on_change=self:callback('toggleLogisticsFeature', 'trade'), }, widgets.ToggleHotkeyLabel{ view_id='dump', @@ -418,6 +421,7 @@ function StockpilesOverlay:init() option_gap=-1, options={{label='Dumping', value=true, pen=COLOR_LIGHTMAGENTA}, {label='Dumping', value=false}}, + initial_option=false, on_change=self:callback('toggleLogisticsFeature', 'dump'), }, widgets.ToggleHotkeyLabel{ view_id='train', @@ -427,6 +431,7 @@ function StockpilesOverlay:init() option_gap=-1, options={{label='Training', value=true, pen=COLOR_LIGHTBLUE}, {label='Training', value=false}}, + initial_option=false, on_change=self:callback('toggleLogisticsFeature', 'train'), }, }, From 0a9056df9148280e3b802ed24e375b9a52d9a808 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 12 Jun 2023 12:55:03 -0700 Subject: [PATCH 24/24] update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index a44d0c5df..c237c90f6 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -34,6 +34,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: # Future ## New Plugins +- `logistics`: automatically mark and route items or animals that come to monitored stockpiles. options are toggleable on an overlay that comes up when you have a stockpile selected. ## Fixes - `dig-now`: clear item occupancy flags for channeled tiles that had items on them