bool list_items_match( const item *item, std::string sPattern ) { size_t iPos; bool hasExclude = false; if( sPattern.find( "-" ) != std::string::npos ) { hasExclude = true; } do { iPos = sPattern.find( "," ); std::string pat = ( iPos == std::string::npos ) ? sPattern : sPattern.substr( 0, iPos ); bool exclude = false; if( pat.substr( 0, 1 ) == "-" ) { exclude = true; pat = pat.substr( 1, pat.size() - 1 ); } else if( hasExclude ) { hasExclude = false; //If there are non exclusive items to filter, we flip this back to false. } std::string namepat = pat; std::transform( namepat.begin(), namepat.end(), namepat.begin(), tolower ); if( lcmatch( remove_color_tags( item->tname() ), namepat ) ) { return !exclude; } if( pat.find( "{", 0 ) != std::string::npos ) { std::string adv_pat_type = pat.substr( 1, pat.find( ":" ) - 1 ); std::string adv_pat_search = pat.substr( pat.find( ":" ) + 1, ( pat.find( "}" ) - pat.find( ":" ) ) - 1 ); std::transform( adv_pat_search.begin(), adv_pat_search.end(), adv_pat_search.begin(), tolower ); if( adv_pat_type == "c" && lcmatch( item->get_category().name, adv_pat_search ) ) { return !exclude; } else if( adv_pat_type == "m" ) { for( auto material : item->made_of_types() ) { if( lcmatch( material->name(), adv_pat_search ) ) { return !exclude; } } } else if( adv_pat_type == "dgt" && item->damage() > atoi( adv_pat_search.c_str() ) ) { return !exclude; } else if( adv_pat_type == "dlt" && item->damage() < atoi( adv_pat_search.c_str() ) ) { return !exclude; } } if( iPos != std::string::npos ) { sPattern = sPattern.substr( iPos + 1, sPattern.size() ); } } while( iPos != std::string::npos ); return hasExclude; }
bool match_include_exclude( const std::string &text, std::string filter ) { size_t iPos; bool found = false; if( filter.empty() ) { return false; } do { iPos = filter.find( ',' ); std::string term = iPos == std::string::npos ? filter : filter.substr( 0, iPos ); const bool exclude = term.substr( 0, 1 ) == "-"; if( exclude ) { term = term.substr( 1 ); } if( ( !found || exclude ) && lcmatch( text, term ) ) { if( exclude ) { return false; } found = true; } if( iPos != std::string::npos ) { filter = filter.substr( iPos + 1, filter.size() ); } } while( iPos != std::string::npos ); return found; }
overmapbuffer::t_notes_vector overmapbuffer::get_notes(int z, const std::string* pattern) { t_notes_vector result; for( auto &it : overmaps ) { const overmap &om = *it.second; const int offset_x = om.pos().x * OMAPX; const int offset_y = om.pos().y * OMAPY; for (int i = 0; i < OMAPX; i++) { for (int j = 0; j < OMAPY; j++) { const std::string ¬e = om.note(i, j, z); if (note.empty()) { continue; } if (pattern != NULL && lcmatch( note, *pattern ) ) { // pattern not found in note text continue; } result.push_back(t_point_with_note( point(offset_x + i, offset_y + j), om.note(i, j, z) )); } } } return result; }
bool search_reqs( std::vector<std::vector<item_comp> > gp, const std::string &txt ) { return std::any_of( gp.begin(), gp.end(), [&]( const std::vector<item_comp> &opts ) { return std::any_of( opts.begin(), opts.end(), [&]( const item_comp & ic ) { return lcmatch( item::nname( ic.type ), txt ); } ); } ); }
bool search_reqs( group gp, const std::string &txt ) { return std::any_of( gp.begin(), gp.end(), [&]( const typename group::value_type & opts ) { return std::any_of( opts.begin(), opts.end(), [&]( const typename group::value_type::value_type & e ) { return lcmatch( e.to_string(), txt ); } ); } ); }
std::vector<const recipe *> recipe_dictionary::search( const std::string &txt ) { std::vector<const recipe *> res; for( const auto &e : recipe_dict.recipes ) { if( lcmatch( item::nname( e.second.result ), txt ) ) { res.push_back( &e.second ); } } return res; }
bool lcmatch_any( const std::vector< std::vector<T> > &list_of_list, const std::string &filter ) { for( auto &list : list_of_list ) { for( auto &comp : list ) { if( lcmatch( item::nname( comp.type ), filter ) ) { return true; } } } return false; }
std::vector<std::string> input_context::filter_strings_by_phrase( const std::vector<std::string> &strings, const std::string &phrase ) const { std::vector<std::string> filtered_strings; for( auto &str : strings ) { if( lcmatch( remove_color_tags( get_action_name( str ) ), phrase ) ) { filtered_strings.push_back( str ); } } return filtered_strings; }
std::vector<std::string> requirement_data::get_folded_list( int width, const inventory &crafting_inv, const std::vector< std::vector<T> > &objs, int batch, std::string hilite ) const { // hack: ensure 'cached' availability is up to date can_make_with_inventory( crafting_inv ); std::vector<std::string> out_buffer; for( const auto &comp_list : objs ) { const bool has_one = any_marked_available( comp_list ); std::ostringstream buffer; std::vector<std::string> buffer_has; bool already_has; for( auto a = comp_list.begin(); a != comp_list.end(); ++a ) { already_has = false; for( auto cont : buffer_has ) { if( cont == a->to_string( batch ) + a->get_color( has_one, crafting_inv, batch ) ) { already_has = true; break; } } if( already_has ) { continue; } if( a != comp_list.begin() ) { buffer << "<color_white> " << _( "OR" ) << "</color> "; } const std::string col = a->get_color( has_one, crafting_inv, batch ); if( !hilite.empty() && lcmatch( a->to_string( batch ), hilite ) ) { buffer << get_tag_from_color( yellow_background( color_from_string( col ) ) ); } else { buffer << "<color_" << col << ">"; } buffer << a->to_string( batch ) << "</color>" << "</color>"; buffer_has.push_back( a->to_string( batch ) + a->get_color( has_one, crafting_inv, batch ) ); } std::vector<std::string> folded = foldstring( buffer.str(), width - 2 ); for( size_t i = 0; i < folded.size(); i++ ) { if( i == 0 ) { out_buffer.push_back( std::string( "> " ).append( folded[i] ) ); } else { out_buffer.push_back( std::string( " " ).append( folded[i] ) ); } } } return out_buffer; }
std::vector<const recipe *> recipe_subset::search( const std::string &txt, const search_type key ) const { std::vector<const recipe *> res; std::copy_if( recipes.begin(), recipes.end(), std::back_inserter( res ), [&]( const recipe * r ) { switch( key ) { case search_type::name: return lcmatch( item::nname( r->result ), txt ); case search_type::skill: return lcmatch( r->required_skills_string(), txt ) || lcmatch( r->skill_used->name(), txt ); case search_type::component: return search_reqs( r->requirements().get_components(), txt ); case search_type::tool: return search_reqs( r->requirements().get_tools(), txt ); case search_type::quality: return search_reqs( r->requirements().get_qualities(), txt ); case search_type::quality_result: { const auto &quals = item::find_type( r->result )->qualities; return std::any_of( quals.begin(), quals.end(), [&]( const std::pair<quality_id, int> &e ) { return lcmatch( e.first->name, txt ); } ); } default: return false; } } ); return res; }
/* * repopulate filtered entries list (fentries) and set fselected accordingly */ void uimenu::filterlist() { bool notfiltering = ( ! filtering || filter.size() < 1 ); int num_entries = entries.size(); bool nocase = (filtering_nocase == true); // todo: && is_all_lc( filter ) std::string fstr = ""; fstr.reserve(filter.size()); if ( nocase ) { transform( filter.begin(), filter.end(), std::back_inserter(fstr), tolower ); } else { fstr = filter; } fentries.clear(); fselected = -1; int f = 0; for( int i = 0; i < num_entries; i++ ) { if( notfiltering || ( nocase == false && entries[ i ].txt.find(filter) != -1 ) || lcmatch(entries[i].txt, fstr ) ) { fentries.push_back( i ); if ( i == selected ) { fselected = f; } else if ( i > selected && fselected == -1 ) { // Past the previously selected entry, which has been filtered out, // choose another nearby entry instead. fselected = f; } f++; } } if ( fselected == -1 ) { fselected = 0; vshift = 0; if ( fentries.empty() ) { selected = -1; } else { selected = fentries [ 0 ]; } } else if (fselected < fentries.size()) { selected = fentries[fselected]; } else { fselected = selected = -1; } // scroll to top of screen if all remaining entries fit the screen. if (fentries.size() <= vmax) { vshift = 0; } }
std::vector<const recipe *> recipe_subset::search( const std::string &txt, const search_type key ) const { std::vector<const recipe *> res; std::copy_if( recipes.begin(), recipes.end(), std::back_inserter( res ), [&]( const recipe * r ) { if( !*r ) { return false; } switch( key ) { case search_type::name: return lcmatch( r->result_name(), txt ); case search_type::skill: return lcmatch( r->required_skills_string( nullptr ), txt ) || lcmatch( r->skill_used->name(), txt ); case search_type::primary_skill: return lcmatch( r->skill_used->name(), txt ); case search_type::component: return search_reqs( r->requirements().get_components(), txt ); case search_type::tool: return search_reqs( r->requirements().get_tools(), txt ); case search_type::quality: return search_reqs( r->requirements().get_qualities(), txt ); case search_type::quality_result: { const auto &quals = item::find_type( r->result() )->qualities; return std::any_of( quals.begin(), quals.end(), [&]( const std::pair<quality_id, int> &e ) { return lcmatch( e.first->name, txt ); } ); } case search_type::description_result: { const item result = r->create_result(); return lcmatch( remove_color_tags( result.info( true ) ), txt ); } default: return false; } } ); return res; }
/* * repopulate filtered entries list (fentries) and set fselected accordingly */ void uimenu::filterlist() { bool notfiltering = ( ! filtering || filter.size() < 1 ); int num_entries = entries.size(); bool nocase = (filtering_nocase == true); // todo: && is_all_lc( filter ) std::string fstr = ""; fstr.reserve(filter.size()); if ( nocase ) { transform( filter.begin(), filter.end(), std::back_inserter(fstr), tolower ); } else { fstr = filter; } fentries.clear(); fselected = -1; int f = 0; for ( int i = 0; i < num_entries; i++ ) { if ( notfiltering || ( nocase == false && entries[ i ].txt.find(filter) != -1 ) || lcmatch(entries[i].txt, fstr) ) { fentries.push_back( i ); if ( i == selected ) { fselected = f; } f++; } } if ( fselected == -1 ) { fselected = 0; vshift = 0; if ( fentries.empty() ) { selected = -1; } else { selected = fentries [ 0 ]; } } }
std::function<bool( const item & )> item_filter_from_string( std::string filter ) { if( filter.empty() ) { // Variable without name prevents unused parameter warning return []( const item & ) { return true; }; } // remove curly braces (they only get in the way) if( filter.find( '{' ) != std::string::npos ) { filter.erase( std::remove( filter.begin(), filter.end(), '{' ) ); } if( filter.find( '}' ) != std::string::npos ) { filter.erase( std::remove( filter.begin(), filter.end(), '}' ) ); } if( filter.find( "," ) != std::string::npos ) { // functions which only one of which must return true std::vector<std::function<bool( const item & )> > functions; // Functions that must all return true std::vector<std::function<bool( const item & )> > inv_functions; size_t comma = filter.find( "," ); while( !filter.empty() ) { const auto ¤t_filter = trim( filter.substr( 0, comma ) ); if( !current_filter.empty() ) { auto current_func = item_filter_from_string( current_filter ); if( current_filter[0] == '-' ) { inv_functions.push_back( current_func ); } else { functions.push_back( current_func ); } } if( comma != std::string::npos ) { filter = trim( filter.substr( comma + 1 ) ); comma = filter.find( "," ); } else { break; } } return [functions, inv_functions]( const item & it ) { auto apply = [&]( const std::function<bool( const item & )> &func ) { return func( it ); }; bool p_result = std::any_of( functions.begin(), functions.end(), apply ); bool n_result = std::all_of( inv_functions.begin(), inv_functions.end(), apply ); if( !functions.empty() && inv_functions.empty() ) { return p_result; } if( functions.empty() && !inv_functions.empty() ) { return n_result; } return p_result && n_result; }; } bool exclude = filter[0] == '-'; if( exclude ) { return [filter]( const item & i ) { return !item_filter_from_string( filter.substr( 1 ) )( i ); }; } size_t colon; char flag = '\0'; if( ( colon = filter.find( ":" ) ) != std::string::npos ) { if( colon >= 1 ) { flag = filter[colon - 1]; filter = filter.substr( colon + 1 ); } } switch( flag ) { case 'c'://category return [filter]( const item & i ) { return lcmatch( i.get_category().name, filter ); }; break; case 'm'://material return [filter]( const item & i ) { return std::any_of( i.made_of().begin(), i.made_of().end(), [&filter]( const material_id & mat ) { return lcmatch( mat->name(), filter ); } ); }; break; case 'b'://both return [filter]( const item & i ) { auto pair = get_both( filter ); return item_filter_from_string( pair.first )( i ) && item_filter_from_string( pair.second )( i ); }; break; default://by name return [filter]( const item & a ) { return lcmatch( a.tname(), filter ); }; break; } }
void pick_recipes( const inventory &crafting_inv, const std::vector<npc *> &helpers, std::vector<const recipe *> ¤t, std::vector<bool> &available, std::string tab, std::string subtab, std::string filter ) { bool search_name = true; bool search_tool = false; bool search_component = false; bool search_skill = false; bool search_skill_primary_only = false; bool search_qualities = false; bool search_result_qualities = false; size_t pos = filter.find( ":" ); if( pos != std::string::npos ) { search_name = false; std::string searchType = filter.substr( 0, pos ); for( auto &elem : searchType ) { if( elem == 'n' ) { search_name = true; } else if( elem == 't' ) { search_tool = true; } else if( elem == 'c' ) { search_component = true; } else if( elem == 's' ) { search_skill = true; } else if( elem == 'S' ) { search_skill_primary_only = true; } else if( elem == 'q' ) { search_qualities = true; } else if( elem == 'Q' ) { search_result_qualities = true; } } filter = filter.substr( pos + 1 ); } std::vector<recipe *> available_recipes; if( filter == "" ) { available_recipes = recipe_dict.in_category( tab ); } else { // lcmatch needs an all lowercase string to match case-insensitive std::transform( filter.begin(), filter.end(), filter.begin(), tolower ); available_recipes.insert( available_recipes.begin(), recipe_dict.begin(), recipe_dict.end() ); } current.clear(); available.clear(); std::vector<const recipe *> filtered_list; int max_difficulty = 0; for( auto rec : available_recipes ) { const auto &needs = rec->requirements(); if( subtab == "CSC_ALL" || rec->subcat == subtab || ( rec->subcat == "" && craft_subcat_list[tab].back() == subtab ) || filter != "" ) { if( ( !g->u.knows_recipe( rec ) && -1 == g->u.has_recipe( rec, crafting_inv, helpers ) ) || ( rec->difficulty < 0 ) ) { continue; } if( filter != "" ) { if( ( search_name && !lcmatch( item::nname( rec->result ), filter ) ) || ( search_tool && !lcmatch_any( needs.get_tools(), filter ) ) || ( search_component && !lcmatch_any( needs.get_components(), filter ) ) ) { continue; } bool match_found = false; if( search_result_qualities ) { const itype *it = item::find_type( rec->result ); for( auto &quality : it->qualities ) { if( lcmatch( quality.first.obj().name, filter ) ) { match_found = true; break; } } if( !match_found ) { continue; } } if( search_qualities ) { for( auto quality_reqs : needs.get_qualities() ) { for( auto quality : quality_reqs ) { if( lcmatch( quality.to_string(), filter ) ) { match_found = true; break; } } if( match_found ) { break; } } if( !match_found ) { continue; } } if( search_skill ) { if( !rec->skill_used ) { continue; } else if( !lcmatch( rec->skill_used.obj().name(), filter ) && !lcmatch( rec->required_skills_string(), filter ) ) { continue; } } if( search_skill_primary_only ) { if( !rec->skill_used ) { continue; } else if( !lcmatch( rec->skill_used.obj().name(), filter ) ) { continue; } } } filtered_list.push_back( rec ); } max_difficulty = std::max( max_difficulty, rec->difficulty ); } int truecount = 0; for( int i = max_difficulty; i != -1; --i ) { for( auto rec : filtered_list ) { if( rec->difficulty == i ) { if( rec->can_make_with_inventory( crafting_inv, helpers ) ) { current.insert( current.begin(), rec ); available.insert( available.begin(), true ); truecount++; } else { current.push_back( rec ); available.push_back( false ); } } } } // This is so the list of available recipes is also is order of difficulty. std::reverse( current.begin(), current.begin() + truecount ); }
void construction_menu() { static bool hide_unconstructable = false; // only display constructions the player can theoretically perform std::vector<std::string> available; std::map<std::string, std::vector<std::string>> cat_available; load_available_constructions( available, cat_available, hide_unconstructable ); if( available.empty() ) { popup( _( "You can not construct anything here." ) ); return; } int w_height = TERMY; if( ( int )available.size() + 2 < w_height ) { w_height = available.size() + 2; } if( w_height < FULL_SCREEN_HEIGHT ) { w_height = FULL_SCREEN_HEIGHT; } const int w_width = std::max( FULL_SCREEN_WIDTH, TERMX * 2 / 3); const int w_y0 = ( TERMY > w_height ) ? ( TERMY - w_height ) / 2 : 0; const int w_x0 = ( TERMX > w_width ) ? ( TERMX - w_width ) / 2 : 0; WINDOW_PTR w_con_ptr {newwin( w_height, w_width, w_y0, w_x0 )}; WINDOW *const w_con = w_con_ptr.get(); const int w_list_width = int( .375 * w_width ); const int w_list_height = w_height - 4; const int w_list_x0 = 1; WINDOW_PTR w_list_ptr {newwin( w_list_height, w_list_width, w_y0 + 3, w_x0 + w_list_x0 )}; WINDOW *const w_list = w_list_ptr.get(); draw_grid( w_con, w_list_width + w_list_x0 ); //tabcount needs to be increased to add more categories int tabcount = 10; std::string construct_cat[] = {_( "All" ), _( "Constructions" ), _( "Furniture" ), _( "Digging and Mining" ), _( "Repairing" ), _( "Reinforcing" ), _( "Decorative" ), _( "Farming and Woodcutting" ), _( "Others" ), _( "Filter" ) }; bool update_info = true; bool update_cat = true; bool isnew = true; int tabindex = 0; int select = 0; int offset = 0; bool exit = false; std::string category_name = ""; std::vector<std::string> constructs; //storage for the color text so it can be scrolled std::vector< std::vector < std::string > > construct_buffers; std::vector<std::string> full_construct_buffer; std::vector<int> construct_buffer_breakpoints; int total_project_breakpoints = 0; int current_construct_breakpoint = 0; bool previous_hide_unconstructable = false; //track the cursor to determine when to refresh the list of construction recipes int previous_tabindex = -1; int previous_select = -1; const inventory &total_inv = g->u.crafting_inventory(); input_context ctxt( "CONSTRUCTION" ); ctxt.register_action( "UP", _( "Move cursor up" ) ); ctxt.register_action( "DOWN", _( "Move cursor down" ) ); ctxt.register_action( "RIGHT", _( "Move tab right" ) ); ctxt.register_action( "LEFT", _( "Move tab left" ) ); ctxt.register_action( "PAGE_UP" ); ctxt.register_action( "PAGE_DOWN" ); ctxt.register_action( "CONFIRM" ); ctxt.register_action( "TOGGLE_UNAVAILABLE_CONSTRUCTIONS" ); ctxt.register_action( "QUIT" ); ctxt.register_action( "HELP_KEYBINDINGS" ); ctxt.register_action( "FILTER" ); std::string filter; int previous_index = 0; do { if( update_cat ) { update_cat = false; switch( tabindex ) { case 0: category_name = "ALL"; break; case 1: category_name = "CONSTRUCT"; break; case 2: category_name = "FURN"; break; case 3: category_name = "DIG"; break; case 4: category_name = "REPAIR"; break; case 5: category_name = "REINFORCE"; break; case 6: category_name = "DECORATE"; break; case 7: category_name = "FARM_WOOD"; break; case 8: category_name = "OTHER"; break; case 9: category_name = "FILTER"; break; } if( category_name == "ALL" ) { constructs = available; previous_index = tabindex; } else if( category_name == "FILTER" ) { constructs.clear(); std::copy_if( available.begin(), available.end(), std::back_inserter( constructs ), [&](const std::string &a){ return lcmatch(a, filter); } ); } else { constructs = cat_available[category_name]; previous_index = tabindex; } if( isnew ){ if( !uistate.last_construction.empty() ){ select = std::distance(constructs.begin(), std::find( constructs.begin(), constructs.end(), uistate.last_construction )); } filter = uistate.construction_filter; } } // Erase existing tab selection & list of constructions mvwhline( w_con, 1, 1, ' ', w_list_width ); werase( w_list ); // Print new tab listing mvwprintz( w_con, 1, 1, c_yellow, "<< %s >>", construct_cat[tabindex].c_str() ); // Determine where in the master list to start printing calcStartPos( offset, select, w_list_height, constructs.size() ); // Print the constructions between offset and max (or how many will fit) for( size_t i = 0; ( int )i < w_list_height && ( i + offset ) < constructs.size(); i++ ) { int current = i + offset; std::string con_name = constructs[current]; bool highlight = ( current == select ); trim_and_print( w_list, i, 0, w_list_width, construction_color( con_name, highlight ), "%s", con_name.c_str() ); } if( update_info ) { update_info = false; // Clear out lines for tools & materials const int pos_x = w_list_width + w_list_x0 + 2; const int available_window_width = w_width - pos_x - 1; for( int i = 1; i < w_height - 1; i++ ) { mvwhline( w_con, i, pos_x, ' ', available_window_width ); } nc_color color_stage = c_white; std::vector<std::string> notes; notes.push_back( string_format( _( "Press %s or %s to tab." ), ctxt.get_desc( "LEFT" ).c_str(), ctxt.get_desc( "RIGHT" ).c_str() ) ); notes.push_back( string_format( _( "Press %s to search." ), ctxt.get_desc( "FILTER" ).c_str() ) ); notes.push_back( string_format( _( "Press %s to toggle unavailable constructions." ), ctxt.get_desc( "TOGGLE_UNAVAILABLE_CONSTRUCTIONS" ).c_str() ) ); notes.push_back( string_format( _( "Press %s to view and edit key-bindings." ), ctxt.get_desc( "HELP_KEYBINDINGS" ).c_str() ) ); //leave room for top and bottom UI text const int available_buffer_height = w_height - 3 - 3 - (int)notes.size(); // print the hotkeys regardless of if there are constructions for( size_t i = 0; i < notes.size(); ++i ) { trim_and_print( w_con, w_height - 1 - (int)notes.size() + (int)i, pos_x, available_window_width, c_white, "%s", notes[i].c_str() ); } if( !constructs.empty() ) { if( select >= (int) constructs.size() ){ select = 0; } std::string current_desc = constructs[select]; // Print construction name trim_and_print( w_con, 1, pos_x, available_window_width, c_white, "%s", current_desc.c_str() ); //only reconstruct the project list when moving away from the current item, or when changing the display mode if( previous_select != select || previous_tabindex != tabindex || previous_hide_unconstructable != hide_unconstructable ) { previous_select = select; previous_tabindex = tabindex; previous_hide_unconstructable = hide_unconstructable; //construct the project list buffer // Print stages and their requirement. std::vector<construction *> options = constructions_by_desc( current_desc ); construct_buffers.clear(); total_project_breakpoints = 0; current_construct_breakpoint = 0; construct_buffer_breakpoints.clear(); full_construct_buffer.clear(); int stage_counter = 0; for( std::vector<construction *>::iterator it = options.begin(); it != options.end(); ++it ) { stage_counter++; construction *current_con = *it; if( hide_unconstructable && !can_construct( *current_con ) ) { continue; } // Update the cached availability of components and tools in the requirement object current_con->requirements->can_make_with_inventory( total_inv ); std::vector<std::string> current_buffer; std::ostringstream current_line; // display result only if more than one step. // Assume single stage constructions should be clear // in their description what their result is. if( current_con->post_terrain != "" && options.size() > 1 ) { //also print out stage number when multiple stages are available current_line << _( "Stage #" ) << stage_counter; current_buffer.push_back( current_line.str() ); current_line.str( "" ); std::string result_string; if( current_con->post_is_furniture ) { result_string = furn_str_id( current_con->post_terrain ).obj().name; } else { result_string = ter_str_id( current_con->post_terrain ).obj().name; } current_line << "<color_" << string_from_color( color_stage ) << ">" << string_format( _( "Result: %s" ), result_string.c_str() ) << "</color>"; std::vector<std::string> folded_result_string = foldstring( current_line.str(), available_window_width ); current_buffer.insert( current_buffer.end(), folded_result_string.begin(), folded_result_string.end() ); } current_line.str( "" ); // display required skill and difficulty int pskill = g->u.get_skill_level( current_con->skill ); int diff = ( current_con->difficulty > 0 ) ? current_con->difficulty : 0; current_line << "<color_" << string_from_color( ( pskill >= diff ? c_white : c_red ) ) << ">" << string_format( _( "Skill Req: %d (%s)" ), diff, current_con->skill.obj().name().c_str() ) << "</color>"; current_buffer.push_back( current_line.str() ); // TODO: Textify pre_flags to provide a bit more information. // Example: First step of dig pit could say something about // requiring diggable ground. current_line.str( "" ); if( current_con->pre_terrain != "" ) { std::string require_string; if( current_con->pre_is_furniture ) { require_string = furn_str_id( current_con->pre_terrain ).obj().name; } else { require_string = ter_str_id( current_con->pre_terrain ).obj().name; } current_line << "<color_" << string_from_color( color_stage ) << ">" << string_format( _( "Requires: %s" ), require_string.c_str() ) << "</color>"; std::vector<std::string> folded_result_string = foldstring( current_line.str(), available_window_width ); current_buffer.insert( current_buffer.end(), folded_result_string.begin(), folded_result_string.end() ); } // get pre-folded versions of the rest of the construction project to be displayed later // get time needed std::vector<std::string> folded_time = current_con->get_folded_time_string( available_window_width ); current_buffer.insert( current_buffer.end(), folded_time.begin(), folded_time.end() ); std::vector<std::string> folded_tools = current_con->requirements->get_folded_tools_list( available_window_width, color_stage, total_inv ); current_buffer.insert( current_buffer.end(), folded_tools.begin(), folded_tools.end() ); std::vector<std::string> folded_components = current_con->requirements->get_folded_components_list( available_window_width, color_stage, total_inv ); current_buffer.insert( current_buffer.end(), folded_components.begin(), folded_components.end() ); construct_buffers.push_back( current_buffer ); } //determine where the printing starts for each project, so it can be scrolled to those points size_t current_buffer_location = 0; for( size_t i = 0; i < construct_buffers.size(); i++ ) { construct_buffer_breakpoints.push_back( static_cast<int>( current_buffer_location ) ); full_construct_buffer.insert( full_construct_buffer.end(), construct_buffers[i].begin(), construct_buffers[i].end() ); //handle text too large for one screen if( construct_buffers[i].size() > static_cast<size_t>( available_buffer_height ) ) { construct_buffer_breakpoints.push_back( static_cast<int>( current_buffer_location + static_cast<size_t>( available_buffer_height ) ) ); } current_buffer_location += construct_buffers[i].size(); if( i < construct_buffers.size() - 1 ) { full_construct_buffer.push_back( std::string( "" ) ); current_buffer_location++; } } total_project_breakpoints = static_cast<int>( construct_buffer_breakpoints.size() ); } if( current_construct_breakpoint > 0 ) { // Print previous stage indicator if breakpoint is past the beginning trim_and_print( w_con, 2, pos_x, available_window_width, c_white, _( "Press %s to show previous stage(s)." ), ctxt.get_desc( "PAGE_UP" ).c_str() ); } if( static_cast<size_t>( construct_buffer_breakpoints[current_construct_breakpoint] + available_buffer_height ) < full_construct_buffer.size() ) { // Print next stage indicator if more breakpoints are remaining after screen height trim_and_print( w_con, w_height - 2 - (int)notes.size(), pos_x, available_window_width, c_white, _( "Press %s to show next stage(s)." ), ctxt.get_desc( "PAGE_DOWN" ).c_str() ); } // Leave room for above/below indicators int ypos = 3; nc_color stored_color = color_stage; for( size_t i = static_cast<size_t>( construct_buffer_breakpoints[current_construct_breakpoint] ); i < full_construct_buffer.size(); i++ ) { //the value of 3 is from leaving room at the top of window if( ypos > available_buffer_height + 3 ) { break; } print_colored_text( w_con, ypos++, ( w_list_width + w_list_x0 + 2 ), stored_color, color_stage, full_construct_buffer[i] ); } } } // Finished updating draw_scrollbar( w_con, select, w_list_height, constructs.size(), 3 ); wrefresh( w_con ); wrefresh( w_list ); const std::string action = ctxt.handle_input(); if( action == "FILTER" ){ filter = string_input_popup( _( "Search" ), 50, filter, "", _( "Filter" ), 100, false ); if( !filter.empty() ){ update_info = true; update_cat = true; tabindex = 9; select = 0; }else if( previous_index !=9 ){ tabindex = previous_index; update_info = true; update_cat = true; select = 0; } uistate.construction_filter = filter; } else if( action == "DOWN" ) { update_info = true; if( select < ( int )constructs.size() - 1 ) { select++; } else { select = 0; } } else if( action == "UP" ) { update_info = true; if( select > 0 ) { select--; } else { select = constructs.size() - 1; } } else if( action == "LEFT" ) { update_info = true; update_cat = true; select = 0; tabindex--; if( tabindex < 0 ) { tabindex = tabcount - 1; } } else if( action == "RIGHT" ) { update_info = true; update_cat = true; select = 0; tabindex = ( tabindex + 1 ) % tabcount; } else if( action == "PAGE_UP" ) { update_info = true; if( current_construct_breakpoint > 0 ) { current_construct_breakpoint--; } if( current_construct_breakpoint < 0 ) { current_construct_breakpoint = 0; } } else if( action == "PAGE_DOWN" ) { update_info = true; if( current_construct_breakpoint < total_project_breakpoints - 1 ) { current_construct_breakpoint++; } if( current_construct_breakpoint >= total_project_breakpoints ) { current_construct_breakpoint = total_project_breakpoints - 1; } } else if( action == "QUIT" ) { exit = true; } else if( action == "HELP_KEYBINDINGS" ) { draw_grid( w_con, w_list_width + w_list_x0 ); } else if( action == "TOGGLE_UNAVAILABLE_CONSTRUCTIONS" ) { update_info = true; update_cat = true; hide_unconstructable = !hide_unconstructable; select = 0; offset = 0; load_available_constructions( available, cat_available, hide_unconstructable ); } else if( action == "CONFIRM" ) { if( constructs.empty() || select >= (int) constructs.size() ){ continue;// Nothing to be done here } if( player_can_build( g->u, total_inv, constructs[select] ) ) { place_construction( constructs[select] ); uistate.last_construction = constructs[select]; exit = true; } else { popup( _( "You can't build that!" ) ); draw_grid( w_con, w_list_width + w_list_x0 ); update_info = true; } } } while( !exit ); w_list_ptr.reset(); w_con_ptr.reset(); g->refresh_all(); }