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 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.
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.
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.
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<<")";
264 std::string CraftReplacements::dump() const
266 std::ostringstream os(std::ios::binary);
268 const char *sep = "";
269 for(std::vector<std::pair<std::string, std::string> >::const_iterator
271 i != pairs.end(); i++)
273 os<<sep<<"\""<<(i->first)<<"\"=>\""<<(i->second)<<"\"";
280 void CraftReplacements::serialize(std::ostream &os) const
282 writeU16(os, pairs.size());
283 for(u32 i=0; i<pairs.size(); i++)
285 os<<serializeString(pairs[i].first);
286 os<<serializeString(pairs[i].second);
290 void CraftReplacements::deSerialize(std::istream &is)
293 u32 count = readU16(is);
294 for(u32 i=0; i<count; i++)
296 std::string first = deSerializeString(is);
297 std::string second = deSerializeString(is);
298 pairs.push_back(std::make_pair(first, second));
306 void CraftDefinition::serialize(std::ostream &os) const
308 writeU8(os, 1); // version
309 os<<serializeString(getName());
313 CraftDefinition* CraftDefinition::deSerialize(std::istream &is)
315 int version = readU8(is);
316 if(version != 1) throw SerializationError(
317 "unsupported CraftDefinition version");
318 std::string name = deSerializeString(is);
319 CraftDefinition *def = NULL;
322 def = new CraftDefinitionShaped;
324 else if(name == "shapeless")
326 def = new CraftDefinitionShapeless;
328 else if(name == "toolrepair")
330 def = new CraftDefinitionToolRepair;
332 else if(name == "cooking")
334 def = new CraftDefinitionCooking;
336 else if(name == "fuel")
338 def = new CraftDefinitionFuel;
342 infostream<<"Unknown CraftDefinition name=\""<<name<<"\""<<std::endl;
343 throw SerializationError("Unknown CraftDefinition name");
345 def->deSerializeBody(is, version);
350 CraftDefinitionShaped
353 std::string CraftDefinitionShaped::getName() const
358 bool CraftDefinitionShaped::check(const CraftInput &input, IGameDef *gamedef) const
360 if(input.method != CRAFT_METHOD_NORMAL)
363 // Get input item matrix
364 std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
365 unsigned int inp_width = input.width;
368 while(inp_names.size() % inp_width != 0)
369 inp_names.push_back("");
372 unsigned int inp_min_x=0, inp_max_x=0, inp_min_y=0, inp_max_y=0;
373 if(!craftGetBounds(inp_names, inp_width, inp_min_x, inp_max_x, inp_min_y, inp_max_y))
374 return false; // it was empty
376 // Get recipe item matrix
377 std::vector<std::string> rec_names = craftGetItemNames(recipe, gamedef);
378 unsigned int rec_width = width;
381 while(rec_names.size() % rec_width != 0)
382 rec_names.push_back("");
385 unsigned int rec_min_x=0, rec_max_x=0, rec_min_y=0, rec_max_y=0;
386 if(!craftGetBounds(rec_names, rec_width, rec_min_x, rec_max_x, rec_min_y, rec_max_y))
387 return false; // it was empty
390 if(inp_max_x - inp_min_x != rec_max_x - rec_min_x)
392 if(inp_max_y - inp_min_y != rec_max_y - rec_min_y)
395 // Verify that all item names in the bounding box are equal
396 unsigned int w = inp_max_x - inp_min_x + 1;
397 unsigned int h = inp_max_y - inp_min_y + 1;
398 for(unsigned int y=0; y<h; y++)
399 for(unsigned int x=0; x<w; x++)
401 unsigned int inp_x = inp_min_x + x;
402 unsigned int inp_y = inp_min_y + y;
403 unsigned int rec_x = rec_min_x + x;
404 unsigned int rec_y = rec_min_y + y;
407 inp_names[inp_y * inp_width + inp_x] !=
408 rec_names[rec_y * rec_width + rec_x]
417 CraftOutput CraftDefinitionShaped::getOutput(const CraftInput &input, IGameDef *gamedef) const
419 return CraftOutput(output, 0);
422 void CraftDefinitionShaped::decrementInput(CraftInput &input, IGameDef *gamedef) const
424 craftDecrementOrReplaceInput(input, replacements, gamedef);
427 std::string CraftDefinitionShaped::dump() const
429 std::ostringstream os(std::ios::binary);
430 os<<"(shaped, output=\""<<output
431 <<"\", recipe="<<craftDumpMatrix(recipe, width)
432 <<", replacements="<<replacements.dump()<<")";
436 void CraftDefinitionShaped::serializeBody(std::ostream &os) const
438 os<<serializeString(output);
440 writeU16(os, recipe.size());
441 for(u32 i=0; i<recipe.size(); i++)
442 os<<serializeString(recipe[i]);
443 replacements.serialize(os);
446 void CraftDefinitionShaped::deSerializeBody(std::istream &is, int version)
448 if(version != 1) throw SerializationError(
449 "unsupported CraftDefinitionShaped version");
450 output = deSerializeString(is);
453 u32 count = readU16(is);
454 for(u32 i=0; i<count; i++)
455 recipe.push_back(deSerializeString(is));
456 replacements.deSerialize(is);
460 CraftDefinitionShapeless
463 std::string CraftDefinitionShapeless::getName() const
468 bool CraftDefinitionShapeless::check(const CraftInput &input, IGameDef *gamedef) const
470 if(input.method != CRAFT_METHOD_NORMAL)
473 // Get input item multiset
474 std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
475 std::multiset<std::string> inp_names_multiset = craftMakeMultiset(inp_names);
477 // Get recipe item multiset
478 std::vector<std::string> rec_names = craftGetItemNames(recipe, gamedef);
479 std::multiset<std::string> rec_names_multiset = craftMakeMultiset(rec_names);
481 // Recipe is matched when the multisets coincide
482 return inp_names_multiset == rec_names_multiset;
485 CraftOutput CraftDefinitionShapeless::getOutput(const CraftInput &input, IGameDef *gamedef) const
487 return CraftOutput(output, 0);
490 void CraftDefinitionShapeless::decrementInput(CraftInput &input, IGameDef *gamedef) const
492 craftDecrementOrReplaceInput(input, replacements, gamedef);
495 std::string CraftDefinitionShapeless::dump() const
497 std::ostringstream os(std::ios::binary);
498 os<<"(shapeless, output=\""<<output
499 <<"\", recipe="<<craftDumpMatrix(recipe, recipe.size())
500 <<", replacements="<<replacements.dump()<<")";
504 void CraftDefinitionShapeless::serializeBody(std::ostream &os) const
506 os<<serializeString(output);
507 writeU16(os, recipe.size());
508 for(u32 i=0; i<recipe.size(); i++)
509 os<<serializeString(recipe[i]);
510 replacements.serialize(os);
513 void CraftDefinitionShapeless::deSerializeBody(std::istream &is, int version)
515 if(version != 1) throw SerializationError(
516 "unsupported CraftDefinitionShapeless version");
517 output = deSerializeString(is);
519 u32 count = readU16(is);
520 for(u32 i=0; i<count; i++)
521 recipe.push_back(deSerializeString(is));
522 replacements.deSerialize(is);
526 CraftDefinitionToolRepair
529 static ItemStack craftToolRepair(
530 const ItemStack &item1,
531 const ItemStack &item2,
532 float additional_wear,
535 IItemDefManager *idef = gamedef->idef();
536 if(item1.count != 1 || item2.count != 1 || item1.name != item2.name
537 || idef->get(item1.name).type != ITEM_TOOL
538 || idef->get(item2.name).type != ITEM_TOOL)
544 s32 item1_uses = 65536 - (u32) item1.wear;
545 s32 item2_uses = 65536 - (u32) item2.wear;
546 s32 new_uses = item1_uses + item2_uses;
547 s32 new_wear = 65536 - new_uses + floor(additional_wear * 65536 + 0.5);
548 if(new_wear >= 65536)
553 ItemStack repaired = item1;
554 repaired.wear = new_wear;
558 std::string CraftDefinitionToolRepair::getName() const
563 bool CraftDefinitionToolRepair::check(const CraftInput &input, IGameDef *gamedef) const
565 if(input.method != CRAFT_METHOD_NORMAL)
570 for(std::vector<ItemStack>::const_iterator
571 i = input.items.begin();
572 i != input.items.end(); i++)
578 else if(item2.empty())
584 ItemStack repaired = craftToolRepair(item1, item2, additional_wear, gamedef);
585 return !repaired.empty();
588 CraftOutput CraftDefinitionToolRepair::getOutput(const CraftInput &input, IGameDef *gamedef) const
592 for(std::vector<ItemStack>::const_iterator
593 i = input.items.begin();
594 i != input.items.end(); i++)
600 else if(item2.empty())
604 ItemStack repaired = craftToolRepair(item1, item2, additional_wear, gamedef);
605 return CraftOutput(repaired.getItemString(), 0);
608 void CraftDefinitionToolRepair::decrementInput(CraftInput &input, IGameDef *gamedef) const
610 craftDecrementInput(input, gamedef);
613 std::string CraftDefinitionToolRepair::dump() const
615 std::ostringstream os(std::ios::binary);
616 os<<"(toolrepair, additional_wear="<<additional_wear<<")";
620 void CraftDefinitionToolRepair::serializeBody(std::ostream &os) const
622 writeF1000(os, additional_wear);
625 void CraftDefinitionToolRepair::deSerializeBody(std::istream &is, int version)
627 if(version != 1) throw SerializationError(
628 "unsupported CraftDefinitionToolRepair version");
629 additional_wear = readF1000(is);
633 CraftDefinitionCooking
636 std::string CraftDefinitionCooking::getName() const
641 bool CraftDefinitionCooking::check(const CraftInput &input, IGameDef *gamedef) const
643 if(input.method != CRAFT_METHOD_COOKING)
646 // Get input item multiset
647 std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
648 std::multiset<std::string> inp_names_multiset = craftMakeMultiset(inp_names);
650 // Get recipe item multiset
651 std::multiset<std::string> rec_names_multiset;
652 rec_names_multiset.insert(craftGetItemName(recipe, gamedef));
654 // Recipe is matched when the multisets coincide
655 return inp_names_multiset == rec_names_multiset;
658 CraftOutput CraftDefinitionCooking::getOutput(const CraftInput &input, IGameDef *gamedef) const
660 return CraftOutput(output, cooktime);
663 void CraftDefinitionCooking::decrementInput(CraftInput &input, IGameDef *gamedef) const
665 craftDecrementOrReplaceInput(input, replacements, gamedef);
668 std::string CraftDefinitionCooking::dump() const
670 std::ostringstream os(std::ios::binary);
671 os<<"(cooking, output=\""<<output
672 <<"\", recipe=\""<<recipe
673 <<"\", cooktime="<<cooktime<<")"
674 <<", replacements="<<replacements.dump()<<")";
678 void CraftDefinitionCooking::serializeBody(std::ostream &os) const
680 os<<serializeString(output);
681 os<<serializeString(recipe);
682 writeF1000(os, cooktime);
683 replacements.serialize(os);
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);
693 replacements.deSerialize(is);
700 std::string CraftDefinitionFuel::getName() const
705 bool CraftDefinitionFuel::check(const CraftInput &input, IGameDef *gamedef) const
707 if(input.method != CRAFT_METHOD_FUEL)
710 // Get input item multiset
711 std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
712 std::multiset<std::string> inp_names_multiset = craftMakeMultiset(inp_names);
714 // Get recipe item multiset
715 std::multiset<std::string> rec_names_multiset;
716 rec_names_multiset.insert(craftGetItemName(recipe, gamedef));
718 // Recipe is matched when the multisets coincide
719 return inp_names_multiset == rec_names_multiset;
722 CraftOutput CraftDefinitionFuel::getOutput(const CraftInput &input, IGameDef *gamedef) const
724 return CraftOutput("", burntime);
727 void CraftDefinitionFuel::decrementInput(CraftInput &input, IGameDef *gamedef) const
729 craftDecrementOrReplaceInput(input, replacements, gamedef);
732 std::string CraftDefinitionFuel::dump() const
734 std::ostringstream os(std::ios::binary);
735 os<<"(fuel, recipe=\""<<recipe
736 <<"\", burntime="<<burntime<<")"
737 <<", replacements="<<replacements.dump()<<")";
741 void CraftDefinitionFuel::serializeBody(std::ostream &os) const
743 os<<serializeString(recipe);
744 writeF1000(os, burntime);
745 replacements.serialize(os);
748 void CraftDefinitionFuel::deSerializeBody(std::istream &is, int version)
750 if(version != 1) throw SerializationError(
751 "unsupported CraftDefinitionFuel version");
752 recipe = deSerializeString(is);
753 burntime = readF1000(is);
754 replacements.deSerialize(is);
758 Craft definition manager
761 class CCraftDefManager: public IWritableCraftDefManager
764 virtual ~CCraftDefManager()
768 virtual bool getCraftResult(CraftInput &input, CraftOutput &output,
769 bool decrementInput, IGameDef *gamedef) const
774 // If all input items are empty, abort.
775 bool all_empty = true;
776 for(std::vector<ItemStack>::const_iterator
777 i = input.items.begin();
778 i != input.items.end(); i++)
789 // Walk crafting definitions from back to front, so that later
790 // definitions can override earlier ones.
791 for(std::vector<CraftDefinition*>::const_reverse_iterator
792 i = m_craft_definitions.rbegin();
793 i != m_craft_definitions.rend(); i++)
795 CraftDefinition *def = *i;
797 /*infostream<<"Checking "<<input.dump()<<std::endl
798 <<" against "<<def->dump()<<std::endl;*/
801 if(def->check(input, gamedef))
803 // Get output, then decrement input (if requested)
804 output = def->getOutput(input, gamedef);
806 def->decrementInput(input, gamedef);
810 catch(SerializationError &e)
812 errorstream<<"getCraftResult: ERROR: "
813 <<"Serialization error in recipe "
814 <<def->dump()<<std::endl;
815 // then go on with the next craft definition
820 virtual std::string dump() const
822 std::ostringstream os(std::ios::binary);
823 os<<"Crafting definitions:\n";
824 for(std::vector<CraftDefinition*>::const_iterator
825 i = m_craft_definitions.begin();
826 i != m_craft_definitions.end(); i++)
828 os<<(*i)->dump()<<"\n";
832 virtual void registerCraft(CraftDefinition *def)
834 verbosestream<<"registerCraft: registering craft definition: "
835 <<def->dump()<<std::endl;
836 m_craft_definitions.push_back(def);
840 for(std::vector<CraftDefinition*>::iterator
841 i = m_craft_definitions.begin();
842 i != m_craft_definitions.end(); i++){
845 m_craft_definitions.clear();
847 virtual void serialize(std::ostream &os) const
849 writeU8(os, 0); // version
850 u16 count = m_craft_definitions.size();
852 for(std::vector<CraftDefinition*>::const_iterator
853 i = m_craft_definitions.begin();
854 i != m_craft_definitions.end(); i++){
855 CraftDefinition *def = *i;
856 // Serialize wrapped in a string
857 std::ostringstream tmp_os(std::ios::binary);
858 def->serialize(tmp_os);
859 os<<serializeString(tmp_os.str());
862 virtual void deSerialize(std::istream &is)
867 int version = readU8(is);
868 if(version != 0) throw SerializationError(
869 "unsupported CraftDefManager version");
870 u16 count = readU16(is);
871 for(u16 i=0; i<count; i++){
872 // Deserialize a string and grab a CraftDefinition from it
873 std::istringstream tmp_is(deSerializeString(is), std::ios::binary);
874 CraftDefinition *def = CraftDefinition::deSerialize(tmp_is);
880 std::vector<CraftDefinition*> m_craft_definitions;
883 IWritableCraftDefManager* createCraftDefManager()
885 return new CCraftDefManager();