// Dwarf Manipulator - a Therapist-style labor editor #include #include #include #include #include #include "df/viewscreen_unitlistst.h" #include "df/viewscreen_storesst.h" #include "df/viewscreen_tradegoodsst.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 class search_parent { public: void reset_all() { reset_search(); valid = false; sort_list1 = NULL; sort_list2 = NULL; viewscreen = NULL; select_key = 's'; } virtual bool process_input(set *input) { if (lock != NULL && lock != this) return false; if (!should_check_input(input)) return false; 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)) { end_entry_mode(); } 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)) { end_entry_mode(); key_processed = false; } } else if (input->count(select_token)) { start_entry_mode(); } else if (input->count((df::interface_key) (select_token + shift_offset))) { clear_search(); } else { key_processed = false; } return key_processed || entry_mode; } virtual void do_post_update_check() { if (redo_search) { do_search(); redo_search = false; } } static search_parent *lock; protected: const S *viewscreen; vector saved_list1; vector saved_list2; bool valid; bool redo_search; string search_string; search_parent() : ascii_to_enum_offset(interface_key::STRING_A048 - '0'), shift_offset('A' - 'a') { 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; } bool is_entry_mode() { return entry_mode; } void start_entry_mode() { entry_mode = true; lock = this; } void end_entry_mode() { entry_mode = false; lock = NULL; } void reset_search() { end_entry_mode(); search_string = ""; saved_list1.clear(); saved_list2.clear(); } void clear_search() { if (saved_list1.size() > 0) { *sort_list1 = saved_list1; if (sort_list2 != NULL) *sort_list2 = saved_list2; } search_string = ""; } 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 should_check_input(set *input) { return true; } void print_search_option(int x, int y = -1) const { if (y == -1) y = gps->dimy - 2; OutputString((entry_mode) ? 4 : 12, x, y, string(1, select_key)); OutputString((entry_mode) ? 10 : 15, x, y, ": Search"); if (search_string.length() > 0 || entry_mode) OutputString(15, x, y, ": " + search_string); if (entry_mode) OutputString(10, x, y, "_"); } virtual string get_element_description(T *element) const = 0; virtual void render () const = 0; private: vector *sort_list1; vector *sort_list2; int *cursor_pos; char select_key; bool entry_mode; df::interface_key select_token; const int ascii_to_enum_offset; const int shift_offset; }; template search_parent *search_parent ::lock = NULL; 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 // class stocks_search : public search_parent { public: 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 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); } } private: virtual string get_element_description(df::item *element) const { return Items::getDescription(element, 0, true); } virtual bool should_check_input(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(); end_entry_mode(); if (search_string.length() > 0) redo_search = true; return false; } return true; } }; 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 // class unitlist_search : public search_parent { public: virtual void render() const { print_search_option(28); } 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]); } } private: virtual string get_element_description(df::unit *element) const { return Translation::TranslateName(Units::getVisibleName(element), false); } virtual bool should_check_input(set *input) { if (input->count(interface_key::CURSOR_LEFT) || input->count(interface_key::CURSOR_RIGHT)) { if (!is_entry_mode()) { clear_search(); reset_all(); } else input->clear(); return false; } return true; } }; typedef search_hook unitlist_search_hook; IMPLEMENT_VMETHOD_INTERPOSE(unitlist_search_hook, feed); IMPLEMENT_VMETHOD_INTERPOSE(unitlist_search_hook, render); // // END: Unit screen search // // // START: Trade screen search // class trade_search_base : public search_parent { private: virtual string get_element_description(df::item *element) const { return Items::getDescription(element, 0, true); } }; class trade_search_merc : public trade_search_base { public: virtual void render() const { print_search_option(2, 26); } virtual void init(df::viewscreen_tradegoodsst *screen) { if (!valid) { viewscreen = screen; search_parent::init(&screen->trader_cursor, &screen->trader_items, NULL, 'q'); } } }; typedef search_hook trade_search_merc_hook; IMPLEMENT_VMETHOD_INTERPOSE(trade_search_merc_hook, feed); IMPLEMENT_VMETHOD_INTERPOSE(trade_search_merc_hook, render); class trade_search_fort : public trade_search_base { public: virtual void render() const { print_search_option(42, 26); } virtual void init(df::viewscreen_tradegoodsst *screen) { if (!valid) { viewscreen = screen; search_parent::init(&screen->broker_cursor, &screen->broker_items, NULL, 'w'); } } }; typedef search_hook trade_search_fort_hook; IMPLEMENT_VMETHOD_INTERPOSE(trade_search_fort_hook, feed); IMPLEMENT_VMETHOD_INTERPOSE(trade_search_fort_hook, render); // // END: Trade 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(trade_search_merc_hook, feed).apply() || !INTERPOSE_HOOK(trade_search_merc_hook, render).apply() || !INTERPOSE_HOOK(trade_search_fort_hook, feed).apply() || !INTERPOSE_HOOK(trade_search_fort_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(trade_search_merc_hook, feed).remove(); INTERPOSE_HOOK(trade_search_merc_hook, render).remove(); INTERPOSE_HOOK(trade_search_fort_hook, feed).remove(); INTERPOSE_HOOK(trade_search_fort_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(); trade_search_merc_hook::module.reset_all(); trade_search_fort_hook::module.reset_all(); stocks_search_hook::module.reset_all(); return CR_OK; }