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