diff --git a/dfhack.init-example b/dfhack.init-example index 169d40046..f8878ea0a 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -181,6 +181,9 @@ tweak farm-plot-select # Add Shift-Left/Right controls to import agreement screen tweak import-priority-category +# Fixes a crash in the work order contition material list (bug 9905). +tweak condition-material + # Misc. UI tweaks tweak block-labors # Prevents labors that can't be used from being toggled tweak civ-view-agreement diff --git a/docs/Plugins.rst b/docs/Plugins.rst index e2b829020..c81b284c3 100644 --- a/docs/Plugins.rst +++ b/docs/Plugins.rst @@ -272,6 +272,7 @@ Subcommands that persist until disabled or DF quits: reagents. :block-labors: Prevents labors that can't be used from being toggled :civ-view-agreement: Fixes overlapping text on the "view agreement" screen +:condition-material: Fixes a crash in the work order contition material list (:bug:`9905`). :craft-age-wear: Fixes the behavior of crafted items wearing out over time (:bug:`6003`). With this tweak, items made from cloth and leather will gain a level of wear every 20 years. diff --git a/library/xml b/library/xml index 8727ebd74..9b834c089 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 8727ebd74a3f5d90e34a08266a3719e0cd5817d9 +Subproject commit 9b834c089efb4657d43a8fa4f8f0822e8224e576 diff --git a/plugins/tweak/tweak.cpp b/plugins/tweak/tweak.cpp index 88c57fb62..1bab3ebcf 100644 --- a/plugins/tweak/tweak.cpp +++ b/plugins/tweak/tweak.cpp @@ -81,6 +81,7 @@ #include "tweaks/advmode-contained.h" #include "tweaks/block-labors.h" #include "tweaks/civ-agreement-ui.h" +#include "tweaks/condition-material.h" #include "tweaks/craft-age-wear.h" #include "tweaks/eggs-fertile.h" #include "tweaks/embark-profile-name.h" @@ -183,6 +184,8 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector list_entries; + std::vector list_unk1; + std::vector list_unk2; + std::vector list_unk3; + std::vector list_visible; + }; + + static std::map order_mat_data; + + static void register_screen(T_screen *scr) + { + if (order_mat_data.find(scr) != order_mat_data.end()) + { + unregister_screen(scr); + } + auto data = new T_order_mat_data; + data->list_entries = scr->list_entries; + data->list_unk1 = scr->list_unk1; + data->list_unk2 = scr->list_unk2; + data->list_unk3 = scr->list_unk3; + data->list_visible = scr->list_visible; + order_mat_data[scr] = data; + } + + static void unregister_screen(T_screen *scr) + { + if (order_mat_data.find(scr) != order_mat_data.end() && order_mat_data[scr]) + { + T_order_mat_data *data = order_mat_data[scr]; + scr->list_entries = data->list_entries; + scr->list_unk1 = data->list_unk1; + scr->list_unk2 = data->list_unk2; + scr->list_unk3 = data->list_unk3; + scr->list_visible = data->list_visible; + delete data; + order_mat_data.erase(scr); + } + } + + void apply_filter() + { + if (order_mat_data.find(this) != order_mat_data.end() && order_mat_data[this]) + { + list_idx = 0; + T_order_mat_data *data = order_mat_data[this]; + // keep the first item ("no material") around, because attempts to delete it + // result in it still being displayed first, regardless of list_entries[0] + list_entries.resize(1); + list_unk1.resize(1); + list_unk2.resize(1); + list_unk3.resize(1); + list_visible.resize(1); + // skip "no material" here + for (size_t i = 1; i < data->list_entries.size(); i++) + { + // cap it at 32767 elements to be safe + if (list_entries.size() >= INT16_MAX) + { + break; + } + std::string *s = data->list_entries[i]; + if (s->find(filter) != std::string::npos) + { + list_entries.push_back(data->list_entries[i]); + list_unk1.push_back(data->list_unk1[i]); + list_unk2.push_back(data->list_unk2[i]); + list_unk3.push_back(data->list_unk3[i]); + // this should be small enough to fit in an int16_t + list_visible.push_back(int16_t(list_entries.size() - 1)); + } + } + } + } + + DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set *input)) + { + using namespace df::enums::interface_key; + if (mode == T_mode::Material) + { + for (auto key : *input) + { + if (key == LEAVESCREEN || key == SELECT) + { + INTERPOSE_NEXT(feed)(input); + unregister_screen(this); + return; + } + else if (key == STANDARDSCROLL_UP || key == STANDARDSCROLL_DOWN || + key == STANDARDSCROLL_PAGEUP || key == STANDARDSCROLL_PAGEDOWN) + { + INTERPOSE_NEXT(feed)(input); + } + int ch = Screen::keyToChar(key); + if (ch != -1) + { + if (ch == 0) + { + if (!filter.empty()) + { + filter.erase(filter.size() - 1); + } + } + else + { + filter += tolower(char(ch)); + } + apply_filter(); + } + } + } + else + { + INTERPOSE_NEXT(feed)(input); + if (mode == T_mode::Material) + { + register_screen(this); + apply_filter(); + } + } + } +}; + +std::map condition_material_hook::order_mat_data; + +IMPLEMENT_VMETHOD_INTERPOSE(condition_material_hook, feed);