From 5f611ec48b65aacf8e9f55a1de579e76087330bf Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Wed, 4 Jun 2014 22:12:30 +1200 Subject: [PATCH] Add automelt plugin --- NEWS | 1 + Readme.rst | 9 ++ plugins/CMakeLists.txt | 1 + plugins/automelt.cpp | 282 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 293 insertions(+) create mode 100644 plugins/automelt.cpp diff --git a/NEWS b/NEWS index 757a9277f..3026fc665 100644 --- a/NEWS +++ b/NEWS @@ -28,6 +28,7 @@ DFHack future New plugins: - rendermax: replace the renderer with something else. Most interesting is "rendermax light"- a lighting engine for df. + - automelt: allows marking stockpiles for automelt (i.e. any items placed in stocpile will be designated for melting) Misc improvements: - digfort: improved csv parsing, add start() comment handling diff --git a/Readme.rst b/Readme.rst index 7491c4a0c..6920835a4 100644 --- a/Readme.rst +++ b/Readme.rst @@ -2621,6 +2621,15 @@ materials, it returns you back to this screen. If you use this along with severa enabled materials, you should be able to place complex constructions more conveniently. +Stockpile Automation +==================== +Enable the automelt plugin in your dfhack.init with + ``enable automelt`` + +When querying a stockpile an option will appear to toggle automelt for this stockpile. +Any items placed in this stockpile will be designated to be melted. + + gui/liquids =========== diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 650fe1e98..91a986e59 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -158,6 +158,7 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(dwarfmonitor dwarfmonitor.cpp) DFHACK_PLUGIN(mousequery mousequery.cpp) DFHACK_PLUGIN(autotrade autotrade.cpp) + DFHACK_PLUGIN(automelt automelt.cpp) DFHACK_PLUGIN(stocks stocks.cpp) DFHACK_PLUGIN(treefarm treefarm.cpp) DFHACK_PLUGIN(cleanconst cleanconst.cpp) diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp new file mode 100644 index 000000000..0c6a7b33f --- /dev/null +++ b/plugins/automelt.cpp @@ -0,0 +1,282 @@ +#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/building_stockpilest.h" +#include "modules/Items.h" +#include "df/ui.h" +#include "modules/Maps.h" +#include "modules/World.h" +#include "df/item_quality.h" + +using df::global::world; +using df::global::cursor; +using df::global::ui; +using df::building_stockpilest; + +DFHACK_PLUGIN("automelt"); +#define PLUGIN_VERSION 0.3 + +static const string PERSISTENCE_KEY = "automelt/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; + + if (!can_melt(item)) + continue; + + if (is_set_to_melt(item)) + continue; + + auto &melting_items = world->items.other[items_other_id::ANY_MELT_DESIGNATED]; + for (auto it = stockpiles.begin(); it != stockpiles.end(); it++) + { + if (!it->inStockpile(item)) + continue; + + ++marked_count; + insert_into_vector(melting_items, &df::item::id, item); + item->flags.bits.melt = true; + } + } + + if (marked_count) + Gui::showAnnouncement("Marked " + int_to_string(marked_count) + " items to melt", COLOR_GREEN, false); +} + +/* + * 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(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 610 + +DFhackCExport command_result plugin_onupdate ( color_ostream &out ) +{ + if(!Maps::IsValid()) + return CR_OK; + + static decltype(world->frame_counter) last_frame_count = 0; + + if (DFHack::World::ReadPauseState()) + return CR_OK; + + if (world->frame_counter - last_frame_count < DELTA_TICKS) + return CR_OK; + + last_frame_count = world->frame_counter; + + monitor.doCycle(); + + return CR_OK; +} + + +/* + * Interface + */ + +struct melt_hook : public df::viewscreen_dwarfmodest +{ + typedef df::viewscreen_dwarfmodest interpose_base; + + bool handleInput(set *input) + { + building_stockpilest *sp = get_selected_stockpile(); + if (!sp) + return false; + + if (input->count(interface_key::CUSTOM_SHIFT_M)) + { + 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 = 25; + + OutputToggleString(x, y, "Auto melt", "Shift-M", monitor.isMonitored(sp), true, left_margin); + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(melt_hook, feed); +IMPLEMENT_VMETHOD_INTERPOSE(melt_hook, render); + + +static command_result automelt_cmd(color_ostream &out, vector & parameters) +{ + if (!parameters.empty()) + { + if (parameters.size() == 1 && toLower(parameters[0])[0] == 'v') + { + out << "Automelt" << endl << "Version: " << PLUGIN_VERSION << endl; + } + } + + return CR_OK; +} + +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 (!gps) + return CR_FAILURE; + + if (enable != is_enabled) + { + if (!INTERPOSE_HOOK(melt_hook, feed).apply(enable) || + !INTERPOSE_HOOK(melt_hook, render).apply(enable)) + return CR_FAILURE; + + is_enabled = enable; + } + + return CR_OK; +} + +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +{ + commands.push_back( + PluginCommand( + "automelt", "Automatically flag metal items in marked stockpiles for melting.", + automelt_cmd, false, "")); + + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + return CR_OK; +}