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