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/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 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..7ffc97c49 --- /dev/null +++ b/docs/plugins/logistics.rst @@ -0,0 +1,68 @@ +logistics +========= + +.. dfhack-tool:: + :summary: Automatically mark and route items in monitored 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, 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. + +Usage +----- + +:: + + enable logistics + logistics [status] + logistics now + logistics add [melt] [trade] [dump] [train] [] + 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..da9b77dc5 100644 --- a/docs/plugins/stockpiles.rst +++ b/docs/plugins/stockpiles.rst @@ -2,11 +2,11 @@ stockpiles ========== .. dfhack-tool:: - :summary: Import, export, or modify stockpile settings and features. + :summary: Import, export, or modify stockpile settings. :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 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..d62ee8c36 --- /dev/null +++ b/plugins/logistics.cpp @@ -0,0 +1,778 @@ +#include "Debug.h" +#include "LuaTools.h" +#include "PluginManager.h" + +#include "modules/Buildings.h" +#include "modules/Job.h" +#include "modules/Persistence.h" +#include "modules/Units.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/training_assignment.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, + STOCKPILE_CONFIG_TRAIN = 4, +}; + +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); + set_config_bool(c, STOCKPILE_CONFIG_TRAIN, 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, int32_t& train_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) && + !get_config_bool(c, STOCKPILE_CONFIG_TRAIN))) { + 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, 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; +} + +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) { } + + bool is_designated(color_ostream &out, df::item *item) override { + return item->flags.bits.melt; + } + + 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; + } + + 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()) { } + + 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; + } + + bool can_designate(color_ostream& out, df::item* item) override { + return Items::canTradeWithContents(item); + } + + 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) { } + + bool is_designated(color_ostream& out, df::item* item) override { + return item->flags.bits.dump; + } + + bool can_designate(color_ostream& out, df::item* item) override { + return true; + } + + bool designate(color_ostream& out, df::item* item) override { + item->flags.bits.dump = true; + return true; + } +}; + +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 { + auto unit = get_caged_unit(item); + return unit && has_training_assignment(unit); + } + + bool can_designate(color_ostream& out, df::item* item) override { + 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 { + 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; + } +}; + +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, + TrainStockProcessor &train_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); + scan_item(out, item, train_stock_processor); + } +} + +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, train_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); + 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, train_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, 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, train_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); + 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; + 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); + 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); + } + 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, 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, + 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)); + 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; +} + +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_getStockpileConfigs\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, 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); + 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); + set_config_bool(c, STOCKPILE_CONFIG_TRAIN, train); +} + +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(); +} + +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; + } + + 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"); + + return 1; +} + +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_COMMAND(logistics_getGlobalCounts), + 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..891332236 --- /dev/null +++ b/plugins/lua/logistics.lua @@ -0,0 +1,156 @@ +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 = tostring(name):lower() + 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), + train=make_stat('train', 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_stockpile_data(data) + 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 %4s %10s %5s %11s %4s %10s %5s %11s'; + print(fmt:format('number', 'name', 'melt', 'melt items', 'trade', 'trade items', 'dump', 'dump items', 'train', 'train items')) + local function uline(len) return ('-'):rep(len) end + print(fmt:format(uline(6), uline(name_len), uline(4), uline(10), uline(5), uline(11), uline(4), uline(10), uline(5), uline(11))) + local function get_enab(stats) return ('[%s]'):format(stats.enabled and 'x' or ' ') end + local function get_dstat(stats) return ('%d/%d'):format(stats.designated, stats.designated + stats.can_designate) end + for _,sp in ipairs(data) do + print(fmt:format(sp.stockpile_number, sp.name, get_enab(sp.melt), get_dstat(sp.melt), get_enab(sp.trade), get_dstat(sp.trade), get_enab(sp.dump), get_dstat(sp.dump), get_enab(sp.train), get_dstat(sp.train))) + 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)) + print(('Total animals marked for training: %5d'):format(global_stats.total_train)) +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 == 1, + features.trade or config.trade == 1, + features.dump or config.dump == 1, + features.train or config.train == 1) + 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..661c6a86d 100644 --- a/plugins/lua/stockpiles.lua +++ b/plugins/lua/stockpiles.lua @@ -1,9 +1,19 @@ 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"; +local STOCKPILES_DIR = 'dfhack-config/stockpiles' +local STOCKPILES_LIBRARY_DIR = 'hack/data/stockpiles' + +local BAD_FILENAME_REGEX = '[^%w._]' + +-------------------- +-- plugin logic +-------------------- local function get_sp_name(name, num) if #name > 0 then return name end @@ -19,7 +29,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 @@ -31,16 +41,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 @@ -59,43 +67,32 @@ 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 name:find('[^%w._]') then + if not name or #name == 0 then qerror('name missing or empty') end + if name:find(BAD_FILENAME_REGEX) then qerror('name can only contain numbers, letters, periods, and underscores') end 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 -local included_elements = { - containers=1, - general=2, - categories=4, - types=8, -} +local included_elements = {containers=1, general=2, categories=4, types=8} function export_stockpile(name, opts) assert_safe_name(name) name = STOCKPILES_DIR .. '/' .. name local includedElements = 0 - for _,inc in ipairs(opts.includes) do - if included_elements[inc] then - includedElements = includedElements | included_elements[inc] - end + 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) @@ -124,14 +121,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 - 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 @@ -139,9 +132,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 @@ -156,25 +147,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 @@ -192,4 +207,290 @@ function parse_commandline(args) return true end +-------------------- +-- dialogs +-------------------- + +StockpilesExport = defclass(StockpilesExport, widgets.Window) +StockpilesExport.ATTRS{frame_title='Export stockpile settings', frame={w=33, h=15}, resizable=true} + +function StockpilesExport:init() + self:addviews{ + 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.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:on_submit(text) + self:dismiss() +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 + +-------------------- +-- MinimizeButton +-------------------- + +MinimizeButton = defclass(MinimizeButton, widgets.Widget) +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, +} + +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 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 + return (' %s '):format(label) + 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}}) + + self:addviews{ + widgets.Label{ + view_id='unhovered_label', + 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}, + on_click=function() + self.on_click() + self:updateLayout() + end, + visible=is_not_hovered, + }, widgets.Label{ + view_id='hovered_label', + frame={t=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=function() + self.on_click() + self:updateLayout() + end, + 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() + 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=24, y=-6}, + default_enabled=true, + viewscreens='dwarfmode/Some/Stockpile', + frame={w=65, h=4}, +} + +function StockpilesOverlay:init() + self.minimized = false + + local main_panel = widgets.Panel{ + view_id='main', + 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='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}, + subviews={ + widgets.Label{ + frame={t=0, l=0, h=1}, + auto_height=false, + text={'Designate items/animals 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}}, + initial_option=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}}, + initial_option=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}}, + initial_option=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}}, + initial_option=false, + on_change=self:callback('toggleLogisticsFeature', 'train'), + }, + }, + }, + }, + } + + self:addviews{ + main_panel, MinimizeButton{ + frame={t=0, r=1}, + get_minimized_fn=function() + return self.minimized + end, + on_click=self:callback('toggleMinimized'), + }, + } +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(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.subviews.train:setOption(config.train == 1) + self.cur_stockpile = sp + end +end + +function StockpilesOverlay:toggleLogisticsFeature(feature) + 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), (feature == 'train') ~= (config.train == 1)) +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:toggleMinimized() + return true + end + return StockpilesOverlay.super.onInput(self, keys) +end + +OVERLAY_WIDGETS = {overlay=StockpilesOverlay} + return _ENV diff --git a/plugins/stockpiles/OrganicMatLookup.cpp b/plugins/stockpiles/OrganicMatLookup.cpp index a9e2210c9..1efdd4df6 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); } /** @@ -20,8 +21,8 @@ namespace DFHack { * 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]; @@ -31,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(); } @@ -71,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; } @@ -107,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 b650ec618..4e2806e22 100644 --- a/plugins/stockpiles/StockpileSerializer.cpp +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -41,9 +41,11 @@ using namespace dfstockpiles; using df::global::world; using std::placeholders::_1; +using std::placeholders::_2; -namespace DFHack { - DBG_EXTERN(stockpiles, log); +namespace DFHack +{ +DBG_EXTERN(stockpiles, log); } static struct OtherMatsFurniture { @@ -161,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)) @@ -174,17 +176,17 @@ 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(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(); @@ -192,18 +194,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", + WARN(log, out).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); } /** @@ -228,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; } } @@ -276,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) { @@ -293,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'); @@ -307,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; } @@ -318,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; @@ -338,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; } @@ -347,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; } @@ -363,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]); } } @@ -387,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) { @@ -399,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; } @@ -423,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) { @@ -447,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) { @@ -502,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; @@ -520,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; } @@ -534,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; @@ -553,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 @@ -576,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; } @@ -587,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) { @@ -606,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; @@ -618,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; } @@ -634,188 +636,189 @@ 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; - 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(uint32_t includedElements) { +void StockpileSerializer::write(color_ostream& out, uint32_t includedElements) { if (includedElements & INCLUDED_ELEMENTS_CONTAINERS) - write_containers(); + write_containers(out); - StockpileSettingsSerializer::write(includedElements); + StockpileSettingsSerializer::write(out, 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); - 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()) { 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(DeserializeMode mode, const vector& filters) { - read_containers(mode); - StockpileSettingsSerializer::read(mode, filters); +void StockpileSerializer::read(color_ostream &out, DeserializeMode mode, const vector& 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) { @@ -825,13 +828,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, @@ -839,7 +842,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(); } @@ -855,51 +858,51 @@ 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); @@ -909,21 +912,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", + mSettings->ammo.other_mats.size()); } size_t num_other_mats = std::min(size_t(2), @@ -935,23 +938,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, @@ -966,18 +969,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) { @@ -985,35 +988,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, @@ -1026,10 +1029,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); }); @@ -1039,7 +1042,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; @@ -1048,72 +1051,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, @@ -1135,47 +1138,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); }); @@ -1189,31 +1192,31 @@ 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, std::bind(&StockpileSettings::has_barsblocks, mBuffer), std::bind(&StockpileSettings::barsblocks, mBuffer), mSettings->flags.whole, @@ -1227,68 +1230,68 @@ 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, std::bind(&StockpileSettings::has_cloth, mBuffer), std::bind(&StockpileSettings::cloth, mBuffer), mSettings->flags.whole, @@ -1306,35 +1309,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); }); @@ -1344,16 +1347,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, @@ -1364,7 +1367,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); }); @@ -1407,33 +1410,35 @@ 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, @@ -1448,23 +1453,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); }); @@ -1652,7 +1657,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; @@ -1664,19 +1669,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, @@ -1693,25 +1698,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; @@ -1725,30 +1729,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, @@ -1771,33 +1775,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); }); @@ -1815,17 +1819,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; @@ -1838,7 +1842,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()); } @@ -1852,16 +1856,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, @@ -1875,11 +1879,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); @@ -1891,11 +1895,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, @@ -1935,21 +1939,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, @@ -1959,7 +1963,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); }); @@ -1974,48 +1978,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, @@ -2036,56 +2040,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, @@ -2097,11 +2101,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); }); @@ -2115,16 +2119,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, @@ -2135,7 +2139,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); }); @@ -2145,49 +2149,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, @@ -2205,30 +2209,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); }); @@ -2238,7 +2242,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)) { @@ -2249,14 +2253,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, @@ -2273,17 +2277,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 4798fba5e..a29fe3906 100644 --- a/plugins/stockpiles/StockpileSerializer.h +++ b/plugins/stockpiles/StockpileSerializer.h @@ -71,74 +71,74 @@ 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 */ - 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; // 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::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); }; /** @@ -155,17 +155,17 @@ 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); }; 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 de57598c4..840e4adc7 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,27 +20,27 @@ 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, - "Import, export, or modify stockpile settings and features.", + "Import, export, or modify stockpile settings.", do_command)); return CR_OK; } -static bool call_stockpiles_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) { +bool call_stockpiles_lua(color_ostream* out, const char* fn_name, + int nargs, int nres, Lua::LuaLambda&& args_lambda, Lua::LuaLambda&& res_lambda) { + DEBUG(log).print("calling stockpiles lua function: '%s'\n", fn_name); CoreSuspender guard; @@ -97,7 +94,7 @@ static bool stockpiles_export(color_ostream& out, string fname, int id, uint32_t try { StockpileSerializer cereal(sp); - if (!cereal.serialize_to_file(fname, includedElements)) { + if (!cereal.serialize_to_file(out, fname, includedElements)) { out.printerr("could not save to '%s'\n", fname.c_str()); return false; } @@ -136,7 +133,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; } @@ -181,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; }