LCOV - code coverage report
Current view: top level - src - craftdef.cpp (source / functions) Hit Total Coverage
Test: report Lines: 1 542 0.2 %
Date: 2015-07-11 18:23:49 Functions: 2 71 2.8 %

          Line data    Source code
       1             : /*
       2             : Minetest
       3             : Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
       4             : 
       5             : This program is free software; you can redistribute it and/or modify
       6             : it under the terms of the GNU Lesser General Public License as published by
       7             : the Free Software Foundation; either version 2.1 of the License, or
       8             : (at your option) any later version.
       9             : 
      10             : This program is distributed in the hope that it will be useful,
      11             : but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             : GNU Lesser General Public License for more details.
      14             : 
      15             : You should have received a copy of the GNU Lesser General Public License along
      16             : with this program; if not, write to the Free Software Foundation, Inc.,
      17             : 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      18             : */
      19             : 
      20             : #include "craftdef.h"
      21             : 
      22             : #include "irrlichttypes.h"
      23             : #include "log.h"
      24             : #include <sstream>
      25             : #include <set>
      26             : #include <algorithm>
      27             : #include "gamedef.h"
      28             : #include "inventory.h"
      29             : #include "util/serialize.h"
      30             : #include "util/string.h"
      31             : #include "util/numeric.h"
      32             : #include "strfnd.h"
      33             : #include "exceptions.h"
      34             : 
      35           0 : inline bool isGroupRecipeStr(const std::string &rec_name)
      36             : {
      37           0 :         return str_starts_with(rec_name, std::string("group:"));
      38             : }
      39             : 
      40           0 : inline u64 getHashForString(const std::string &recipe_str)
      41             : {
      42             :         /*errorstream << "Hashing craft string  \"" << recipe_str << '"';*/
      43           0 :         return murmur_hash_64_ua(recipe_str.data(), recipe_str.length(), 0xdeadbeef);
      44             : }
      45             : 
      46           0 : static u64 getHashForGrid(CraftHashType type, const std::vector<std::string> &grid_names)
      47             : {
      48           0 :         switch (type) {
      49             :                 case CRAFT_HASH_TYPE_ITEM_NAMES: {
      50           0 :                         std::ostringstream os;
      51           0 :                         bool is_first = true;
      52           0 :                         for (size_t i = 0; i < grid_names.size(); i++) {
      53           0 :                                 if (grid_names[i] != "") {
      54           0 :                                         os << (is_first ? "" : "\n") << grid_names[i];
      55           0 :                                         is_first = false;
      56             :                                 }
      57             :                         }
      58           0 :                         return getHashForString(os.str());
      59             :                 } case CRAFT_HASH_TYPE_COUNT: {
      60           0 :                         u64 cnt = 0;
      61           0 :                         for (size_t i = 0; i < grid_names.size(); i++)
      62           0 :                                 if (grid_names[i] != "")
      63           0 :                                         cnt++;
      64           0 :                         return cnt;
      65             :                 } case CRAFT_HASH_TYPE_UNHASHED:
      66           0 :                         return 0;
      67             :         }
      68             :         // invalid CraftHashType
      69             :         assert(false);
      70           0 :         return 0;
      71             : }
      72             : 
      73             : // Check if input matches recipe
      74             : // Takes recipe groups into account
      75           0 : static bool inputItemMatchesRecipe(const std::string &inp_name,
      76             :                 const std::string &rec_name, IItemDefManager *idef)
      77             : {
      78             :         // Exact name
      79           0 :         if (inp_name == rec_name)
      80           0 :                 return true;
      81             : 
      82             :         // Group
      83           0 :         if (isGroupRecipeStr(rec_name) && idef->isKnown(inp_name)) {
      84           0 :                 const struct ItemDefinition &def = idef->get(inp_name);
      85           0 :                 Strfnd f(rec_name.substr(6));
      86           0 :                 bool all_groups_match = true;
      87           0 :                 do {
      88           0 :                         std::string check_group = f.next(",");
      89           0 :                         if (itemgroup_get(def.groups, check_group) == 0) {
      90           0 :                                 all_groups_match = false;
      91           0 :                                 break;
      92             :                         }
      93           0 :                 } while (!f.atend());
      94           0 :                 if (all_groups_match)
      95           0 :                         return true;
      96             :         }
      97             : 
      98             :         // Didn't match
      99           0 :         return false;
     100             : }
     101             : 
     102             : // Deserialize an itemstring then return the name of the item
     103           0 : static std::string craftGetItemName(const std::string &itemstring, IGameDef *gamedef)
     104             : {
     105           0 :         ItemStack item;
     106           0 :         item.deSerialize(itemstring, gamedef->idef());
     107           0 :         return item.name;
     108             : }
     109             : 
     110             : // (mapcar craftGetItemName itemstrings)
     111           0 : static std::vector<std::string> craftGetItemNames(
     112             :                 const std::vector<std::string> &itemstrings, IGameDef *gamedef)
     113             : {
     114           0 :         std::vector<std::string> result;
     115           0 :         for (std::vector<std::string>::const_iterator
     116           0 :                         it = itemstrings.begin();
     117           0 :                         it != itemstrings.end(); it++) {
     118           0 :                 result.push_back(craftGetItemName(*it, gamedef));
     119             :         }
     120           0 :         return result;
     121             : }
     122             : 
     123             : // Get name of each item, and return them as a new list.
     124           0 : static std::vector<std::string> craftGetItemNames(
     125             :                 const std::vector<ItemStack> &items, IGameDef *gamedef)
     126             : {
     127           0 :         std::vector<std::string> result;
     128           0 :         for (std::vector<ItemStack>::const_iterator
     129           0 :                         it = items.begin();
     130           0 :                         it != items.end(); it++) {
     131           0 :                 result.push_back(it->name);
     132             :         }
     133           0 :         return result;
     134             : }
     135             : 
     136             : // convert a list of item names, to ItemStacks.
     137           0 : static std::vector<ItemStack> craftGetItems(
     138             :                 const std::vector<std::string> &items, IGameDef *gamedef)
     139             : {
     140           0 :         std::vector<ItemStack> result;
     141           0 :         for (std::vector<std::string>::const_iterator
     142           0 :                         it = items.begin();
     143           0 :                         it != items.end(); it++) {
     144           0 :                 result.push_back(ItemStack(std::string(*it), (u16)1,
     145           0 :                         (u16)0, "", gamedef->getItemDefManager()));
     146             :         }
     147           0 :         return result;
     148             : }
     149             : 
     150             : // Compute bounding rectangle given a matrix of items
     151             : // Returns false if every item is ""
     152           0 : static bool craftGetBounds(const std::vector<std::string> &items, unsigned int width,
     153             :                 unsigned int &min_x, unsigned int &max_x,
     154             :                 unsigned int &min_y, unsigned int &max_y)
     155             : {
     156           0 :         bool success = false;
     157           0 :         unsigned int x = 0;
     158           0 :         unsigned int y = 0;
     159           0 :         for (std::vector<std::string>::const_iterator
     160           0 :                         it = items.begin();
     161           0 :                         it != items.end(); it++) {
     162             :                 // Is this an actual item?
     163           0 :                 if (*it != "") {
     164           0 :                         if (!success) {
     165             :                                 // This is the first nonempty item
     166           0 :                                 min_x = max_x = x;
     167           0 :                                 min_y = max_y = y;
     168           0 :                                 success = true;
     169             :                         } else {
     170           0 :                                 if (x < min_x) min_x = x;
     171           0 :                                 if (x > max_x) max_x = x;
     172           0 :                                 if (y < min_y) min_y = y;
     173           0 :                                 if (y > max_y) max_y = y;
     174             :                         }
     175             :                 }
     176             : 
     177             :                 // Step coordinate
     178           0 :                 x++;
     179           0 :                 if (x == width) {
     180           0 :                         x = 0;
     181           0 :                         y++;
     182             :                 }
     183             :         }
     184           0 :         return success;
     185             : }
     186             : 
     187             : // Removes 1 from each item stack
     188           0 : static void craftDecrementInput(CraftInput &input, IGameDef *gamedef)
     189             : {
     190           0 :         for (std::vector<ItemStack>::iterator
     191           0 :                         it = input.items.begin();
     192           0 :                         it != input.items.end(); it++) {
     193           0 :                 if (it->count != 0)
     194           0 :                         it->remove(1);
     195             :         }
     196           0 : }
     197             : 
     198             : // Removes 1 from each item stack with replacement support
     199             : // Example: if replacements contains the pair ("bucket:bucket_water", "bucket:bucket_empty"),
     200             : //   a water bucket will not be removed but replaced by an empty bucket.
     201           0 : static void craftDecrementOrReplaceInput(CraftInput &input,
     202             :                 std::vector<ItemStack> &output_replacements,
     203             :                 const CraftReplacements &replacements,
     204             :                 IGameDef *gamedef)
     205             : {
     206           0 :         if (replacements.pairs.empty()) {
     207           0 :                 craftDecrementInput(input, gamedef);
     208           0 :                 return;
     209             :         }
     210             : 
     211             :         // Make a copy of the replacements pair list
     212           0 :         std::vector<std::pair<std::string, std::string> > pairs = replacements.pairs;
     213             : 
     214           0 :         for (std::vector<ItemStack>::iterator
     215           0 :                         it = input.items.begin();
     216           0 :                         it != input.items.end(); it++) {
     217             :                 // Find an appropriate replacement
     218           0 :                 bool found_replacement = false;
     219           0 :                 for (std::vector<std::pair<std::string, std::string> >::iterator
     220           0 :                                 j = pairs.begin();
     221           0 :                                 j != pairs.end(); j++) {
     222           0 :                         if (it->name == craftGetItemName(j->first, gamedef)) {
     223           0 :                                 if (it->count == 1) {
     224           0 :                                         it->deSerialize(j->second, gamedef->idef());
     225           0 :                                         found_replacement = true;
     226           0 :                                         pairs.erase(j);
     227           0 :                                         break;
     228             :                                 } else {
     229           0 :                                         ItemStack rep;
     230           0 :                                         rep.deSerialize(j->second, gamedef->idef());
     231           0 :                                         it->remove(1);
     232           0 :                                         found_replacement = true;
     233           0 :                                         output_replacements.push_back(rep);
     234           0 :                                         break;
     235             :                                 }
     236             :                         }
     237             :                 }
     238             :                 // No replacement was found, simply decrement count to zero
     239           0 :                 if (!found_replacement)
     240           0 :                         it->remove(1);
     241             :         }
     242             : }
     243             : 
     244             : // Dump an itemstring matrix
     245           0 : static std::string craftDumpMatrix(const std::vector<std::string> &items,
     246             :                 unsigned int width)
     247             : {
     248           0 :         std::ostringstream os(std::ios::binary);
     249           0 :         os<<"{ ";
     250           0 :         unsigned int x = 0;
     251           0 :         for(std::vector<std::string>::const_iterator
     252           0 :                         it = items.begin();
     253           0 :                         it != items.end(); it++, x++) {
     254           0 :                 if (x == width) {
     255           0 :                         os<<"; ";
     256           0 :                         x = 0;
     257           0 :                 } else if (x != 0) {
     258           0 :                         os<<",";
     259             :                 }
     260           0 :                 os << '"' << (*it) << '"';
     261             :         }
     262           0 :         os << " }";
     263           0 :         return os.str();
     264             : }
     265             : 
     266             : // Dump an item matrix
     267           0 : std::string craftDumpMatrix(const std::vector<ItemStack> &items,
     268             :                 unsigned int width)
     269             : {
     270           0 :         std::ostringstream os(std::ios::binary);
     271           0 :         os << "{ ";
     272           0 :         unsigned int x = 0;
     273           0 :         for (std::vector<ItemStack>::const_iterator
     274           0 :                         it = items.begin();
     275           0 :                         it != items.end(); it++, x++) {
     276           0 :                 if (x == width) {
     277           0 :                         os << "; ";
     278           0 :                         x = 0;
     279           0 :                 } else if (x != 0) {
     280           0 :                         os<<",";
     281             :                 }
     282           0 :                 os << '"' << (it->getItemString()) << '"';
     283             :         }
     284           0 :         os << " }";
     285           0 :         return os.str();
     286             : }
     287             : 
     288             : 
     289             : /*
     290             :         CraftInput
     291             : */
     292             : 
     293           0 : std::string CraftInput::dump() const
     294             : {
     295           0 :         std::ostringstream os(std::ios::binary);
     296           0 :         os << "(method=" << ((int)method) << ", items="
     297           0 :                 << craftDumpMatrix(items, width) << ")";
     298           0 :         return os.str();
     299             : }
     300             : 
     301             : /*
     302             :         CraftOutput
     303             : */
     304             : 
     305           0 : std::string CraftOutput::dump() const
     306             : {
     307           0 :         std::ostringstream os(std::ios::binary);
     308           0 :         os << "(item=\"" << item << "\", time=" << time << ")";
     309           0 :         return os.str();
     310             : }
     311             : 
     312             : /*
     313             :         CraftReplacements
     314             : */
     315             : 
     316           0 : std::string CraftReplacements::dump() const
     317             : {
     318           0 :         std::ostringstream os(std::ios::binary);
     319           0 :         os<<"{";
     320           0 :         const char *sep = "";
     321           0 :         for (std::vector<std::pair<std::string, std::string> >::const_iterator
     322           0 :                         it = pairs.begin();
     323           0 :                         it != pairs.end(); it++) {
     324           0 :                 os << sep << '"' << (it->first) << "\"=>\"" << (it->second) << '"';
     325           0 :                 sep = ",";
     326             :         }
     327           0 :         os << "}";
     328           0 :         return os.str();
     329             : }
     330             : 
     331             : /*
     332             :         CraftDefinitionShaped
     333             : */
     334             : 
     335           0 : std::string CraftDefinitionShaped::getName() const
     336             : {
     337           0 :         return "shaped";
     338             : }
     339             : 
     340           0 : bool CraftDefinitionShaped::check(const CraftInput &input, IGameDef *gamedef) const
     341             : {
     342           0 :         if (input.method != CRAFT_METHOD_NORMAL)
     343           0 :                 return false;
     344             : 
     345             :         // Get input item matrix
     346           0 :         std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
     347           0 :         unsigned int inp_width = input.width;
     348           0 :         if (inp_width == 0)
     349           0 :                 return false;
     350           0 :         while (inp_names.size() % inp_width != 0)
     351           0 :                 inp_names.push_back("");
     352             : 
     353             :         // Get input bounds
     354           0 :         unsigned int inp_min_x = 0, inp_max_x = 0, inp_min_y = 0, inp_max_y = 0;
     355           0 :         if (!craftGetBounds(inp_names, inp_width, inp_min_x, inp_max_x,
     356             :                         inp_min_y, inp_max_y))
     357           0 :                 return false;  // it was empty
     358             : 
     359           0 :         std::vector<std::string> rec_names;
     360           0 :         if (hash_inited)
     361           0 :                 rec_names = recipe_names;
     362             :         else
     363           0 :                 rec_names = craftGetItemNames(recipe, gamedef);
     364             : 
     365             :         // Get recipe item matrix
     366           0 :         unsigned int rec_width = width;
     367           0 :         if (rec_width == 0)
     368           0 :                 return false;
     369           0 :         while (rec_names.size() % rec_width != 0)
     370           0 :                 rec_names.push_back("");
     371             : 
     372             :         // Get recipe bounds
     373           0 :         unsigned int rec_min_x=0, rec_max_x=0, rec_min_y=0, rec_max_y=0;
     374           0 :         if (!craftGetBounds(rec_names, rec_width, rec_min_x, rec_max_x,
     375             :                         rec_min_y, rec_max_y))
     376           0 :                 return false;  // it was empty
     377             : 
     378             :         // Different sizes?
     379           0 :         if (inp_max_x - inp_min_x != rec_max_x - rec_min_x ||
     380           0 :                         inp_max_y - inp_min_y != rec_max_y - rec_min_y)
     381           0 :                 return false;
     382             : 
     383             :         // Verify that all item names in the bounding box are equal
     384           0 :         unsigned int w = inp_max_x - inp_min_x + 1;
     385           0 :         unsigned int h = inp_max_y - inp_min_y + 1;
     386             : 
     387           0 :         for (unsigned int y=0; y < h; y++) {
     388           0 :                 unsigned int inp_y = (inp_min_y + y) * inp_width;
     389           0 :                 unsigned int rec_y = (rec_min_y + y) * rec_width;
     390             : 
     391           0 :                 for (unsigned int x=0; x < w; x++) {
     392           0 :                         unsigned int inp_x = inp_min_x + x;
     393           0 :                         unsigned int rec_x = rec_min_x + x;
     394             : 
     395           0 :                         if (!inputItemMatchesRecipe(
     396           0 :                                         inp_names[inp_y + inp_x],
     397           0 :                                         rec_names[rec_y + rec_x], gamedef->idef())) {
     398           0 :                                 return false;
     399             :                         }
     400             :                 }
     401             :         }
     402             : 
     403           0 :         return true;
     404             : }
     405             : 
     406           0 : CraftOutput CraftDefinitionShaped::getOutput(const CraftInput &input, IGameDef *gamedef) const
     407             : {
     408           0 :         return CraftOutput(output, 0);
     409             : }
     410             : 
     411           0 : CraftInput CraftDefinitionShaped::getInput(const CraftOutput &output, IGameDef *gamedef) const
     412             : {
     413           0 :         return CraftInput(CRAFT_METHOD_NORMAL,width,craftGetItems(recipe,gamedef));
     414             : }
     415             : 
     416           0 : void CraftDefinitionShaped::decrementInput(CraftInput &input, std::vector<ItemStack> &output_replacements,
     417             :          IGameDef *gamedef) const
     418             : {
     419           0 :         craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef);
     420           0 : }
     421             : 
     422           0 : CraftHashType CraftDefinitionShaped::getHashType() const
     423             : {
     424             :         assert(hash_inited); // Pre-condition
     425           0 :         bool has_group = false;
     426           0 :         for (size_t i = 0; i < recipe_names.size(); i++) {
     427           0 :                 if (isGroupRecipeStr(recipe_names[i])) {
     428           0 :                         has_group = true;
     429           0 :                         break;
     430             :                 }
     431             :         }
     432           0 :         if (has_group)
     433           0 :                 return CRAFT_HASH_TYPE_COUNT;
     434             :         else
     435           0 :                 return CRAFT_HASH_TYPE_ITEM_NAMES;
     436             : }
     437             : 
     438           0 : u64 CraftDefinitionShaped::getHash(CraftHashType type) const
     439             : {
     440             :         assert(hash_inited); // Pre-condition
     441             :         assert((type == CRAFT_HASH_TYPE_ITEM_NAMES)
     442             :                 || (type == CRAFT_HASH_TYPE_COUNT)); // Pre-condition
     443             : 
     444           0 :         std::vector<std::string> rec_names = recipe_names;
     445           0 :         std::sort(rec_names.begin(), rec_names.end());
     446           0 :         return getHashForGrid(type, rec_names);
     447             : }
     448             : 
     449           0 : void CraftDefinitionShaped::initHash(IGameDef *gamedef)
     450             : {
     451           0 :         if (hash_inited)
     452           0 :                 return;
     453           0 :         hash_inited = true;
     454           0 :         recipe_names = craftGetItemNames(recipe, gamedef);
     455             : }
     456             : 
     457           0 : std::string CraftDefinitionShaped::dump() const
     458             : {
     459           0 :         std::ostringstream os(std::ios::binary);
     460           0 :         os << "(shaped, output=\"" << output
     461           0 :                 << "\", recipe=" << craftDumpMatrix(recipe, width)
     462           0 :                 << ", replacements=" << replacements.dump() << ")";
     463           0 :         return os.str();
     464             : }
     465             : 
     466             : /*
     467             :         CraftDefinitionShapeless
     468             : */
     469             : 
     470           0 : std::string CraftDefinitionShapeless::getName() const
     471             : {
     472           0 :         return "shapeless";
     473             : }
     474             : 
     475           0 : bool CraftDefinitionShapeless::check(const CraftInput &input, IGameDef *gamedef) const
     476             : {
     477           0 :         if (input.method != CRAFT_METHOD_NORMAL)
     478           0 :                 return false;
     479             : 
     480             :         // Filter empty items out of input
     481           0 :         std::vector<std::string> input_filtered;
     482           0 :         for (std::vector<ItemStack>::const_iterator
     483           0 :                         it = input.items.begin();
     484           0 :                         it != input.items.end(); it++) {
     485           0 :                 if (it->name != "")
     486           0 :                         input_filtered.push_back(it->name);
     487             :         }
     488             : 
     489             :         // If there is a wrong number of items in input, no match
     490           0 :         if (input_filtered.size() != recipe.size()) {
     491             :                 /*dstream<<"Number of input items ("<<input_filtered.size()
     492             :                                 <<") does not match recipe size ("<<recipe.size()<<") "
     493             :                                 <<"of recipe with output="<<output<<std::endl;*/
     494           0 :                 return false;
     495             :         }
     496             : 
     497           0 :         std::vector<std::string> recipe_copy;
     498           0 :         if (hash_inited)
     499           0 :                 recipe_copy = recipe_names;
     500             :         else {
     501           0 :                 recipe_copy = craftGetItemNames(recipe, gamedef);
     502           0 :                 std::sort(recipe_copy.begin(), recipe_copy.end());
     503             :         }
     504             : 
     505             :         // Try with all permutations of the recipe,
     506             :         // start from the lexicographically first permutation (=sorted),
     507             :         // recipe_names is pre-sorted
     508           0 :         do {
     509             :                 // If all items match, the recipe matches
     510           0 :                 bool all_match = true;
     511             :                 //dstream<<"Testing recipe (output="<<output<<"):";
     512           0 :                 for (size_t i=0; i<recipe.size(); i++) {
     513             :                         //dstream<<" ("<<input_filtered[i]<<" == "<<recipe_copy[i]<<")";
     514           0 :                         if (!inputItemMatchesRecipe(input_filtered[i], recipe_copy[i],
     515             :                                         gamedef->idef())) {
     516           0 :                                 all_match = false;
     517           0 :                                 break;
     518             :                         }
     519             :                 }
     520             :                 //dstream<<" -> match="<<all_match<<std::endl;
     521           0 :                 if (all_match)
     522           0 :                         return true;
     523           0 :         } while (std::next_permutation(recipe_copy.begin(), recipe_copy.end()));
     524             : 
     525           0 :         return false;
     526             : }
     527             : 
     528           0 : CraftOutput CraftDefinitionShapeless::getOutput(const CraftInput &input, IGameDef *gamedef) const
     529             : {
     530           0 :         return CraftOutput(output, 0);
     531             : }
     532             : 
     533           0 : CraftInput CraftDefinitionShapeless::getInput(const CraftOutput &output, IGameDef *gamedef) const
     534             : {
     535           0 :         return CraftInput(CRAFT_METHOD_NORMAL, 0, craftGetItems(recipe, gamedef));
     536             : }
     537             : 
     538           0 : void CraftDefinitionShapeless::decrementInput(CraftInput &input, std::vector<ItemStack> &output_replacements,
     539             :         IGameDef *gamedef) const
     540             : {
     541           0 :         craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef);
     542           0 : }
     543             : 
     544           0 : CraftHashType CraftDefinitionShapeless::getHashType() const
     545             : {
     546             :         assert(hash_inited); // Pre-condition
     547           0 :         bool has_group = false;
     548           0 :         for (size_t i = 0; i < recipe_names.size(); i++) {
     549           0 :                 if (isGroupRecipeStr(recipe_names[i])) {
     550           0 :                         has_group = true;
     551           0 :                         break;
     552             :                 }
     553             :         }
     554           0 :         if (has_group)
     555           0 :                 return CRAFT_HASH_TYPE_COUNT;
     556             :         else
     557           0 :                 return CRAFT_HASH_TYPE_ITEM_NAMES;
     558             : }
     559             : 
     560           0 : u64 CraftDefinitionShapeless::getHash(CraftHashType type) const
     561             : {
     562             :         assert(hash_inited); // Pre-condition
     563             :         assert(type == CRAFT_HASH_TYPE_ITEM_NAMES
     564             :                 || type == CRAFT_HASH_TYPE_COUNT); // Pre-condition
     565           0 :         return getHashForGrid(type, recipe_names);
     566             : }
     567             : 
     568           0 : void CraftDefinitionShapeless::initHash(IGameDef *gamedef)
     569             : {
     570           0 :         if (hash_inited)
     571           0 :                 return;
     572           0 :         hash_inited = true;
     573           0 :         recipe_names = craftGetItemNames(recipe, gamedef);
     574           0 :         std::sort(recipe_names.begin(), recipe_names.end());
     575             : }
     576             : 
     577           0 : std::string CraftDefinitionShapeless::dump() const
     578             : {
     579           0 :         std::ostringstream os(std::ios::binary);
     580           0 :         os << "(shapeless, output=\"" << output
     581           0 :                 << "\", recipe=" << craftDumpMatrix(recipe, recipe.size())
     582           0 :                 << ", replacements=" << replacements.dump() << ")";
     583           0 :         return os.str();
     584             : }
     585             : 
     586             : /*
     587             :         CraftDefinitionToolRepair
     588             : */
     589             : 
     590           0 : static ItemStack craftToolRepair(
     591             :                 const ItemStack &item1,
     592             :                 const ItemStack &item2,
     593             :                 float additional_wear,
     594             :                 IGameDef *gamedef)
     595             : {
     596           0 :         IItemDefManager *idef = gamedef->idef();
     597           0 :         if (item1.count != 1 || item2.count != 1 || item1.name != item2.name
     598           0 :                         || idef->get(item1.name).type != ITEM_TOOL
     599           0 :                         || idef->get(item2.name).type != ITEM_TOOL) {
     600             :                 // Failure
     601           0 :                 return ItemStack();
     602             :         }
     603             : 
     604           0 :         s32 item1_uses = 65536 - (u32) item1.wear;
     605           0 :         s32 item2_uses = 65536 - (u32) item2.wear;
     606           0 :         s32 new_uses = item1_uses + item2_uses;
     607           0 :         s32 new_wear = 65536 - new_uses + floor(additional_wear * 65536 + 0.5);
     608           0 :         if (new_wear >= 65536)
     609           0 :                 return ItemStack();
     610           0 :         if (new_wear < 0)
     611           0 :                 new_wear = 0;
     612             : 
     613           0 :         ItemStack repaired = item1;
     614           0 :         repaired.wear = new_wear;
     615           0 :         return repaired;
     616             : }
     617             : 
     618           0 : std::string CraftDefinitionToolRepair::getName() const
     619             : {
     620           0 :         return "toolrepair";
     621             : }
     622             : 
     623           0 : bool CraftDefinitionToolRepair::check(const CraftInput &input, IGameDef *gamedef) const
     624             : {
     625           0 :         if (input.method != CRAFT_METHOD_NORMAL)
     626           0 :                 return false;
     627             : 
     628           0 :         ItemStack item1;
     629           0 :         ItemStack item2;
     630           0 :         for (std::vector<ItemStack>::const_iterator
     631           0 :                         it = input.items.begin();
     632           0 :                         it != input.items.end(); it++) {
     633           0 :                 if (!it->empty()) {
     634           0 :                         if (item1.empty())
     635           0 :                                 item1 = *it;
     636           0 :                         else if (item2.empty())
     637           0 :                                 item2 = *it;
     638             :                         else
     639           0 :                                 return false;
     640             :                 }
     641             :         }
     642           0 :         ItemStack repaired = craftToolRepair(item1, item2, additional_wear, gamedef);
     643           0 :         return !repaired.empty();
     644             : }
     645             : 
     646           0 : CraftOutput CraftDefinitionToolRepair::getOutput(const CraftInput &input, IGameDef *gamedef) const
     647             : {
     648           0 :         ItemStack item1;
     649           0 :         ItemStack item2;
     650           0 :         for (std::vector<ItemStack>::const_iterator
     651           0 :                         it = input.items.begin();
     652           0 :                         it != input.items.end(); it++) {
     653           0 :                 if (!it->empty()) {
     654           0 :                         if (item1.empty())
     655           0 :                                 item1 = *it;
     656           0 :                         else if (item2.empty())
     657           0 :                                 item2 = *it;
     658             :                 }
     659             :         }
     660           0 :         ItemStack repaired = craftToolRepair(item1, item2, additional_wear, gamedef);
     661           0 :         return CraftOutput(repaired.getItemString(), 0);
     662             : }
     663             : 
     664           0 : CraftInput CraftDefinitionToolRepair::getInput(const CraftOutput &output, IGameDef *gamedef) const
     665             : {
     666           0 :         std::vector<ItemStack> stack;
     667           0 :         stack.push_back(ItemStack());
     668           0 :         return CraftInput(CRAFT_METHOD_COOKING, additional_wear, stack);
     669             : }
     670             : 
     671           0 : void CraftDefinitionToolRepair::decrementInput(CraftInput &input, std::vector<ItemStack> &output_replacements,
     672             :         IGameDef *gamedef) const
     673             : {
     674           0 :         craftDecrementInput(input, gamedef);
     675           0 : }
     676             : 
     677           0 : std::string CraftDefinitionToolRepair::dump() const
     678             : {
     679           0 :         std::ostringstream os(std::ios::binary);
     680           0 :         os << "(toolrepair, additional_wear=" << additional_wear << ")";
     681           0 :         return os.str();
     682             : }
     683             : 
     684             : /*
     685             :         CraftDefinitionCooking
     686             : */
     687             : 
     688           0 : std::string CraftDefinitionCooking::getName() const
     689             : {
     690           0 :         return "cooking";
     691             : }
     692             : 
     693           0 : bool CraftDefinitionCooking::check(const CraftInput &input, IGameDef *gamedef) const
     694             : {
     695           0 :         if (input.method != CRAFT_METHOD_COOKING)
     696           0 :                 return false;
     697             : 
     698             :         // Filter empty items out of input
     699           0 :         std::vector<std::string> input_filtered;
     700           0 :         for (std::vector<ItemStack>::const_iterator
     701           0 :                         it = input.items.begin();
     702           0 :                         it != input.items.end(); it++) {
     703           0 :                 if (it->name != "")
     704           0 :                         input_filtered.push_back(it->name);
     705             :         }
     706             : 
     707             :         // If there is a wrong number of items in input, no match
     708           0 :         if (input_filtered.size() != 1) {
     709             :                 /*dstream<<"Number of input items ("<<input_filtered.size()
     710             :                                 <<") does not match recipe size (1) "
     711             :                                 <<"of cooking recipe with output="<<output<<std::endl;*/
     712           0 :                 return false;
     713             :         }
     714             : 
     715             :         // Check the single input item
     716           0 :         return inputItemMatchesRecipe(input_filtered[0], recipe, gamedef->idef());
     717             : }
     718             : 
     719           0 : CraftOutput CraftDefinitionCooking::getOutput(const CraftInput &input, IGameDef *gamedef) const
     720             : {
     721           0 :         return CraftOutput(output, cooktime);
     722             : }
     723             : 
     724           0 : CraftInput CraftDefinitionCooking::getInput(const CraftOutput &output, IGameDef *gamedef) const
     725             : {
     726           0 :         std::vector<std::string> rec;
     727           0 :         rec.push_back(recipe);
     728           0 :         return CraftInput(CRAFT_METHOD_COOKING,cooktime,craftGetItems(rec,gamedef));
     729             : }
     730             : 
     731           0 : void CraftDefinitionCooking::decrementInput(CraftInput &input, std::vector<ItemStack> &output_replacements,
     732             :         IGameDef *gamedef) const
     733             : {
     734           0 :         craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef);
     735           0 : }
     736             : 
     737           0 : CraftHashType CraftDefinitionCooking::getHashType() const
     738             : {
     739           0 :         if (isGroupRecipeStr(recipe_name))
     740           0 :                 return CRAFT_HASH_TYPE_COUNT;
     741             :         else
     742           0 :                 return CRAFT_HASH_TYPE_ITEM_NAMES;
     743             : }
     744             : 
     745           0 : u64 CraftDefinitionCooking::getHash(CraftHashType type) const
     746             : {
     747           0 :         if (type == CRAFT_HASH_TYPE_ITEM_NAMES) {
     748           0 :                 return getHashForString(recipe_name);
     749           0 :         } else if (type == CRAFT_HASH_TYPE_COUNT) {
     750           0 :                 return 1;
     751             :         } else {
     752             :                 //illegal hash type for this CraftDefinition (pre-condition)
     753             :                 assert(false);
     754           0 :                 return 0;
     755             :         }
     756             : }
     757             : 
     758           0 : void CraftDefinitionCooking::initHash(IGameDef *gamedef)
     759             : {
     760           0 :         if (hash_inited)
     761           0 :                 return;
     762           0 :         hash_inited = true;
     763           0 :         recipe_name = craftGetItemName(recipe, gamedef);
     764             : }
     765             : 
     766           0 : std::string CraftDefinitionCooking::dump() const
     767             : {
     768           0 :         std::ostringstream os(std::ios::binary);
     769           0 :         os << "(cooking, output=\"" << output
     770           0 :                 << "\", recipe=\"" << recipe
     771           0 :                 << "\", cooktime=" << cooktime << ")"
     772           0 :                 << ", replacements=" << replacements.dump() << ")";
     773           0 :         return os.str();
     774             : }
     775             : 
     776             : /*
     777             :         CraftDefinitionFuel
     778             : */
     779             : 
     780           0 : std::string CraftDefinitionFuel::getName() const
     781             : {
     782           0 :         return "fuel";
     783             : }
     784             : 
     785           0 : bool CraftDefinitionFuel::check(const CraftInput &input, IGameDef *gamedef) const
     786             : {
     787           0 :         if (input.method != CRAFT_METHOD_FUEL)
     788           0 :                 return false;
     789             : 
     790             :         // Filter empty items out of input
     791           0 :         std::vector<std::string> input_filtered;
     792           0 :         for (std::vector<ItemStack>::const_iterator
     793           0 :                         it = input.items.begin();
     794           0 :                         it != input.items.end(); it++) {
     795           0 :                 if (it->name != "")
     796           0 :                         input_filtered.push_back(it->name);
     797             :         }
     798             : 
     799             :         // If there is a wrong number of items in input, no match
     800           0 :         if (input_filtered.size() != 1) {
     801             :                 /*dstream<<"Number of input items ("<<input_filtered.size()
     802             :                                 <<") does not match recipe size (1) "
     803             :                                 <<"of fuel recipe with burntime="<<burntime<<std::endl;*/
     804           0 :                 return false;
     805             :         }
     806             : 
     807             :         // Check the single input item
     808           0 :         return inputItemMatchesRecipe(input_filtered[0], recipe, gamedef->idef());
     809             : }
     810             : 
     811           0 : CraftOutput CraftDefinitionFuel::getOutput(const CraftInput &input, IGameDef *gamedef) const
     812             : {
     813           0 :         return CraftOutput("", burntime);
     814             : }
     815             : 
     816           0 : CraftInput CraftDefinitionFuel::getInput(const CraftOutput &output, IGameDef *gamedef) const
     817             : {
     818           0 :         std::vector<std::string> rec;
     819           0 :         rec.push_back(recipe);
     820           0 :         return CraftInput(CRAFT_METHOD_COOKING,(int)burntime,craftGetItems(rec,gamedef));
     821             : }
     822             : 
     823           0 : void CraftDefinitionFuel::decrementInput(CraftInput &input, std::vector<ItemStack> &output_replacements,
     824             :         IGameDef *gamedef) const
     825             : {
     826           0 :         craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef);
     827           0 : }
     828             : 
     829           0 : CraftHashType CraftDefinitionFuel::getHashType() const
     830             : {
     831           0 :         if (isGroupRecipeStr(recipe_name))
     832           0 :                 return CRAFT_HASH_TYPE_COUNT;
     833             :         else
     834           0 :                 return CRAFT_HASH_TYPE_ITEM_NAMES;
     835             : }
     836             : 
     837           0 : u64 CraftDefinitionFuel::getHash(CraftHashType type) const
     838             : {
     839           0 :         if (type == CRAFT_HASH_TYPE_ITEM_NAMES) {
     840           0 :                 return getHashForString(recipe_name);
     841           0 :         } else if (type == CRAFT_HASH_TYPE_COUNT) {
     842           0 :                 return 1;
     843             :         } else {
     844             :                 //illegal hash type for this CraftDefinition (pre-condition)
     845             :                 assert(false);
     846           0 :                 return 0;
     847             :         }
     848             : }
     849             : 
     850           0 : void CraftDefinitionFuel::initHash(IGameDef *gamedef)
     851             : {
     852           0 :         if (hash_inited)
     853           0 :                 return;
     854           0 :         hash_inited = true;
     855           0 :         recipe_name = craftGetItemName(recipe, gamedef);
     856             : }
     857           0 : std::string CraftDefinitionFuel::dump() const
     858             : {
     859           0 :         std::ostringstream os(std::ios::binary);
     860           0 :         os << "(fuel, recipe=\"" << recipe
     861           0 :                 << "\", burntime=" << burntime << ")"
     862           0 :                 << ", replacements=" << replacements.dump() << ")";
     863           0 :         return os.str();
     864             : }
     865             : 
     866             : /*
     867             :         Craft definition manager
     868             : */
     869             : 
     870             : class CCraftDefManager: public IWritableCraftDefManager
     871             : {
     872             : public:
     873           0 :         CCraftDefManager()
     874           0 :         {
     875           0 :                 m_craft_defs.resize(craft_hash_type_max + 1);
     876           0 :         }
     877             : 
     878           0 :         virtual ~CCraftDefManager()
     879           0 :         {
     880           0 :                 clear();
     881           0 :         }
     882             : 
     883           0 :         virtual bool getCraftResult(CraftInput &input, CraftOutput &output,
     884             :                         std::vector<ItemStack> &output_replacement, bool decrementInput,
     885             :                         IGameDef *gamedef) const
     886             :         {
     887           0 :                 output.item = "";
     888           0 :                 output.time = 0;
     889             : 
     890             :                 // If all input items are empty, abort.
     891           0 :                 bool all_empty = true;
     892           0 :                 for (std::vector<ItemStack>::const_iterator
     893           0 :                                 it = input.items.begin();
     894           0 :                                 it != input.items.end(); it++) {
     895           0 :                         if (!it->empty()) {
     896           0 :                                 all_empty = false;
     897           0 :                                 break;
     898             :                         }
     899             :                 }
     900           0 :                 if (all_empty)
     901           0 :                         return false;
     902             : 
     903           0 :                 std::vector<std::string> input_names;
     904           0 :                 input_names = craftGetItemNames(input.items, gamedef);
     905           0 :                 std::sort(input_names.begin(), input_names.end());
     906             : 
     907             :                 // Try hash types with increasing collision rate, and return if found.
     908           0 :                 for (int type = 0; type <= craft_hash_type_max; type++) {
     909           0 :                         u64 hash = getHashForGrid((CraftHashType) type, input_names);
     910             : 
     911             :                         /*errorstream << "Checking type " << type << " with hash " << hash << std::endl;*/
     912             : 
     913             :                         // We'd like to do "const [...] hash_collisions = m_craft_defs[type][hash];"
     914             :                         // but that doesn't compile for some reason. This does.
     915             :                         std::map<u64, std::vector<CraftDefinition*> >::const_iterator
     916           0 :                                 col_iter = (m_craft_defs[type]).find(hash);
     917             : 
     918           0 :                         if (col_iter == (m_craft_defs[type]).end())
     919           0 :                                 continue;
     920             : 
     921           0 :                         const std::vector<CraftDefinition*> &hash_collisions = col_iter->second;
     922             :                         // Walk crafting definitions from back to front, so that later
     923             :                         // definitions can override earlier ones.
     924           0 :                         for (std::vector<CraftDefinition*>::const_reverse_iterator
     925           0 :                                         it = hash_collisions.rbegin();
     926           0 :                                         it != hash_collisions.rend(); it++) {
     927           0 :                                 CraftDefinition *def = *it;
     928             : 
     929             :                                 /*errorstream << "Checking " << input.dump() << std::endl
     930             :                                         << " against " << def->dump() << std::endl;*/
     931             : 
     932           0 :                                 if (def->check(input, gamedef)) {
     933             :                                         // Get output, then decrement input (if requested)
     934           0 :                                         output = def->getOutput(input, gamedef);
     935           0 :                                         if (decrementInput)
     936           0 :                                                 def->decrementInput(input, output_replacement, gamedef);
     937             :                                         /*errorstream << "Check RETURNS TRUE" << std::endl;*/
     938           0 :                                         return true;
     939             :                                 }
     940             :                         }
     941             :                 }
     942           0 :                 return false;
     943             :         }
     944             : 
     945           0 :         virtual std::vector<CraftDefinition*> getCraftRecipes(CraftOutput &output,
     946             :                         IGameDef *gamedef, unsigned limit=0) const
     947             :         {
     948           0 :                 std::vector<CraftDefinition*> recipes;
     949             : 
     950             :                 std::map<std::string, std::vector<CraftDefinition*> >::const_iterator
     951           0 :                         vec_iter = m_output_craft_definitions.find(output.item);
     952             : 
     953           0 :                 if (vec_iter == m_output_craft_definitions.end())
     954           0 :                         return recipes;
     955             : 
     956           0 :                 const std::vector<CraftDefinition*> &vec = vec_iter->second;
     957             : 
     958           0 :                 recipes.reserve(limit ? MYMIN(limit, vec.size()) : vec.size());
     959             : 
     960           0 :                 for (std::vector<CraftDefinition*>::const_reverse_iterator
     961           0 :                                 it = vec.rbegin(); it != vec.rend(); ++it) {
     962           0 :                         if (limit && recipes.size() >= limit)
     963           0 :                                 break;
     964           0 :                         recipes.push_back(*it);
     965             :                 }
     966             : 
     967           0 :                 return recipes;
     968             :         }
     969           0 :         virtual std::string dump() const
     970             :         {
     971           0 :                 std::ostringstream os(std::ios::binary);
     972           0 :                 os << "Crafting definitions:\n";
     973           0 :                 for (int type = 0; type <= craft_hash_type_max; type++) {
     974           0 :                         for (std::map<u64, std::vector<CraftDefinition*> >::const_iterator
     975           0 :                                         it = (m_craft_defs[type]).begin();
     976           0 :                                         it != (m_craft_defs[type]).end(); it++) {
     977           0 :                                 for (std::vector<CraftDefinition*>::const_iterator
     978           0 :                                                 iit = it->second.begin(); iit != it->second.end(); iit++) {
     979           0 :                                         os << "type " << type << " hash " << it->first << (*iit)->dump() << "\n";
     980             :                                 }
     981             :                         }
     982             :                 }
     983           0 :                 return os.str();
     984             :         }
     985           0 :         virtual void registerCraft(CraftDefinition *def, IGameDef *gamedef)
     986             :         {
     987           0 :                 verbosestream << "registerCraft: registering craft definition: "
     988           0 :                                 << def->dump() << std::endl;
     989           0 :                 m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0].push_back(def);
     990             : 
     991           0 :                 CraftInput input;
     992             :                 std::string output_name = craftGetItemName(
     993           0 :                                 def->getOutput(input, gamedef).item, gamedef);
     994           0 :                 m_output_craft_definitions[output_name].push_back(def);
     995           0 :         }
     996           0 :         virtual void clear()
     997             :         {
     998           0 :                 for (int type = 0; type <= craft_hash_type_max; type++) {
     999           0 :                         for (std::map<u64, std::vector<CraftDefinition*> >::iterator
    1000           0 :                                         it = m_craft_defs[type].begin();
    1001           0 :                                         it != m_craft_defs[type].end(); it++) {
    1002           0 :                                 for (std::vector<CraftDefinition*>::iterator
    1003           0 :                                                 iit = it->second.begin(); iit != it->second.end(); iit++) {
    1004           0 :                                         delete *iit;
    1005             :                                 }
    1006           0 :                                 it->second.clear();
    1007             :                         }
    1008           0 :                         m_craft_defs[type].clear();
    1009             :                 }
    1010           0 :                 m_output_craft_definitions.clear();
    1011           0 :         }
    1012           0 :         virtual void initHashes(IGameDef *gamedef)
    1013             :         {
    1014             :                 // Move the CraftDefs from the unhashed layer into layers higher up.
    1015           0 :                 for (std::vector<CraftDefinition*>::iterator
    1016           0 :                         it = (m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0]).begin();
    1017           0 :                         it != (m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0]).end(); it++) {
    1018           0 :                         CraftDefinition *def = *it;
    1019             : 
    1020             :                         // Initialize and get the definition's hash
    1021           0 :                         def->initHash(gamedef);
    1022           0 :                         CraftHashType type = def->getHashType();
    1023           0 :                         u64 hash = def->getHash(type);
    1024             : 
    1025             :                         // Enter the definition
    1026           0 :                         m_craft_defs[type][hash].push_back(def);
    1027             :                 }
    1028           0 :                 m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0].clear();
    1029           0 :         }
    1030             : private:
    1031             :         //TODO: change both maps to unordered_map when c++11 can be used
    1032             :         std::vector<std::map<u64, std::vector<CraftDefinition*> > > m_craft_defs;
    1033             :         std::map<std::string, std::vector<CraftDefinition*> > m_output_craft_definitions;
    1034             : };
    1035             : 
    1036           0 : IWritableCraftDefManager* createCraftDefManager()
    1037             : {
    1038           0 :         return new CCraftDefManager();
    1039           3 : }
    1040             : 

Generated by: LCOV version 1.11