int related_menu_fill( uilist &rmenu, const std::vector<std::pair<itype_id, std::string>> &related_recipes, const recipe_subset &available ) { const std::vector<uilist_entry> &entries = rmenu.entries; int np_last = entries.empty() ? -1 : entries.back().retval; if( related_recipes.empty() ) { return np_last; } std::string recipe_name_prev; for( const std::pair<itype_id, std::string> &p : related_recipes ) { // we have different recipes with the same names // list only one of them as we show and filter by name only std::string recipe_name = p.second; if( recipe_name == recipe_name_prev ) { continue; } recipe_name_prev = recipe_name; std::vector<const recipe *> current_part = available.search_result( p.first ); if( !current_part.empty() ) { bool defferent_recipes = false; // 1st pass: check if we need to add group for( size_t recipe_n = 0; recipe_n < current_part.size(); recipe_n++ ) { if( current_part[ recipe_n ]->result_name() != recipe_name ) { // add group rmenu.addentry( ++np_last, false, -1, recipe_name ); defferent_recipes = true; break; } else if( recipe_n == current_part.size() - 1 ) { // only one result rmenu.addentry( ++np_last, true, -1, "─ " + recipe_name ); } } if( defferent_recipes ) { std::string prev_item_name; // 2nd pass: add defferent recipes for( size_t recipe_n = 0; recipe_n < current_part.size(); recipe_n++ ) { std::string cur_item_name = current_part[ recipe_n ]->result_name(); if( cur_item_name != prev_item_name ) { std::string sym = recipe_n == current_part.size() - 1 ? "└ " : "├ "; rmenu.addentry( ++np_last, true, -1, sym + cur_item_name ); } prev_item_name = cur_item_name; } } } } return np_last; }
std::string peek_related_recipe( const recipe *current, const recipe_subset &available ) { // current recipe components std::vector<std::pair<itype_id, std::string>> related_components; const requirement_data &req = current->requirements(); for( const std::vector<item_comp> &comp_list : req.get_components() ) { for( const item_comp &a : comp_list ) { related_components.push_back( { a.type, item::nname( a.type, 1 ) } ); } } // current recipe result std::vector<std::pair<itype_id, std::string>> related_results; item tmp = current->create_result(); itype_id tid; if( tmp.contents.empty() ) { // use this item tid = tmp.typeId(); } else { // use the contained item tid = tmp.contents.front().typeId(); } const std::set<const recipe *> &known_recipes = g->u.get_learned_recipes().of_component( tid ); for( const auto &b : known_recipes ) { if( available.contains( b ) ) { related_results.push_back( { b->result(), b->result_name() } ); } } std::stable_sort( related_results.begin(), related_results.end(), []( const std::pair<std::string, std::string> &a, const std::pair<std::string, std::string> &b ) { return a.second < b.second; } ); uilist rel_menu; int np_last = -1; if( !related_components.empty() ) { rel_menu.addentry( ++np_last, false, -1, _( "COMPONENTS" ) ); } np_last = related_menu_fill( rel_menu, related_components, available ); if( !related_results.empty() ) { rel_menu.addentry( ++np_last, false, -1, _( "RESULTS" ) ); } np_last = related_menu_fill( rel_menu, related_results, available ); rel_menu.settext( _( "Related recipes:" ) ); rel_menu.query(); if( rel_menu.ret != UILIST_CANCEL ) { std::wstring wstr_recipe_name = utf8_to_wstr( rel_menu.entries[ rel_menu.ret ].txt ); return wstr_to_utf8( wstr_recipe_name.substr( 2 ) ); // 2 = prefix length } return ""; }
static void draw_recipe_subtabs( const catacurses::window &w, const std::string &tab, const std::string &subtab, const recipe_subset &available_recipes, TAB_MODE mode ) { werase( w ); int width = getmaxx( w ); for( int i = 0; i < width; i++ ) { if( i == 0 ) { mvwputch( w, 2, i, BORDER_COLOR, LINE_XXXO ); } else if( i == width ) { // @todo: that is always false! mvwputch( w, 2, i, BORDER_COLOR, LINE_XOXX ); } else { mvwputch( w, 2, i, BORDER_COLOR, LINE_OXOX ); } } for( int i = 0; i < 3; i++ ) { mvwputch( w, i, 0, BORDER_COLOR, LINE_XOXO ); // |^ mvwputch( w, i, width - 1, BORDER_COLOR, LINE_XOXO ); // ^| } switch( mode ) { case NORMAL: { int pos_x = 2;//draw the tabs on each other int tab_step = 3;//step between tabs, two for tabs border for( const auto &stt : craft_subcat_list[tab] ) { bool empty = available_recipes.empty_category( tab, stt != "CSC_ALL" ? stt : "" ); draw_subtab( w, pos_x, normalized_names[stt], subtab == stt, true, empty ); pos_x += utf8_width( normalized_names[stt] ) + tab_step; } break; } case FILTERED: case BATCH: werase( w ); for( int i = 0; i < 3; i++ ) { mvwputch( w, i, 0, BORDER_COLOR, LINE_XOXO ); // |^ mvwputch( w, i, width - 1, BORDER_COLOR, LINE_XOXO ); // ^| } break; } wrefresh( w ); }
void recipe_subset::include( const recipe_subset &subset ) { for( const auto &elem : subset ) { include( elem, subset.get_custom_difficulty( elem ) ); } }
#include "catch/catch.hpp" #include "crafting.h" #include "game.h" #include "itype.h" #include "map.h" #include "map_helpers.h" #include "npc.h" #include "output.h" #include "player.h" #include "player_helpers.h" #include "recipe_dictionary.h" TEST_CASE( "recipe_subset" ) { recipe_subset subset; REQUIRE( subset.size() == 0 ); GIVEN( "a recipe of rum" ) { const recipe *r = &recipe_id( "brew_rum" ).obj(); WHEN( "the recipe is included" ) { subset.include( r ); THEN( "it's in the subset" ) { CHECK( subset.size() == 1 ); CHECK( subset.contains( r ) ); } THEN( "it has its default difficulty" ) { CHECK( subset.get_custom_difficulty( r ) == r->difficulty ); }