Working group-shapeless and multigroup recipes

This commit is contained in:
Perttu Ahola 2012-07-26 13:49:13 +03:00
parent a26a66a8c4
commit 0c91a0d59d
2 changed files with 100 additions and 30 deletions

@ -423,7 +423,7 @@ effective towards.
Groups in crafting recipes Groups in crafting recipes
--------------------------- ---------------------------
An example: An example: Make meat soup from any meat, any water and any bowl
{ {
output = 'food:meat_soup_raw', output = 'food:meat_soup_raw',
recipe = { recipe = {
@ -431,7 +431,13 @@ An example:
{'group:water'}, {'group:water'},
{'group:bowl'}, {'group:bowl'},
}, },
preserve = {'group:bowl'}, -- Not implemented yet (TODO) -- preserve = {'group:bowl'}, -- Not implemented yet (TODO)
}
An another example: Make red wool from white wool and red dye
{
type = 'shapeless',
output = 'wool:red',
recipe = {'wool:white', 'group:dye,basecolor_red'},
} }
Special groups Special groups

@ -23,9 +23,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h" #include "log.h"
#include <sstream> #include <sstream>
#include <set> #include <set>
#include <algorithm>
#include "gamedef.h" #include "gamedef.h"
#include "inventory.h" #include "inventory.h"
#include "util/serialize.h" #include "util/serialize.h"
#include "strfnd.h"
// Check if input matches recipe // Check if input matches recipe
// Takes recipe groups into account // Takes recipe groups into account
@ -38,9 +40,17 @@ static bool inputItemMatchesRecipe(const std::string &inp_name,
// Group // Group
if(rec_name.substr(0,6) == "group:" && idef->isKnown(inp_name)){ if(rec_name.substr(0,6) == "group:" && idef->isKnown(inp_name)){
std::string rec_group = rec_name.substr(6);
const struct ItemDefinition &def = idef->get(inp_name); const struct ItemDefinition &def = idef->get(inp_name);
if(itemgroup_get(def.groups, rec_group) != 0) Strfnd f(rec_name.substr(6));
bool all_groups_match = true;
do{
std::string check_group = f.next(",");
if(itemgroup_get(def.groups, check_group) == 0){
all_groups_match = false;
break;
}
}while(!f.atend());
if(all_groups_match)
return true; return true;
} }
@ -140,6 +150,8 @@ static bool craftGetBounds(const std::vector<std::string> &items, unsigned int w
return success; return success;
} }
#if 0
// This became useless when group support was added to shapeless recipes
// Convert a list of item names to a multiset // Convert a list of item names to a multiset
static std::multiset<std::string> craftMakeMultiset(const std::vector<std::string> &names) static std::multiset<std::string> craftMakeMultiset(const std::vector<std::string> &names)
{ {
@ -153,6 +165,7 @@ static std::multiset<std::string> craftMakeMultiset(const std::vector<std::strin
} }
return set; return set;
} }
#endif
// Removes 1 from each item stack // Removes 1 from each item stack
static void craftDecrementInput(CraftInput &input, IGameDef *gamedef) static void craftDecrementInput(CraftInput &input, IGameDef *gamedef)
@ -508,17 +521,48 @@ bool CraftDefinitionShapeless::check(const CraftInput &input, IGameDef *gamedef)
{ {
if(input.method != CRAFT_METHOD_NORMAL) if(input.method != CRAFT_METHOD_NORMAL)
return false; return false;
// Filter empty items out of input
std::vector<std::string> input_filtered;
for(std::vector<ItemStack>::const_iterator
i = input.items.begin();
i != input.items.end(); i++)
{
if(i->name != "")
input_filtered.push_back(i->name);
}
// Get input item multiset // If there is a wrong number of items in input, no match
std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef); if(input_filtered.size() != recipe.size()){
std::multiset<std::string> inp_names_multiset = craftMakeMultiset(inp_names); /*dstream<<"Number of input items ("<<input_filtered.size()
<<") does not match recipe size ("<<recipe.size()<<") "
<<"of recipe with output="<<output<<std::endl;*/
return false;
}
// Get recipe item multiset // Try with all permutations of the recipe
std::vector<std::string> rec_names = craftGetItemNames(recipe, gamedef); std::vector<std::string> recipe_copy = recipe;
std::multiset<std::string> rec_names_multiset = craftMakeMultiset(rec_names); // Start from the lexicographically first permutation (=sorted)
std::sort(recipe_copy.begin(), recipe_copy.end());
//while(std::prev_permutation(recipe_copy.begin(), recipe_copy.end())){}
do{
// If all items match, the recipe matches
bool all_match = true;
//dstream<<"Testing recipe (output="<<output<<"):";
for(size_t i=0; i<recipe.size(); i++){
//dstream<<" ("<<input_filtered[i]<<" == "<<recipe_copy[i]<<")";
if(!inputItemMatchesRecipe(input_filtered[i], recipe_copy[i],
gamedef->idef())){
all_match = false;
break;
}
}
//dstream<<" -> match="<<all_match<<std::endl;
if(all_match)
return true;
}while(std::next_permutation(recipe_copy.begin(), recipe_copy.end()));
// Recipe is matched when the multisets coincide return false;
return inp_names_multiset == rec_names_multiset;
} }
CraftOutput CraftDefinitionShapeless::getOutput(const CraftInput &input, IGameDef *gamedef) const CraftOutput CraftDefinitionShapeless::getOutput(const CraftInput &input, IGameDef *gamedef) const
@ -694,16 +738,26 @@ bool CraftDefinitionCooking::check(const CraftInput &input, IGameDef *gamedef) c
if(input.method != CRAFT_METHOD_COOKING) if(input.method != CRAFT_METHOD_COOKING)
return false; return false;
// Get input item multiset // Filter empty items out of input
std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef); std::vector<std::string> input_filtered;
std::multiset<std::string> inp_names_multiset = craftMakeMultiset(inp_names); for(std::vector<ItemStack>::const_iterator
i = input.items.begin();
i != input.items.end(); i++)
{
if(i->name != "")
input_filtered.push_back(i->name);
}
// Get recipe item multiset // If there is a wrong number of items in input, no match
std::multiset<std::string> rec_names_multiset; if(input_filtered.size() != 1){
rec_names_multiset.insert(craftGetItemName(recipe, gamedef)); /*dstream<<"Number of input items ("<<input_filtered.size()
<<") does not match recipe size (1) "
// Recipe is matched when the multisets coincide <<"of cooking recipe with output="<<output<<std::endl;*/
return inp_names_multiset == rec_names_multiset; return false;
}
// Check the single input item
return inputItemMatchesRecipe(input_filtered[0], recipe, gamedef->idef());
} }
CraftOutput CraftDefinitionCooking::getOutput(const CraftInput &input, IGameDef *gamedef) const CraftOutput CraftDefinitionCooking::getOutput(const CraftInput &input, IGameDef *gamedef) const
@ -765,16 +819,26 @@ bool CraftDefinitionFuel::check(const CraftInput &input, IGameDef *gamedef) cons
if(input.method != CRAFT_METHOD_FUEL) if(input.method != CRAFT_METHOD_FUEL)
return false; return false;
// Get input item multiset // Filter empty items out of input
std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef); std::vector<std::string> input_filtered;
std::multiset<std::string> inp_names_multiset = craftMakeMultiset(inp_names); for(std::vector<ItemStack>::const_iterator
i = input.items.begin();
i != input.items.end(); i++)
{
if(i->name != "")
input_filtered.push_back(i->name);
}
// Get recipe item multiset // If there is a wrong number of items in input, no match
std::multiset<std::string> rec_names_multiset; if(input_filtered.size() != 1){
rec_names_multiset.insert(craftGetItemName(recipe, gamedef)); /*dstream<<"Number of input items ("<<input_filtered.size()
<<") does not match recipe size (1) "
// Recipe is matched when the multisets coincide <<"of fuel recipe with burntime="<<burntime<<std::endl;*/
return inp_names_multiset == rec_names_multiset; return false;
}
// Check the single input item
return inputItemMatchesRecipe(input_filtered[0], recipe, gamedef->idef());
} }
CraftOutput CraftDefinitionFuel::getOutput(const CraftInput &input, IGameDef *gamedef) const CraftOutput CraftDefinitionFuel::getOutput(const CraftInput &input, IGameDef *gamedef) const