Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-filter for recipes in crafting menu #27500

Merged
merged 10 commits into from Jan 12, 2019
127 changes: 73 additions & 54 deletions src/crafting_gui.cpp
Expand Up @@ -258,64 +258,82 @@ const recipe *select_crafting_recipe( int &batch_size )
std::vector<const recipe *> picking;
if( !filterstring.empty() ) {
auto qry = trim( filterstring );
if( qry.size() > 2 && qry[1] == ':' ) {
switch( qry[0] ) {
case 't':
picking = available_recipes.search( qry.substr( 2 ), recipe_subset::search_type::tool );
break;

case 'c':
picking = available_recipes.search( qry.substr( 2 ), recipe_subset::search_type::component );
break;

case 's':
picking = available_recipes.search( qry.substr( 2 ), recipe_subset::search_type::skill );
break;

case 'p':
picking = available_recipes.search( qry.substr( 2 ), recipe_subset::search_type::primary_skill );
break;

case 'Q':
picking = available_recipes.search( qry.substr( 2 ), recipe_subset::search_type::quality );
break;

case 'q':
picking = available_recipes.search( qry.substr( 2 ), recipe_subset::search_type::quality_result );
break;

case 'd':
picking = available_recipes.search( qry.substr( 2 ),
recipe_subset::search_type::description_result );
break;

case 'm': {
auto &learned = g->u.get_learned_recipes();
if( query_is_yes( qry ) ) {
std::set_intersection( available_recipes.begin(), available_recipes.end(), learned.begin(),
learned.end(), std::back_inserter( picking ) );
} else {
std::set_difference( available_recipes.begin(), available_recipes.end(), learned.begin(),
learned.end(),
std::back_inserter( picking ) );
size_t qry_begin = 0;
size_t qry_end = 0;
recipe_subset filtered_recipes = available_recipes;
do {
// Find next ','
qry_end = qry.find_first_of( ',', qry_begin );

auto qry_filter_str = trim( qry.substr( qry_begin, qry_end - qry_begin ) );
// Process filter
if( qry_filter_str.size() > 2 && qry_filter_str[1] == ':' ) {
switch( qry_filter_str[0] ) {
case 't':
filtered_recipes = filtered_recipes.reduce( qry_filter_str.substr( 2 ),
recipe_subset::search_type::tool );
break;

case 'c':
filtered_recipes = filtered_recipes.reduce( qry_filter_str.substr( 2 ),
recipe_subset::search_type::component );
break;

case 's':
filtered_recipes = filtered_recipes.reduce( qry_filter_str.substr( 2 ),
recipe_subset::search_type::skill );
break;

case 'p':
filtered_recipes = filtered_recipes.reduce( qry_filter_str.substr( 2 ),
recipe_subset::search_type::primary_skill );
break;

case 'Q':
filtered_recipes = filtered_recipes.reduce( qry_filter_str.substr( 2 ),
recipe_subset::search_type::quality );
break;

case 'q':
filtered_recipes = filtered_recipes.reduce( qry_filter_str.substr( 2 ),
recipe_subset::search_type::quality_result );
break;

case 'd':
filtered_recipes = filtered_recipes.reduce( qry_filter_str.substr( 2 ),
recipe_subset::search_type::description_result );
break;

case 'm': {
auto &learned = g->u.get_learned_recipes();
recipe_subset temp_subset;
if( query_is_yes( qry_filter_str ) ) {
temp_subset = available_recipes.intersection( learned );
} else {
temp_subset = available_recipes.difference( learned );
}
filtered_recipes = filtered_recipes.intersection( temp_subset );
break;
}
break;
}

case 'h': {
std::copy( available_recipes.begin(), available_recipes.end(), std::back_inserter( picking ) );
if( query_is_yes( qry ) ) {
show_hidden = true;
case 'h': {
filtered_recipes = filtered_recipes.intersection( available_recipes );
if( query_is_yes( qry_filter_str ) ) {
show_hidden = true;
}
break;
}
break;
}

default:
current.clear();
default:
current.clear();
}
} else {
filtered_recipes = filtered_recipes.reduce( qry_filter_str );
}
} else {
picking = available_recipes.search( qry );
}

qry_begin = qry_end + 1;
} while( qry_end != std::string::npos );
picking.insert( picking.end(), filtered_recipes.begin(), filtered_recipes.end() );
} else if( subtab.cur() == "CSC_*_FAVORITE" ) {
picking = available_recipes.favorite();
} else if( subtab.cur() == "CSC_*_RECENT" ) {
Expand Down Expand Up @@ -703,7 +721,8 @@ const recipe *select_crafting_recipe( int &batch_size )

std::string description =
_( "The default is to search result names. Some single-character prefixes "
"can be used with a colon (:) to search in other ways.\n"
"can be used with a colon (:) to search in other ways. Additional filters "
"are separated by commas (,).\n"
"\n"
"<color_white>Examples:</color>\n" );

Expand Down
27 changes: 26 additions & 1 deletion src/recipe_dictionary.cpp
Expand Up @@ -120,7 +120,6 @@ std::vector<const recipe *> recipe_subset::recent() const

return res;
}

std::vector<const recipe *> recipe_subset::search( const std::string &txt,
const search_type key ) const
{
Expand Down Expand Up @@ -170,6 +169,32 @@ std::vector<const recipe *> recipe_subset::search( const std::string &txt,
return res;
}

recipe_subset::recipe_subset( const recipe_subset &src, const std::vector<const recipe *> &recipes )
{
for( const auto elem : recipes ) {
include( elem, src.get_custom_difficulty( elem ) );
}
}

recipe_subset recipe_subset::reduce( const std::string &txt, const search_type key ) const
{
return recipe_subset( *this, search( txt, key ) );
}
recipe_subset recipe_subset::intersection( const recipe_subset &subset ) const
{
std::vector<const recipe *> intersection_result;
std::set_intersection( this->begin(), this->end(), subset.begin(), subset.end(),
std::back_inserter( intersection_result ) );
return recipe_subset( *this, intersection_result );
}
recipe_subset recipe_subset::difference( const recipe_subset &subset ) const
{
std::vector<const recipe *> difference_result;
std::set_difference( this->begin(), this->end(), subset.begin(), subset.end(),
std::back_inserter( difference_result ) );
return recipe_subset( *this, difference_result );
}

std::vector<const recipe *> recipe_subset::search_result( const itype_id &item ) const
{
std::vector<const recipe *> res;
Expand Down
8 changes: 8 additions & 0 deletions src/recipe_dictionary.h
Expand Up @@ -66,6 +66,8 @@ extern recipe_dictionary recipe_dict;
class recipe_subset
{
public:
recipe_subset( void ) = default;
recipe_subset( const recipe_subset &src, const std::vector<const recipe *> &recipes );
/**
* Include a recipe to the subset.
* @param r recipe to include
Expand Down Expand Up @@ -133,6 +135,12 @@ class recipe_subset
/** Find recipes matching query (left anchored partial matches are supported) */
std::vector<const recipe *> search( const std::string &txt,
const search_type key = search_type::name ) const;
/** Find recipes matching query and return a new recipe_subset */
recipe_subset reduce( const std::string &txt, const search_type key = search_type::name ) const;
/** Set intersection between recipe_subsets */
recipe_subset intersection( const recipe_subset &subset ) const;
/** Set difference between recipe_subsets */
recipe_subset difference( const recipe_subset &subset ) const;
/** Find recipes producing the item */
std::vector<const recipe *> search_result( const itype_id &item ) const;

Expand Down