// Dwarf Manipulator - a Therapist-style labor editor #include #include #include #include #include #include "df/viewscreen_unitlistst.h" #include "df/viewscreen_storesst.h" #include "df/interface_key.h" using std::set; using std::vector; using std::string; using namespace DFHack; using namespace df::enums; using df::global::gps; void OutputString(int8_t color, int &x, int y, const std::string &text) { Screen::paintString(Screen::Pen(' ', color, 0), x, y, text); x += text.length(); } // // START: Base Search functionality // template struct search_parent { vector *sort_list1; vector *sort_list2; int *cursor_pos; char select_key; const S *viewscreen; bool valid; bool entry_mode; bool redo_search; string search_string; vector saved_list1; vector saved_list2; df::interface_key select_token; const int ascii_to_enum_offset; search_parent() : ascii_to_enum_offset(interface_key::STRING_A048 - '0') { reset_all(); } virtual void init(int *cursor_pos, vector *sort_list1, vector *sort_list2 = NULL, char select_key = 's') { this->cursor_pos = cursor_pos; this->sort_list1 = sort_list1; this->sort_list2 = sort_list2; this->select_key = select_key; select_token = (df::interface_key) (ascii_to_enum_offset + select_key); valid = true; } void reset_search() { entry_mode = false; search_string = ""; saved_list1.clear(); saved_list2.clear(); } void reset_all() { reset_search(); valid = false; sort_list1 = NULL; sort_list2 = NULL; viewscreen = NULL; select_key = 's'; } void clear_search() { if (saved_list1.size() > 0) { *sort_list1 = saved_list1; if (sort_list2 != NULL) *sort_list2 = saved_list2; } } void do_search() { if (search_string.length() == 0) { clear_search(); return; } if (saved_list1.size() == 0) { saved_list1 = *sort_list1; if (sort_list2 != NULL) saved_list2 = *sort_list2; } sort_list1->clear(); if (sort_list2 != NULL) sort_list2->clear(); string search_string_l = toLower(search_string); for (int i = 0; i < saved_list1.size(); i++ ) { T *element = saved_list1[i]; string desc = toLower(get_element_description(element)); if (desc.find(search_string_l) != string::npos) { sort_list1->push_back(element); if (sort_list2 != NULL) sort_list2->push_back(saved_list2[i]); } } *cursor_pos = 0; } virtual bool process_input(const set *input) { bool key_processed = true; if (entry_mode) { df::interface_key last_token = *input->rbegin(); if (last_token >= interface_key::STRING_A032 && last_token <= interface_key::STRING_A126) { search_string += last_token - ascii_to_enum_offset; do_search(); } else if (last_token == interface_key::STRING_A000) { if (search_string.length() > 0) { search_string.erase(search_string.length()-1); do_search(); } } else if (input->count(interface_key::SELECT) || input->count(interface_key::LEAVESCREEN)) { entry_mode = false; } else if (input->count(interface_key::CURSOR_UP) || input->count(interface_key::CURSOR_DOWN) || input->count(interface_key::CURSOR_LEFT) || input->count(interface_key::CURSOR_RIGHT)) { entry_mode = false; key_processed = false; } } else if (input->count(select_token)) { entry_mode = true; } else { key_processed = false; } return key_processed; } virtual string get_element_description(T *element) const = 0; virtual void render () const = 0; virtual void do_post_update_check() { if (redo_search) { do_search(); redo_search = false; } } void print_search_option(int x) const { OutputString((entry_mode) ? 4 : 12, x, gps->dimy - 2, string(1, select_key)); OutputString((entry_mode) ? 10 : 15, x, gps->dimy - 2, ": Search"); if (search_string.length() > 0 || entry_mode) OutputString(15, x, gps->dimy - 2, ": " + search_string); if (entry_mode) OutputString(10, x, gps->dimy - 2, "_"); } }; template struct search_hook : T { typedef T interpose_base; static V module; DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) { module.init(this); if (!module.process_input(input)) { INTERPOSE_NEXT(feed)(input); module.do_post_update_check(); } } DEFINE_VMETHOD_INTERPOSE(void, render, ()) { module.init(this); INTERPOSE_NEXT(render)(); module.render(); } }; template V search_hook ::module; // // END: Base Search functionality // // // START: Stocks screen search // struct stocks_search : search_parent { virtual void render() const { if (!viewscreen->in_group_mode) print_search_option(1); else { int x = 1; OutputString(15, x, gps->dimy - 2, "Tab to enable Search"); } } virtual string get_element_description(df::item *element) const { return Items::getDescription(element, 0, true); } virtual bool process_input(const set *input) { if (viewscreen->in_group_mode) return false; if ((input->count(interface_key::CURSOR_UP) || input->count(interface_key::CURSOR_DOWN)) && !viewscreen->in_right_list) { saved_list1.clear(); entry_mode = false; if (search_string.length() > 0) redo_search = true; return false; } else return search_parent::process_input(input) || entry_mode; return true; } virtual void do_post_update_check() { if (viewscreen->in_group_mode) { clear_search(); reset_search(); } else search_parent::do_post_update_check(); } virtual void init(df::viewscreen_storesst *screen) { if (!valid) { viewscreen = screen; search_parent::init(&screen->item_cursor, &screen->items); } } }; typedef search_hook stocks_search_hook; IMPLEMENT_VMETHOD_INTERPOSE(stocks_search_hook, feed); IMPLEMENT_VMETHOD_INTERPOSE(stocks_search_hook, render); // // END: Stocks screen search // // // START: Unit screen search // struct unitlist_search : search_parent { virtual void render() const { print_search_option(28); } virtual string get_element_description(df::unit *element) const { return Translation::TranslateName(Units::getVisibleName(element), false); } virtual bool process_input(const set *input) { if (input->count(interface_key::CURSOR_LEFT) || input->count(interface_key::CURSOR_RIGHT)) { if (!entry_mode) { clear_search(); reset_search(); return false; } } else return search_parent::process_input(input) || entry_mode; return true; } virtual void init(df::viewscreen_unitlistst *screen) { if (!valid) { viewscreen = screen; search_parent::init(&screen->cursor_pos[viewscreen->page], &screen->units[viewscreen->page], &screen->jobs[viewscreen->page]); } } }; typedef search_hook unitlist_search_hook; IMPLEMENT_VMETHOD_INTERPOSE(unitlist_search_hook, feed); IMPLEMENT_VMETHOD_INTERPOSE(unitlist_search_hook, render); // // END: Unit screen search // DFHACK_PLUGIN("search"); DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) { if (!gps || !INTERPOSE_HOOK(unitlist_search_hook, feed).apply() || !INTERPOSE_HOOK(unitlist_search_hook, render).apply() || !INTERPOSE_HOOK(stocks_search_hook, feed).apply() || !INTERPOSE_HOOK(stocks_search_hook, render).apply()) out.printerr("Could not insert Search hooks!\n"); return CR_OK; } DFhackCExport command_result plugin_shutdown ( color_ostream &out ) { INTERPOSE_HOOK(unitlist_search_hook, feed).remove(); INTERPOSE_HOOK(unitlist_search_hook, render).remove(); INTERPOSE_HOOK(stocks_search_hook, feed).remove(); INTERPOSE_HOOK(stocks_search_hook, render).remove(); return CR_OK; } DFhackCExport command_result plugin_onstatechange ( color_ostream &out, state_change_event event ) { unitlist_search_hook::module.reset_all(); stocks_search_hook::module.reset_all(); return CR_OK; }