diff --git a/plugins/stocks.cpp b/plugins/stocks.cpp index b6adf6827..b9e864f57 100644 --- a/plugins/stocks.cpp +++ b/plugins/stocks.cpp @@ -11,12 +11,14 @@ #include "df/job.h" #include "df/unit.h" #include "df/world.h" +#include "df/item_quality.h" #include "modules/Gui.h" #include "modules/Items.h" #include "modules/Job.h" #include "modules/World.h" #include "modules/Screen.h" +#include "modules/Maps.h" using df::global::world; @@ -43,10 +45,13 @@ static void debug(const string &msg) } -/*struct FlagDisplay +static string getQualityName(const df::item_quality quality) { - -};*/ + if (gps->dimx - SIDEBAR_WIDTH < 60) + return int_to_string(quality); + else + return ENUM_KEY_STR(item_quality, quality); +} template class StockListColumn : public ListColumn @@ -97,6 +102,43 @@ class StockListColumn : public ListColumn OutputString(COLOR_GREY, x, y, "I"); else OutputString(COLOR_LIGHTBLUE, x, y, " "); + + if (item->isImproved()) + OutputString(COLOR_BLUE, x, y, "* "); + else + OutputString(COLOR_LIGHTBLUE, x, y, " "); + + auto quality = static_cast(item->getQuality()); + if (quality > item_quality::Ordinary) + { + auto color = COLOR_BROWN; + switch(quality) + { + case item_quality::FinelyCrafted: + color = COLOR_CYAN; + break; + + case item_quality::Superior: + color = COLOR_LIGHTBLUE; + break; + + case item_quality::Exceptional: + color = COLOR_GREEN; + break; + + case item_quality::Masterful: + color = COLOR_LIGHTGREEN; + break; + + case item_quality::Artifact: + color = COLOR_BLUE; + break; + + default: + break; + } + OutputString(color, x, y, getQualityName(quality)); + } } }; @@ -104,6 +146,8 @@ class StockListColumn : public ListColumn class ViewscreenStocks : public dfhack_viewscreen { public: + static df::item_flags hide_flags; + ViewscreenStocks() { selected_column = 0; @@ -117,7 +161,23 @@ public: items_column.changeHighlight(0); - hide_flags.whole = 0; + apply_to_all = false; + hide_unflagged = false; + + checked_flags.bits.in_job = true; + checked_flags.bits.rotten = true; + checked_flags.bits.foreign = true; + checked_flags.bits.owned = true; + checked_flags.bits.forbid = true; + checked_flags.bits.dump = true; + checked_flags.bits.on_fire = true; + checked_flags.bits.melt = true; + checked_flags.bits.on_fire = true; + checked_flags.bits.in_inventory = true; + + min_quality = item_quality::Ordinary; + max_quality = item_quality::Artifact; + min_wear = 0; populateItems(); @@ -144,53 +204,100 @@ public: return; } - if (input->count(interface_key::CUSTOM_SHIFT_G)) + if (input->count(interface_key::CUSTOM_SHIFT_G)) { hide_flags.bits.foreign = !hide_flags.bits.foreign; populateItems(); } - else if (input->count(interface_key::CUSTOM_SHIFT_J)) + else if (input->count(interface_key::CUSTOM_SHIFT_J)) { hide_flags.bits.in_job = !hide_flags.bits.in_job; populateItems(); } - else if (input->count(interface_key::CUSTOM_SHIFT_N)) + else if (input->count(interface_key::CUSTOM_SHIFT_T)) { hide_flags.bits.rotten = !hide_flags.bits.rotten; populateItems(); } - else if (input->count(interface_key::CUSTOM_SHIFT_O)) + else if (input->count(interface_key::CUSTOM_SHIFT_O)) { hide_flags.bits.owned = !hide_flags.bits.owned; populateItems(); } - else if (input->count(interface_key::CUSTOM_SHIFT_F)) + else if (input->count(interface_key::CUSTOM_SHIFT_F)) { hide_flags.bits.forbid = !hide_flags.bits.forbid; populateItems(); } - else if (input->count(interface_key::CUSTOM_SHIFT_D)) + else if (input->count(interface_key::CUSTOM_SHIFT_D)) { hide_flags.bits.dump = !hide_flags.bits.dump; populateItems(); } - else if (input->count(interface_key::CUSTOM_SHIFT_R)) + else if (input->count(interface_key::CUSTOM_SHIFT_R)) { hide_flags.bits.on_fire = !hide_flags.bits.on_fire; populateItems(); } - else if (input->count(interface_key::CUSTOM_SHIFT_M)) + else if (input->count(interface_key::CUSTOM_SHIFT_M)) { hide_flags.bits.melt = !hide_flags.bits.melt; populateItems(); } - else if (input->count(interface_key::CUSTOM_SHIFT_I)) + else if (input->count(interface_key::CUSTOM_SHIFT_I)) { hide_flags.bits.in_inventory = !hide_flags.bits.in_inventory; populateItems(); } + else if (input->count(interface_key::CUSTOM_SHIFT_N)) + { + hide_unflagged = !hide_unflagged; + populateItems(); + } + else if (input->count(interface_key::CUSTOM_SHIFT_C)) + { + setAllFlags(true); + populateItems(); + } + else if (input->count(interface_key::CUSTOM_SHIFT_E)) + { + setAllFlags(false); + populateItems(); + } + else if (input->count(interface_key::SECONDSCROLL_UP)) + { + if (min_quality > item_quality::Ordinary) + { + min_quality = static_cast(static_cast(min_quality) - 1); + populateItems(); + } + } + else if (input->count(interface_key::SECONDSCROLL_DOWN)) + { + if (min_quality < max_quality && min_quality < item_quality::Artifact) + { + min_quality = static_cast(static_cast(min_quality) + 1); + populateItems(); + } + } + else if (input->count(interface_key::SECONDSCROLL_PAGEUP)) + { + if (max_quality > min_quality && max_quality > item_quality::Ordinary) + { + max_quality = static_cast(static_cast(max_quality) - 1); + populateItems(); + } + } + else if (input->count(interface_key::SECONDSCROLL_PAGEDOWN)) + { + if (max_quality < item_quality::Artifact) + { + max_quality = static_cast(static_cast(max_quality) + 1); + populateItems(); + } + } - else if (input->count(interface_key::CUSTOM_SHIFT_Z)) + else if (input->count(interface_key::CUSTOM_SHIFT_Z)) { input->clear(); auto item = items_column.getFirstSelectedElem(); @@ -201,16 +308,34 @@ public: return; Screen::dismiss(this); + // Could be clever here, if item is in a container, to look inside the container. + // But that's different for built containers vs bags/pots in stockpiles. send_key(interface_key::D_LOOK); move_cursor(*pos); } + else if (input->count(interface_key::CUSTOM_SHIFT_A)) + { + apply_to_all = !apply_to_all; + } + else if (input->count(interface_key::CUSTOM_SHIFT_P)) + { + df::item_flags flags; + flags.bits.dump = true; + applyFlag(flags); + } + else if (input->count(interface_key::CUSTOM_SHIFT_B)) + { + df::item_flags flags; + flags.bits.forbid = true; + applyFlag(flags); + } - else if (input->count(interface_key::CURSOR_LEFT)) + else if (input->count(interface_key::CURSOR_LEFT)) { --selected_column; validateColumn(); } - else if (input->count(interface_key::CURSOR_RIGHT)) + else if (input->count(interface_key::CURSOR_RIGHT)) { selected_column++; validateColumn(); @@ -224,7 +349,7 @@ public: } } - void move_cursor(df::coord &pos) + void move_cursor(const df::coord &pos) { Gui::setCursorCoords(pos.x, pos.y, pos.z); send_key(interface_key::CURSOR_DOWN_Z); @@ -264,7 +389,7 @@ public: OutputString(COLOR_BROWN, x, y, "Filters", true, left_margin); OutputString(COLOR_LIGHTRED, x, y, "Press Shift-Hotkey to Toggle", true, left_margin); OutputFilterString(x, y, "In Job", "J", !hide_flags.bits.in_job, true, left_margin, COLOR_LIGHTBLUE); - OutputFilterString(x, y, "Rotten", "N", !hide_flags.bits.rotten, true, left_margin, COLOR_CYAN); + OutputFilterString(x, y, "Rotten", "T", !hide_flags.bits.rotten, true, left_margin, COLOR_CYAN); OutputFilterString(x, y, "Foreign Made", "G", !hide_flags.bits.foreign, true, left_margin, COLOR_BROWN); OutputFilterString(x, y, "Owned", "O", !hide_flags.bits.owned, true, left_margin, COLOR_GREEN); OutputFilterString(x, y, "Forbidden", "F", !hide_flags.bits.forbid, true, left_margin, COLOR_RED); @@ -272,12 +397,29 @@ public: OutputFilterString(x, y, "On Fire", "R", !hide_flags.bits.on_fire, true, left_margin, COLOR_LIGHTRED); OutputFilterString(x, y, "Melt", "M", !hide_flags.bits.melt, true, left_margin, COLOR_BLUE); OutputFilterString(x, y, "In Inventory", "I", !hide_flags.bits.in_inventory, true, left_margin, COLOR_GREY); + OutputFilterString(x, y, "No Flags", "N", !hide_unflagged, true, left_margin, COLOR_GREY); + ++y; + OutputHotkeyString(x, y, "Clear All", "Shift-C", true, left_margin); + OutputHotkeyString(x, y, "Enable All", "Shift-E", true, left_margin); + ++y; + OutputHotkeyString(x, y, "Min Qual: ", "-+"); + OutputString(COLOR_BROWN, x, y, getQualityName(min_quality), true, left_margin); + OutputHotkeyString(x, y, "Max Qual: ", "/*"); + OutputString(COLOR_BROWN, x, y, getQualityName(max_quality), true, left_margin); + + OutputHotkeyString(x, y, "Min Wear: ", "Shift-W"); + OutputString(COLOR_BROWN, x, y, int_to_string(min_wear), true, left_margin); + ++y; - OutputString(COLOR_BROWN, x, y, "Actions (" + int_to_string(items_column.getDisplayedListSize()) + " Items)", - true, left_margin); + OutputString(COLOR_BROWN, x, y, "Actions ("); + OutputString(COLOR_LIGHTGREEN, x, y, int_to_string(items_column.getDisplayedListSize())); + OutputString(COLOR_BROWN, x, y, " Items)", true, left_margin); OutputHotkeyString(x, y, "Zoom", "Shift-Z", true, left_margin); - + OutputHotkeyString(x, y, "Apply to: ", "Shift-A"); + OutputString(COLOR_BROWN, x, y, (apply_to_all) ? "Listed" : "Selected", true, left_margin); + OutputHotkeyString(x, y, "Dump", "Shift-P", true, left_margin); + OutputHotkeyString(x, y, "Forbid", "Shift-B", true, left_margin); } std::string getFocusString() { return "stocks_view"; } @@ -285,9 +427,12 @@ public: private: StockListColumn items_column; int selected_column; - df::item_flags hide_flags; + bool apply_to_all, hide_unflagged; + df::item_flags checked_flags; + df::item_quality min_quality, max_quality; + int16_t min_wear; - df::coord *getRealPos(df::item *item) + static df::coord *getRealPos(df::item *item) { if (item->flags.bits.in_inventory) { @@ -318,6 +463,50 @@ private: return &item->pos; } + void applyFlag(const df::item_flags flags) + { + if (apply_to_all) + { + int state_to_apply = -1; + for (auto iter = items_column.getDisplayList().begin(); iter != items_column.getDisplayList().end(); iter++) + { + auto item = (*iter)->elem; + if (item) + { + // Set all flags based on state of first item in list + if (state_to_apply == -1) + state_to_apply = (item->flags.whole & flags.whole) ? 0 : 1; + + if (state_to_apply) + item->flags.whole |= flags.whole; + else + item->flags.whole &= ~flags.whole; + } + } + } + else + { + auto item = items_column.getFirstSelectedElem(); + if (item) + item->flags.whole ^= flags.whole; + } + } + + void setAllFlags(bool state) + { + hide_flags.bits.in_job = state; + hide_flags.bits.rotten = state; + hide_flags.bits.foreign = state; + hide_flags.bits.owned = state; + hide_flags.bits.forbid = state; + hide_flags.bits.dump = state; + hide_flags.bits.on_fire = state; + hide_flags.bits.melt = state; + hide_flags.bits.on_fire = state; + hide_flags.bits.in_inventory = state; + hide_unflagged = state; + } + void populateItems() { items_column.clear(); @@ -347,10 +536,41 @@ private: if (item->pos.x == -30000) continue; - if (!getRealPos(item)) + auto pos = getRealPos(item); + if (!pos) + continue; + + auto designation = Maps::getTileDesignation(*pos); + if (!designation) continue; - auto label = pad_string(Items::getDescription(item, 0, true), MAX_NAME, false, true); + if (designation->bits.hidden) + continue; // Items in parts of the map not yet revealed + + if (hide_unflagged && !(item->flags.whole & checked_flags.whole)) + continue; + + auto quality = static_cast(item->getQuality()); + if (quality < min_quality || quality > max_quality) + continue; + + auto wear = item->getWear(); + if (wear < min_wear) + continue; + + auto label = Items::getDescription(item, 0, false); + if (wear > 0) + { + string wearX = ""; + for (int i = 0; i < wear; i++) + { + wearX += "X"; + } + + label = wearX + label + wearX; + } + + label = pad_string(label, MAX_NAME, false, true); items_column.add(label, item); } @@ -371,6 +591,8 @@ private: } }; +df::item_flags ViewscreenStocks::hide_flags; + static command_result stocks_cmd(color_ostream &out, vector & parameters) { @@ -402,6 +624,8 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector *> &getDisplayList() + { + return display_list; + } + size_t getBaseListSize() { return list.size();