From 6a76c226e10e92c3e3339096f07f8ab065e2098b Mon Sep 17 00:00:00 2001 From: Kahrl Date: Thu, 12 Jan 2012 06:10:39 +0100 Subject: The huge item definition and item namespace unification patch (itemdef), see http://c55.me/minetest/wiki/doku.php?id=changes:itemdef --- src/craftdef.cpp | 844 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 744 insertions(+), 100 deletions(-) (limited to 'src/craftdef.cpp') diff --git a/src/craftdef.cpp b/src/craftdef.cpp index 0cbb74ea0..5bcbf6f94 100644 --- a/src/craftdef.cpp +++ b/src/craftdef.cpp @@ -22,88 +22,738 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes.h" #include "log.h" #include +#include #include "utility.h" #include "gamedef.h" #include "inventory.h" -#include "inventorymanager.h" // checkItemCombination -CraftPointerInput::~CraftPointerInput() + +// Deserialize an itemstring then return the name of the item +static std::string craftGetItemName(const std::string &itemstring, IGameDef *gamedef) +{ + ItemStack item; + item.deSerialize(itemstring, gamedef->idef()); + return item.name; +} + +// (mapcar craftGetItemName itemstrings) +static std::vector craftGetItemNames( + const std::vector &itemstrings, IGameDef *gamedef) +{ + std::vector result; + for(std::vector::const_iterator + i = itemstrings.begin(); + i != itemstrings.end(); i++) + { + result.push_back(craftGetItemName(*i, gamedef)); + } + return result; +} + +// Get name of each item, and return them as a new list. +static std::vector craftGetItemNames( + const std::vector &items, IGameDef *gamedef) { - for(u32 i=0; i result; + for(std::vector::const_iterator + i = items.begin(); + i != items.end(); i++) + { + result.push_back(i->name); + } + return result; } -CraftPointerInput createPointerInput(const CraftInput &ci, IGameDef *gamedef) +// Compute bounding rectangle given a matrix of items +// Returns false if every item is "" +static bool craftGetBounds(const std::vector &items, unsigned int width, + unsigned int &min_x, unsigned int &max_x, + unsigned int &min_y, unsigned int &max_y) { - std::vector items; - for(u32 i=0; i::const_iterator + i = items.begin(); + i != items.end(); i++) + { + if(*i != "") // Is this an actual item? + { + if(!success) + { + // This is the first nonempty item + min_x = max_x = x; + min_y = max_y = y; + success = true; + } + else + { + if(x < min_x) min_x = x; + if(x > max_x) max_x = x; + if(y < min_y) min_y = y; + if(y > max_y) max_y = y; + } } - items.push_back(item); + + // Step coordinate + x++; + if(x == width) + { + x = 0; + y++; + } + } + return success; +} + +// Convert a list of item names to a multiset +static std::multiset craftMakeMultiset(const std::vector &names) +{ + std::multiset set; + for(std::vector::const_iterator + i = names.begin(); + i != names.end(); i++) + { + if(*i != "") + set.insert(*i); } - return CraftPointerInput(ci.width, items); + return set; } -CraftInput createInput(const CraftPointerInput &cpi) +// Removes 1 from each item stack +static void craftDecrementInput(CraftInput &input, IGameDef *gamedef) { - std::vector items; - for(u32 i=0; iserialize(oss); - items.push_back(oss.str()); + for(std::vector::iterator + i = input.items.begin(); + i != input.items.end(); i++) + { + if(i->count != 0) + i->remove(1); + } +} + +// Removes 1 from each item stack with replacement support +// Example: if replacements contains the pair ("bucket:bucket_water", "bucket:bucket_empty"), +// a water bucket will not be removed but replaced by an empty bucket. +static void craftDecrementOrReplaceInput(CraftInput &input, + const CraftReplacements &replacements, + IGameDef *gamedef) +{ + if(replacements.pairs.empty()) + { + craftDecrementInput(input, gamedef); + return; + } + + // Make a copy of the replacements pair list + std::vector > pairs = replacements.pairs; + + for(std::vector::iterator + i = input.items.begin(); + i != input.items.end(); i++) + { + if(i->count == 1) + { + // Find an appropriate replacement + bool found_replacement = false; + for(std::vector >::iterator + j = pairs.begin(); + j != pairs.end(); j++) + { + ItemStack from_item; + from_item.deSerialize(j->first, gamedef->idef()); + if(i->name == from_item.name) + { + i->deSerialize(j->second, gamedef->idef()); + found_replacement = true; + pairs.erase(j); + break; + } + } + // No replacement was found, simply decrement count to zero + if(!found_replacement) + i->remove(1); + } + else if(i->count >= 2) + { + // Ignore replacements for items with count >= 2 + i->remove(1); + } + } +} + +// Dump an itemstring matrix +static std::string craftDumpMatrix(const std::vector &items, + unsigned int width) +{ + std::ostringstream os(std::ios::binary); + os<<"{ "; + unsigned int x = 0; + for(std::vector::const_iterator + i = items.begin(); + i != items.end(); i++, x++) + { + if(x == width) + { + os<<"; "; + x = 0; + } + else if(x != 0) + { + os<<","; + } + os<<"\""<<(*i)<<"\""; + } + os<<" }"; + return os.str(); +} + +// Dump an item matrix +std::string craftDumpMatrix(const std::vector &items, + unsigned int width) +{ + std::ostringstream os(std::ios::binary); + os<<"{ "; + unsigned int x = 0; + for(std::vector::const_iterator + i = items.begin(); + i != items.end(); i++, x++) + { + if(x == width) + { + os<<"; "; + x = 0; + } + else if(x != 0) + { + os<<","; } + os<<"\""<<(i->getItemString())<<"\""; } - return CraftInput(cpi.width, items); + os<<" }"; + return os.str(); } + +/* + CraftInput +*/ + std::string CraftInput::dump() const { std::ostringstream os(std::ios::binary); - os<<"(width="< >::const_iterator + i = pairs.begin(); + i != pairs.end(); i++) + { + os<first)<<"\"=>\""<<(i->second)<<"\""; + sep = ","; + } + os<<"}"; + return os.str(); +} + + +/* + CraftDefinition +*/ + void CraftDefinition::serialize(std::ostream &os) const { - writeU8(os, 0); // version - os<deSerializeBody(is, version); + return def; +} + +/* + CraftDefinitionShaped +*/ + +std::string CraftDefinitionShaped::getName() const +{ + return "shaped"; +} + +bool CraftDefinitionShaped::check(const CraftInput &input, IGameDef *gamedef) const +{ + if(input.method != CRAFT_METHOD_NORMAL) + return false; + + // Get input item matrix + std::vector inp_names = craftGetItemNames(input.items, gamedef); + unsigned int inp_width = input.width; + if(inp_width == 0) + return false; + while(inp_names.size() % inp_width != 0) + inp_names.push_back(""); + + // Get input bounds + unsigned int inp_min_x=0, inp_max_x=0, inp_min_y=0, inp_max_y=0; + if(!craftGetBounds(inp_names, inp_width, inp_min_x, inp_max_x, inp_min_y, inp_max_y)) + return false; // it was empty + + // Get recipe item matrix + std::vector rec_names = craftGetItemNames(recipe, gamedef); + unsigned int rec_width = width; + if(rec_width == 0) + return false; + while(rec_names.size() % rec_width != 0) + rec_names.push_back(""); + + // Get recipe bounds + unsigned int rec_min_x=0, rec_max_x=0, rec_min_y=0, rec_max_y=0; + if(!craftGetBounds(rec_names, rec_width, rec_min_x, rec_max_x, rec_min_y, rec_max_y)) + return false; // it was empty + + // Different sizes? + if(inp_max_x - inp_min_x != rec_max_x - rec_min_x) + return false; + if(inp_max_y - inp_min_y != rec_max_y - rec_min_y) + return false; + + // Verify that all item names in the bounding box are equal + unsigned int w = inp_max_x - inp_min_x + 1; + unsigned int h = inp_max_y - inp_min_y + 1; + for(unsigned int y=0; y inp_names = craftGetItemNames(input.items, gamedef); + std::multiset inp_names_multiset = craftMakeMultiset(inp_names); + + // Get recipe item multiset + std::vector rec_names = craftGetItemNames(recipe, gamedef); + std::multiset rec_names_multiset = craftMakeMultiset(rec_names); + + // Recipe is matched when the multisets coincide + return inp_names_multiset == rec_names_multiset; +} + +CraftOutput CraftDefinitionShapeless::getOutput(const CraftInput &input, IGameDef *gamedef) const +{ + return CraftOutput(output, 0); +} + +void CraftDefinitionShapeless::decrementInput(CraftInput &input, IGameDef *gamedef) const +{ + craftDecrementOrReplaceInput(input, replacements, gamedef); +} + +std::string CraftDefinitionShapeless::dump() const +{ + std::ostringstream os(std::ios::binary); + os<<"(shapeless, output=\""<idef(); + if(item1.count != 1 || item2.count != 1 || item1.name != item2.name + || idef->get(item1.name).type != ITEM_TOOL + || idef->get(item2.name).type != ITEM_TOOL) + { + // Failure + return ItemStack(); + } + + s32 item1_uses = 65536 - (u32) item1.wear; + s32 item2_uses = 65536 - (u32) item2.wear; + s32 new_uses = item1_uses + item2_uses; + s32 new_wear = 65536 - new_uses + floor(additional_wear * 65536 + 0.5); + if(new_wear >= 65536) + return ItemStack(); + if(new_wear < 0) + new_wear = 0; + + ItemStack repaired = item1; + repaired.wear = new_wear; + return repaired; +} + +std::string CraftDefinitionToolRepair::getName() const +{ + return "toolrepair"; +} + +bool CraftDefinitionToolRepair::check(const CraftInput &input, IGameDef *gamedef) const +{ + if(input.method != CRAFT_METHOD_NORMAL) + return false; + + ItemStack item1; + ItemStack item2; + for(std::vector::const_iterator + i = input.items.begin(); + i != input.items.end(); i++) + { + if(!i->empty()) + { + if(item1.empty()) + item1 = *i; + else if(item2.empty()) + item2 = *i; + else + return false; + } + } + ItemStack repaired = craftToolRepair(item1, item2, additional_wear, gamedef); + return !repaired.empty(); +} + +CraftOutput CraftDefinitionToolRepair::getOutput(const CraftInput &input, IGameDef *gamedef) const +{ + ItemStack item1; + ItemStack item2; + for(std::vector::const_iterator + i = input.items.begin(); + i != input.items.end(); i++) + { + if(!i->empty()) + { + if(item1.empty()) + item1 = *i; + else if(item2.empty()) + item2 = *i; + } + } + ItemStack repaired = craftToolRepair(item1, item2, additional_wear, gamedef); + return CraftOutput(repaired.getItemString(), 0); +} + +void CraftDefinitionToolRepair::decrementInput(CraftInput &input, IGameDef *gamedef) const +{ + craftDecrementInput(input, gamedef); +} + +std::string CraftDefinitionToolRepair::dump() const +{ + std::ostringstream os(std::ios::binary); + os<<"(toolrepair, additional_wear="< inp_names = craftGetItemNames(input.items, gamedef); + std::multiset inp_names_multiset = craftMakeMultiset(inp_names); + + // Get recipe item multiset + std::multiset rec_names_multiset; + rec_names_multiset.insert(craftGetItemName(recipe, gamedef)); + + // Recipe is matched when the multisets coincide + return inp_names_multiset == rec_names_multiset; +} + +CraftOutput CraftDefinitionCooking::getOutput(const CraftInput &input, IGameDef *gamedef) const +{ + return CraftOutput(output, cooktime); +} + +void CraftDefinitionCooking::decrementInput(CraftInput &input, IGameDef *gamedef) const +{ + craftDecrementInput(input, gamedef); +} + +std::string CraftDefinitionCooking::dump() const +{ + std::ostringstream os(std::ios::binary); + os<<"(cooking, output=\""< inp_names = craftGetItemNames(input.items, gamedef); + std::multiset inp_names_multiset = craftMakeMultiset(inp_names); + + // Get recipe item multiset + std::multiset rec_names_multiset; + rec_names_multiset.insert(craftGetItemName(recipe, gamedef)); + + // Recipe is matched when the multisets coincide + return inp_names_multiset == rec_names_multiset; +} + +CraftOutput CraftDefinitionFuel::getOutput(const CraftInput &input, IGameDef *gamedef) const +{ + return CraftOutput("", burntime); +} + +void CraftDefinitionFuel::decrementInput(CraftInput &input, IGameDef *gamedef) const +{ + craftDecrementInput(input, gamedef); +} + +std::string CraftDefinitionFuel::dump() const +{ + std::ostringstream os(std::ios::binary); + os<<"(fuel, recipe=\""< 3){ - errorstream<<"getCraftResult(): ERROR: " - <<"input_cpi.width > 3; Failing to craft."<::const_iterator + i = input.items.begin(); + i != input.items.end(); i++) { - u32 i=y*3+x; - if(x >= input_cpi.width || y >= input_cpi.height()) - input_items[i] = NULL; - else - input_items[i] = input_cpi.items[y*input_cpi.width+x]; + if(!i->empty()) + { + all_empty = false; + break; + } } - for(core::list::ConstIterator - i = m_craft_definitions.begin(); - i != m_craft_definitions.end(); i++) + if(all_empty) + return false; + + // Walk crafting definitions from back to front, so that later + // definitions can override earlier ones. + for(std::vector::const_reverse_iterator + i = m_craft_definitions.rbegin(); + i != m_craft_definitions.rend(); i++) { CraftDefinition *def = *i; - /*infostream<<"Checking "<input.dump() - <<" (output=\""<output<<"\")"<dump()<input, gamedef); - if(spec_cpi.width > 3){ - errorstream<<"getCraftResult: ERROR: " - <<"spec_cpi.width > 3 in recipe " - <dump()<check(input, gamedef)) { - u32 i=y*3+x; - if(x >= spec_cpi.width || y >= spec_cpi.height()) - spec_items[i] = NULL; - else - spec_items[i] = spec_cpi.items[y*spec_cpi.width+x]; - } - - bool match = checkItemCombination(input_items, spec_items); - - if(match){ - std::istringstream iss(def->output, std::ios::binary); - return InventoryItem::deSerialize(iss, gamedef); + // Get output, then decrement input (if requested) + output = def->getOutput(input, gamedef); + if(decrementInput) + def->decrementInput(input, gamedef); + return true; } } catch(SerializationError &e) @@ -173,34 +811,41 @@ public: // then go on with the next craft definition } } - return NULL; + return false; } - virtual void registerCraft(const CraftDefinition &def) + virtual std::string dump() const { - infostream<<"registerCraft: registering craft definition: " - < 3 || def.input.height() > 3){ - errorstream<<"registerCraft: input size is larger than 3x3," - <<" ignoring"<::const_iterator + i = m_craft_definitions.begin(); + i != m_craft_definitions.end(); i++) + { + os<<(*i)->dump()<<"\n"; } - m_craft_definitions.push_back(new CraftDefinition(def)); + return os.str(); + } + virtual void registerCraft(CraftDefinition *def) + { + infostream<<"registerCraft: registering craft definition: " + <dump()<::Iterator + for(std::vector::iterator i = m_craft_definitions.begin(); i != m_craft_definitions.end(); i++){ delete *i; } m_craft_definitions.clear(); } - virtual void serialize(std::ostream &os) + virtual void serialize(std::ostream &os) const { writeU8(os, 0); // version u16 count = m_craft_definitions.size(); writeU16(os, count); - for(core::list::Iterator + for(std::vector::const_iterator i = m_craft_definitions.begin(); i != m_craft_definitions.end(); i++){ CraftDefinition *def = *i; @@ -222,14 +867,13 @@ public: for(u16 i=0; i m_craft_definitions; + std::vector m_craft_definitions; }; IWritableCraftDefManager* createCraftDefManager() -- cgit v1.2.3