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