3 Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
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 General Public License for more details.
15 You should have received a copy of the GNU 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.
22 #include "irrlichttypes.h"
28 #include "inventory.h"
31 // Deserialize an itemstring then return the name of the item
32 static std::string craftGetItemName(const std::string &itemstring, IGameDef *gamedef)
35 item.deSerialize(itemstring, gamedef->idef());
39 // (mapcar craftGetItemName itemstrings)
40 static std::vector<std::string> craftGetItemNames(
41 const std::vector<std::string> &itemstrings, IGameDef *gamedef)
43 std::vector<std::string> result;
44 for(std::vector<std::string>::const_iterator
45 i = itemstrings.begin();
46 i != itemstrings.end(); i++)
48 result.push_back(craftGetItemName(*i, gamedef));
53 // Get name of each item, and return them as a new list.
54 static std::vector<std::string> craftGetItemNames(
55 const std::vector<ItemStack> &items, IGameDef *gamedef)
57 std::vector<std::string> result;
58 for(std::vector<ItemStack>::const_iterator
60 i != items.end(); i++)
62 result.push_back(i->name);
67 // Compute bounding rectangle given a matrix of items
68 // Returns false if every item is ""
69 static bool craftGetBounds(const std::vector<std::string> &items, unsigned int width,
70 unsigned int &min_x, unsigned int &max_x,
71 unsigned int &min_y, unsigned int &max_y)
76 for(std::vector<std::string>::const_iterator
78 i != items.end(); i++)
80 if(*i != "") // Is this an actual item?
84 // This is the first nonempty item
91 if(x < min_x) min_x = x;
92 if(x > max_x) max_x = x;
93 if(y < min_y) min_y = y;
94 if(y > max_y) max_y = y;
109 // Convert a list of item names to a multiset
110 static std::multiset<std::string> craftMakeMultiset(const std::vector<std::string> &names)
112 std::multiset<std::string> set;
113 for(std::vector<std::string>::const_iterator
115 i != names.end(); i++)
123 // Removes 1 from each item stack
124 static void craftDecrementInput(CraftInput &input, IGameDef *gamedef)
126 for(std::vector<ItemStack>::iterator
127 i = input.items.begin();
128 i != input.items.end(); i++)
135 // Removes 1 from each item stack with replacement support
136 // Example: if replacements contains the pair ("bucket:bucket_water", "bucket:bucket_empty"),
137 // a water bucket will not be removed but replaced by an empty bucket.
138 static void craftDecrementOrReplaceInput(CraftInput &input,
139 const CraftReplacements &replacements,
142 if(replacements.pairs.empty())
144 craftDecrementInput(input, gamedef);
148 // Make a copy of the replacements pair list
149 std::vector<std::pair<std::string, std::string> > pairs = replacements.pairs;
151 for(std::vector<ItemStack>::iterator
152 i = input.items.begin();
153 i != input.items.end(); i++)
157 // Find an appropriate replacement
158 bool found_replacement = false;
159 for(std::vector<std::pair<std::string, std::string> >::iterator
161 j != pairs.end(); j++)
164 from_item.deSerialize(j->first, gamedef->idef());
165 if(i->name == from_item.name)
167 i->deSerialize(j->second, gamedef->idef());
168 found_replacement = true;
173 // No replacement was found, simply decrement count to zero
174 if(!found_replacement)
177 else if(i->count >= 2)
179 // Ignore replacements for items with count >= 2
185 // Dump an itemstring matrix
186 static std::string craftDumpMatrix(const std::vector<std::string> &items,
189 std::ostringstream os(std::ios::binary);
192 for(std::vector<std::string>::const_iterator
194 i != items.end(); i++, x++)
205 os<<"\""<<(*i)<<"\"";
211 // Dump an item matrix
212 std::string craftDumpMatrix(const std::vector<ItemStack> &items,
215 std::ostringstream os(std::ios::binary);
218 for(std::vector<ItemStack>::const_iterator
220 i != items.end(); i++, x++)
231 os<<"\""<<(i->getItemString())<<"\"";
242 std::string CraftInput::dump() const
244 std::ostringstream os(std::ios::binary);
245 os<<"(method="<<((int)method)<<", items="<<craftDumpMatrix(items, width)<<")";
253 std::string CraftOutput::dump() const
255 std::ostringstream os(std::ios::binary);
256 os<<"(item=\""<<item<<"\", time="<<time<<")";
263 std::string CraftReplacements::dump() const
265 std::ostringstream os(std::ios::binary);
267 const char *sep = "";
268 for(std::vector<std::pair<std::string, std::string> >::const_iterator
270 i != pairs.end(); i++)
272 os<<sep<<"\""<<(i->first)<<"\"=>\""<<(i->second)<<"\"";
284 void CraftDefinition::serialize(std::ostream &os) const
286 writeU8(os, 1); // version
287 os<<serializeString(getName());
291 CraftDefinition* CraftDefinition::deSerialize(std::istream &is)
293 int version = readU8(is);
294 if(version != 1) throw SerializationError(
295 "unsupported CraftDefinition version");
296 std::string name = deSerializeString(is);
297 CraftDefinition *def = NULL;
300 def = new CraftDefinitionShaped;
302 else if(name == "shapeless")
304 def = new CraftDefinitionShapeless;
306 else if(name == "toolrepair")
308 def = new CraftDefinitionToolRepair;
310 else if(name == "cooking")
312 def = new CraftDefinitionCooking;
314 else if(name == "fuel")
316 def = new CraftDefinitionFuel;
320 infostream<<"Unknown CraftDefinition name=\""<<name<<"\""<<std::endl;
321 throw SerializationError("Unknown CraftDefinition name");
323 def->deSerializeBody(is, version);
328 CraftDefinitionShaped
331 std::string CraftDefinitionShaped::getName() const
336 bool CraftDefinitionShaped::check(const CraftInput &input, IGameDef *gamedef) const
338 if(input.method != CRAFT_METHOD_NORMAL)
341 // Get input item matrix
342 std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
343 unsigned int inp_width = input.width;
346 while(inp_names.size() % inp_width != 0)
347 inp_names.push_back("");
350 unsigned int inp_min_x=0, inp_max_x=0, inp_min_y=0, inp_max_y=0;
351 if(!craftGetBounds(inp_names, inp_width, inp_min_x, inp_max_x, inp_min_y, inp_max_y))
352 return false; // it was empty
354 // Get recipe item matrix
355 std::vector<std::string> rec_names = craftGetItemNames(recipe, gamedef);
356 unsigned int rec_width = width;
359 while(rec_names.size() % rec_width != 0)
360 rec_names.push_back("");
363 unsigned int rec_min_x=0, rec_max_x=0, rec_min_y=0, rec_max_y=0;
364 if(!craftGetBounds(rec_names, rec_width, rec_min_x, rec_max_x, rec_min_y, rec_max_y))
365 return false; // it was empty
368 if(inp_max_x - inp_min_x != rec_max_x - rec_min_x)
370 if(inp_max_y - inp_min_y != rec_max_y - rec_min_y)
373 // Verify that all item names in the bounding box are equal
374 unsigned int w = inp_max_x - inp_min_x + 1;
375 unsigned int h = inp_max_y - inp_min_y + 1;
376 for(unsigned int y=0; y<h; y++)
377 for(unsigned int x=0; x<w; x++)
379 unsigned int inp_x = inp_min_x + x;
380 unsigned int inp_y = inp_min_y + y;
381 unsigned int rec_x = rec_min_x + x;
382 unsigned int rec_y = rec_min_y + y;
385 inp_names[inp_y * inp_width + inp_x] !=
386 rec_names[rec_y * rec_width + rec_x]
395 CraftOutput CraftDefinitionShaped::getOutput(const CraftInput &input, IGameDef *gamedef) const
397 return CraftOutput(output, 0);
400 void CraftDefinitionShaped::decrementInput(CraftInput &input, IGameDef *gamedef) const
402 craftDecrementOrReplaceInput(input, replacements, gamedef);
405 std::string CraftDefinitionShaped::dump() const
407 std::ostringstream os(std::ios::binary);
408 os<<"(shaped, output=\""<<output
409 <<"\", recipe="<<craftDumpMatrix(recipe, width)
410 <<", replacements="<<replacements.dump()<<")";
414 void CraftDefinitionShaped::serializeBody(std::ostream &os) const
416 os<<serializeString(output);
418 writeU16(os, recipe.size());
419 for(u32 i=0; i<recipe.size(); i++)
420 os<<serializeString(recipe[i]);
421 writeU16(os, replacements.pairs.size());
422 for(u32 i=0; i<replacements.pairs.size(); i++)
424 os<<serializeString(replacements.pairs[i].first);
425 os<<serializeString(replacements.pairs[i].second);
429 void CraftDefinitionShaped::deSerializeBody(std::istream &is, int version)
431 if(version != 1) throw SerializationError(
432 "unsupported CraftDefinitionShaped version");
433 output = deSerializeString(is);
436 u32 count = readU16(is);
437 for(u32 i=0; i<count; i++)
438 recipe.push_back(deSerializeString(is));
439 replacements.pairs.clear();
441 for(u32 i=0; i<count; i++)
443 std::string first = deSerializeString(is);
444 std::string second = deSerializeString(is);
445 replacements.pairs.push_back(std::make_pair(first, second));
450 CraftDefinitionShapeless
453 std::string CraftDefinitionShapeless::getName() const
458 bool CraftDefinitionShapeless::check(const CraftInput &input, IGameDef *gamedef) const
460 if(input.method != CRAFT_METHOD_NORMAL)
463 // Get input item multiset
464 std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
465 std::multiset<std::string> inp_names_multiset = craftMakeMultiset(inp_names);
467 // Get recipe item multiset
468 std::vector<std::string> rec_names = craftGetItemNames(recipe, gamedef);
469 std::multiset<std::string> rec_names_multiset = craftMakeMultiset(rec_names);
471 // Recipe is matched when the multisets coincide
472 return inp_names_multiset == rec_names_multiset;
475 CraftOutput CraftDefinitionShapeless::getOutput(const CraftInput &input, IGameDef *gamedef) const
477 return CraftOutput(output, 0);
480 void CraftDefinitionShapeless::decrementInput(CraftInput &input, IGameDef *gamedef) const
482 craftDecrementOrReplaceInput(input, replacements, gamedef);
485 std::string CraftDefinitionShapeless::dump() const
487 std::ostringstream os(std::ios::binary);
488 os<<"(shapeless, output=\""<<output
489 <<"\", recipe="<<craftDumpMatrix(recipe, recipe.size())
490 <<", replacements="<<replacements.dump()<<")";
494 void CraftDefinitionShapeless::serializeBody(std::ostream &os) const
496 os<<serializeString(output);
497 writeU16(os, recipe.size());
498 for(u32 i=0; i<recipe.size(); i++)
499 os<<serializeString(recipe[i]);
500 writeU16(os, replacements.pairs.size());
501 for(u32 i=0; i<replacements.pairs.size(); i++)
503 os<<serializeString(replacements.pairs[i].first);
504 os<<serializeString(replacements.pairs[i].second);
508 void CraftDefinitionShapeless::deSerializeBody(std::istream &is, int version)
510 if(version != 1) throw SerializationError(
511 "unsupported CraftDefinitionShapeless version");
512 output = deSerializeString(is);
514 u32 count = readU16(is);
515 for(u32 i=0; i<count; i++)
516 recipe.push_back(deSerializeString(is));
517 replacements.pairs.clear();
519 for(u32 i=0; i<count; i++)
521 std::string first = deSerializeString(is);
522 std::string second = deSerializeString(is);
523 replacements.pairs.push_back(std::make_pair(first, second));
528 CraftDefinitionToolRepair
531 static ItemStack craftToolRepair(
532 const ItemStack &item1,
533 const ItemStack &item2,
534 float additional_wear,
537 IItemDefManager *idef = gamedef->idef();
538 if(item1.count != 1 || item2.count != 1 || item1.name != item2.name
539 || idef->get(item1.name).type != ITEM_TOOL
540 || idef->get(item2.name).type != ITEM_TOOL)
546 s32 item1_uses = 65536 - (u32) item1.wear;
547 s32 item2_uses = 65536 - (u32) item2.wear;
548 s32 new_uses = item1_uses + item2_uses;
549 s32 new_wear = 65536 - new_uses + floor(additional_wear * 65536 + 0.5);
550 if(new_wear >= 65536)
555 ItemStack repaired = item1;
556 repaired.wear = new_wear;
560 std::string CraftDefinitionToolRepair::getName() const
565 bool CraftDefinitionToolRepair::check(const CraftInput &input, IGameDef *gamedef) const
567 if(input.method != CRAFT_METHOD_NORMAL)
572 for(std::vector<ItemStack>::const_iterator
573 i = input.items.begin();
574 i != input.items.end(); i++)
580 else if(item2.empty())
586 ItemStack repaired = craftToolRepair(item1, item2, additional_wear, gamedef);
587 return !repaired.empty();
590 CraftOutput CraftDefinitionToolRepair::getOutput(const CraftInput &input, IGameDef *gamedef) const
594 for(std::vector<ItemStack>::const_iterator
595 i = input.items.begin();
596 i != input.items.end(); i++)
602 else if(item2.empty())
606 ItemStack repaired = craftToolRepair(item1, item2, additional_wear, gamedef);
607 return CraftOutput(repaired.getItemString(), 0);
610 void CraftDefinitionToolRepair::decrementInput(CraftInput &input, IGameDef *gamedef) const
612 craftDecrementInput(input, gamedef);
615 std::string CraftDefinitionToolRepair::dump() const
617 std::ostringstream os(std::ios::binary);
618 os<<"(toolrepair, additional_wear="<<additional_wear<<")";
622 void CraftDefinitionToolRepair::serializeBody(std::ostream &os) const
624 writeF1000(os, additional_wear);
627 void CraftDefinitionToolRepair::deSerializeBody(std::istream &is, int version)
629 if(version != 1) throw SerializationError(
630 "unsupported CraftDefinitionToolRepair version");
631 additional_wear = readF1000(is);
635 CraftDefinitionCooking
638 std::string CraftDefinitionCooking::getName() const
643 bool CraftDefinitionCooking::check(const CraftInput &input, IGameDef *gamedef) const
645 if(input.method != CRAFT_METHOD_COOKING)
648 // Get input item multiset
649 std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
650 std::multiset<std::string> inp_names_multiset = craftMakeMultiset(inp_names);
652 // Get recipe item multiset
653 std::multiset<std::string> rec_names_multiset;
654 rec_names_multiset.insert(craftGetItemName(recipe, gamedef));
656 // Recipe is matched when the multisets coincide
657 return inp_names_multiset == rec_names_multiset;
660 CraftOutput CraftDefinitionCooking::getOutput(const CraftInput &input, IGameDef *gamedef) const
662 return CraftOutput(output, cooktime);
665 void CraftDefinitionCooking::decrementInput(CraftInput &input, IGameDef *gamedef) const
667 craftDecrementInput(input, gamedef);
670 std::string CraftDefinitionCooking::dump() const
672 std::ostringstream os(std::ios::binary);
673 os<<"(cooking, output=\""<<output
674 <<"\", recipe=\""<<recipe
675 <<"\", cooktime="<<cooktime<<")";
679 void CraftDefinitionCooking::serializeBody(std::ostream &os) const
681 os<<serializeString(output);
682 os<<serializeString(recipe);
683 writeF1000(os, cooktime);
686 void CraftDefinitionCooking::deSerializeBody(std::istream &is, int version)
688 if(version != 1) throw SerializationError(
689 "unsupported CraftDefinitionCooking version");
690 output = deSerializeString(is);
691 recipe = deSerializeString(is);
692 cooktime = readF1000(is);
699 std::string CraftDefinitionFuel::getName() const
704 bool CraftDefinitionFuel::check(const CraftInput &input, IGameDef *gamedef) const
706 if(input.method != CRAFT_METHOD_FUEL)
709 // Get input item multiset
710 std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
711 std::multiset<std::string> inp_names_multiset = craftMakeMultiset(inp_names);
713 // Get recipe item multiset
714 std::multiset<std::string> rec_names_multiset;
715 rec_names_multiset.insert(craftGetItemName(recipe, gamedef));
717 // Recipe is matched when the multisets coincide
718 return inp_names_multiset == rec_names_multiset;
721 CraftOutput CraftDefinitionFuel::getOutput(const CraftInput &input, IGameDef *gamedef) const
723 return CraftOutput("", burntime);
726 void CraftDefinitionFuel::decrementInput(CraftInput &input, IGameDef *gamedef) const
728 craftDecrementInput(input, gamedef);
731 std::string CraftDefinitionFuel::dump() const
733 std::ostringstream os(std::ios::binary);
734 os<<"(fuel, recipe=\""<<recipe
735 <<"\", burntime="<<burntime<<")";
739 void CraftDefinitionFuel::serializeBody(std::ostream &os) const
741 os<<serializeString(recipe);
742 writeF1000(os, burntime);
745 void CraftDefinitionFuel::deSerializeBody(std::istream &is, int version)
747 if(version != 1) throw SerializationError(
748 "unsupported CraftDefinitionFuel version");
749 recipe = deSerializeString(is);
750 burntime = readF1000(is);
754 Craft definition manager
757 class CCraftDefManager: public IWritableCraftDefManager
760 virtual ~CCraftDefManager()
764 virtual bool getCraftResult(CraftInput &input, CraftOutput &output,
765 bool decrementInput, IGameDef *gamedef) const
770 // If all input items are empty, abort.
771 bool all_empty = true;
772 for(std::vector<ItemStack>::const_iterator
773 i = input.items.begin();
774 i != input.items.end(); i++)
785 // Walk crafting definitions from back to front, so that later
786 // definitions can override earlier ones.
787 for(std::vector<CraftDefinition*>::const_reverse_iterator
788 i = m_craft_definitions.rbegin();
789 i != m_craft_definitions.rend(); i++)
791 CraftDefinition *def = *i;
793 /*infostream<<"Checking "<<input.dump()<<std::endl
794 <<" against "<<def->dump()<<std::endl;*/
797 if(def->check(input, gamedef))
799 // Get output, then decrement input (if requested)
800 output = def->getOutput(input, gamedef);
802 def->decrementInput(input, gamedef);
806 catch(SerializationError &e)
808 errorstream<<"getCraftResult: ERROR: "
809 <<"Serialization error in recipe "
810 <<def->dump()<<std::endl;
811 // then go on with the next craft definition
816 virtual std::string dump() const
818 std::ostringstream os(std::ios::binary);
819 os<<"Crafting definitions:\n";
820 for(std::vector<CraftDefinition*>::const_iterator
821 i = m_craft_definitions.begin();
822 i != m_craft_definitions.end(); i++)
824 os<<(*i)->dump()<<"\n";
828 virtual void registerCraft(CraftDefinition *def)
830 verbosestream<<"registerCraft: registering craft definition: "
831 <<def->dump()<<std::endl;
832 m_craft_definitions.push_back(def);
836 for(std::vector<CraftDefinition*>::iterator
837 i = m_craft_definitions.begin();
838 i != m_craft_definitions.end(); i++){
841 m_craft_definitions.clear();
843 virtual void serialize(std::ostream &os) const
845 writeU8(os, 0); // version
846 u16 count = m_craft_definitions.size();
848 for(std::vector<CraftDefinition*>::const_iterator
849 i = m_craft_definitions.begin();
850 i != m_craft_definitions.end(); i++){
851 CraftDefinition *def = *i;
852 // Serialize wrapped in a string
853 std::ostringstream tmp_os(std::ios::binary);
854 def->serialize(tmp_os);
855 os<<serializeString(tmp_os.str());
858 virtual void deSerialize(std::istream &is)
863 int version = readU8(is);
864 if(version != 0) throw SerializationError(
865 "unsupported CraftDefManager version");
866 u16 count = readU16(is);
867 for(u16 i=0; i<count; i++){
868 // Deserialize a string and grab a CraftDefinition from it
869 std::istringstream tmp_is(deSerializeString(is), std::ios::binary);
870 CraftDefinition *def = CraftDefinition::deSerialize(tmp_is);
876 std::vector<CraftDefinition*> m_craft_definitions;
879 IWritableCraftDefManager* createCraftDefManager()
881 return new CCraftDefManager();