From 17ba584fe254eeaee3489cc20e03810a59f3ef9b Mon Sep 17 00:00:00 2001 From: TeTpaAka Date: Tue, 2 Jun 2015 20:30:04 +0200 Subject: [PATCH] Fix bug when craft input isn't replaced --- doc/lua_api.txt | 2 + src/craftdef.cpp | 59 ++++++++++++--------- src/craftdef.h | 20 +++++--- src/inventorymanager.cpp | 93 ++++++++++++++++++++++++---------- src/inventorymanager.h | 1 + src/script/lua_api/l_craft.cpp | 9 +++- src/server.cpp | 3 +- 7 files changed, 128 insertions(+), 59 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index d2505284a..95b514a92 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2077,6 +2077,8 @@ and `minetest.auth_reload` call the authetification handler. `{ stack1, stack2, stack3, stack4, stack 5, stack 6, stack 7, stack 8, stack 9 }` * `output.item` = `ItemStack`, if unsuccessful: empty `ItemStack` * `output.time` = a number, if unsuccessful: `0` + * `output.replacements` = list of `ItemStack`s that couldn't be placed in + `decremented_input.items` * `decremented_input` = like `input` * `minetest.get_craft_recipe(output)`: returns input * returns last registered recipe for output item (node) diff --git a/src/craftdef.cpp b/src/craftdef.cpp index 8c0e91690..cc0e83be7 100644 --- a/src/craftdef.cpp +++ b/src/craftdef.cpp @@ -199,6 +199,7 @@ static void craftDecrementInput(CraftInput &input, IGameDef *gamedef) // 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, + std::vector &output_replacements, const CraftReplacements &replacements, IGameDef *gamedef) { @@ -213,26 +214,30 @@ static void craftDecrementOrReplaceInput(CraftInput &input, for (std::vector::iterator it = input.items.begin(); it != input.items.end(); it++) { - if (it->count == 1) { - // Find an appropriate replacement - bool found_replacement = false; - for (std::vector >::iterator - j = pairs.begin(); - j != pairs.end(); j++) { - if (it->name == craftGetItemName(j->first, gamedef)) { + // Find an appropriate replacement + bool found_replacement = false; + for (std::vector >::iterator + j = pairs.begin(); + j != pairs.end(); j++) { + if (it->name == craftGetItemName(j->first, gamedef)) { + if (it->count == 1) { it->deSerialize(j->second, gamedef->idef()); found_replacement = true; pairs.erase(j); break; + } else { + ItemStack rep; + rep.deSerialize(j->second, gamedef->idef()); + it->remove(1); + found_replacement = true; + output_replacements.push_back(rep); + break; } } - // No replacement was found, simply decrement count to zero - if (!found_replacement) - it->remove(1); - } else if (it->count >= 2) { - // Ignore replacements for items with count >= 2 - it->remove(1); } + // No replacement was found, simply decrement count to zero + if (!found_replacement) + it->remove(1); } } @@ -408,9 +413,10 @@ CraftInput CraftDefinitionShaped::getInput(const CraftOutput &output, IGameDef * return CraftInput(CRAFT_METHOD_NORMAL,width,craftGetItems(recipe,gamedef)); } -void CraftDefinitionShaped::decrementInput(CraftInput &input, IGameDef *gamedef) const +void CraftDefinitionShaped::decrementInput(CraftInput &input, std::vector &output_replacements, + IGameDef *gamedef) const { - craftDecrementOrReplaceInput(input, replacements, gamedef); + craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef); } CraftHashType CraftDefinitionShaped::getHashType() const @@ -529,9 +535,10 @@ CraftInput CraftDefinitionShapeless::getInput(const CraftOutput &output, IGameDe return CraftInput(CRAFT_METHOD_NORMAL, 0, craftGetItems(recipe, gamedef)); } -void CraftDefinitionShapeless::decrementInput(CraftInput &input, IGameDef *gamedef) const +void CraftDefinitionShapeless::decrementInput(CraftInput &input, std::vector &output_replacements, + IGameDef *gamedef) const { - craftDecrementOrReplaceInput(input, replacements, gamedef); + craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef); } CraftHashType CraftDefinitionShapeless::getHashType() const @@ -661,7 +668,8 @@ CraftInput CraftDefinitionToolRepair::getInput(const CraftOutput &output, IGameD return CraftInput(CRAFT_METHOD_COOKING, additional_wear, stack); } -void CraftDefinitionToolRepair::decrementInput(CraftInput &input, IGameDef *gamedef) const +void CraftDefinitionToolRepair::decrementInput(CraftInput &input, std::vector &output_replacements, + IGameDef *gamedef) const { craftDecrementInput(input, gamedef); } @@ -720,9 +728,10 @@ CraftInput CraftDefinitionCooking::getInput(const CraftOutput &output, IGameDef return CraftInput(CRAFT_METHOD_COOKING,cooktime,craftGetItems(rec,gamedef)); } -void CraftDefinitionCooking::decrementInput(CraftInput &input, IGameDef *gamedef) const +void CraftDefinitionCooking::decrementInput(CraftInput &input, std::vector &output_replacements, + IGameDef *gamedef) const { - craftDecrementOrReplaceInput(input, replacements, gamedef); + craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef); } CraftHashType CraftDefinitionCooking::getHashType() const @@ -811,9 +820,10 @@ CraftInput CraftDefinitionFuel::getInput(const CraftOutput &output, IGameDef *ga return CraftInput(CRAFT_METHOD_COOKING,(int)burntime,craftGetItems(rec,gamedef)); } -void CraftDefinitionFuel::decrementInput(CraftInput &input, IGameDef *gamedef) const +void CraftDefinitionFuel::decrementInput(CraftInput &input, std::vector &output_replacements, + IGameDef *gamedef) const { - craftDecrementOrReplaceInput(input, replacements, gamedef); + craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef); } CraftHashType CraftDefinitionFuel::getHashType() const @@ -871,7 +881,8 @@ public: } virtual bool getCraftResult(CraftInput &input, CraftOutput &output, - bool decrementInput, IGameDef *gamedef) const + std::vector &output_replacement, bool decrementInput, + IGameDef *gamedef) const { output.item = ""; output.time = 0; @@ -922,7 +933,7 @@ public: // Get output, then decrement input (if requested) output = def->getOutput(input, gamedef); if (decrementInput) - def->decrementInput(input, gamedef); + def->decrementInput(input, output_replacement, gamedef); /*errorstream << "Check RETURNS TRUE" << std::endl;*/ return true; } diff --git a/src/craftdef.h b/src/craftdef.h index f62ad3313..cebb2d7ae 100644 --- a/src/craftdef.h +++ b/src/craftdef.h @@ -150,7 +150,8 @@ public: // the inverse of the above virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const=0; // Decreases count of every input item - virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const=0; + virtual void decrementInput(CraftInput &input, + std::vector &output_replacements, IGameDef *gamedef) const=0; virtual CraftHashType getHashType() const = 0; virtual u64 getHash(CraftHashType type) const = 0; @@ -187,7 +188,8 @@ public: virtual bool check(const CraftInput &input, IGameDef *gamedef) const; virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const; virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const; - virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const; + virtual void decrementInput(CraftInput &input, + std::vector &output_replacements, IGameDef *gamedef) const; virtual CraftHashType getHashType() const; virtual u64 getHash(CraftHashType type) const; @@ -235,7 +237,8 @@ public: virtual bool check(const CraftInput &input, IGameDef *gamedef) const; virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const; virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const; - virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const; + virtual void decrementInput(CraftInput &input, + std::vector &output_replacements, IGameDef *gamedef) const; virtual CraftHashType getHashType() const; virtual u64 getHash(CraftHashType type) const; @@ -278,7 +281,8 @@ public: virtual bool check(const CraftInput &input, IGameDef *gamedef) const; virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const; virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const; - virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const; + virtual void decrementInput(CraftInput &input, + std::vector &output_replacements, IGameDef *gamedef) const; virtual CraftHashType getHashType() const { return CRAFT_HASH_TYPE_COUNT; } virtual u64 getHash(CraftHashType type) const { return 2; } @@ -320,7 +324,8 @@ public: virtual bool check(const CraftInput &input, IGameDef *gamedef) const; virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const; virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const; - virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const; + virtual void decrementInput(CraftInput &input, + std::vector &output_replacements, IGameDef *gamedef) const; virtual CraftHashType getHashType() const; virtual u64 getHash(CraftHashType type) const; @@ -365,7 +370,8 @@ public: virtual bool check(const CraftInput &input, IGameDef *gamedef) const; virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const; virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const; - virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const; + virtual void decrementInput(CraftInput &input, + std::vector &output_replacements, IGameDef *gamedef) const; virtual CraftHashType getHashType() const; virtual u64 getHash(CraftHashType type) const; @@ -398,6 +404,7 @@ public: // The main crafting function virtual bool getCraftResult(CraftInput &input, CraftOutput &output, + std::vector &output_replacements, bool decrementInput, IGameDef *gamedef) const=0; virtual std::vector getCraftRecipes(CraftOutput &output, IGameDef *gamedef, unsigned limit=0) const=0; @@ -414,6 +421,7 @@ public: // The main crafting function virtual bool getCraftResult(CraftInput &input, CraftOutput &output, + std::vector &output_replacements, bool decrementInput, IGameDef *gamedef) const=0; virtual std::vector getCraftRecipes(CraftOutput &output, IGameDef *gamedef, unsigned limit=0) const=0; diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index c3a9576f9..640e3395b 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -691,72 +691,112 @@ ICraftAction::ICraftAction(std::istream &is) craft_inv.deSerialize(ts); } -void ICraftAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef) +void ICraftAction::apply(InventoryManager *mgr, + ServerActiveObject *player, IGameDef *gamedef) { Inventory *inv_craft = mgr->getInventory(craft_inv); - if(!inv_craft){ - infostream<<"ICraftAction::apply(): FAIL: inventory not found: " - <<"craft_inv=\""<getList("craft"); InventoryList *list_craftresult = inv_craft->getList("craftresult"); + InventoryList *list_main = inv_craft->getList("main"); /* If a list doesn't exist or the source item doesn't exist */ - if(!list_craft){ - infostream<<"ICraftAction::apply(): FAIL: craft list not found: " - <<"craft_inv=\""<getSize() < 1){ - infostream<<"ICraftAction::apply(): FAIL: craftresult list too short: " - <<"craft_inv=\""<getSize() < 1) { + infostream << "ICraftAction::apply(): FAIL: craftresult list too short: " + << "craft_inv=\"" << craft_inv.dump() << "\"" << std::endl; return; } ItemStack crafted; ItemStack craftresultitem; int count_remaining = count; - getCraftingResult(inv_craft, crafted, false, gamedef); + std::vector output_replacements; + getCraftingResult(inv_craft, crafted, output_replacements, false, gamedef); PLAYER_TO_SA(player)->item_CraftPredict(crafted, player, list_craft, craft_inv); bool found = !crafted.empty(); - while(found && list_craftresult->itemFits(0, crafted)) - { + while (found && list_craftresult->itemFits(0, crafted)) { InventoryList saved_craft_list = *list_craft; - + + std::vector temp; // Decrement input and add crafting output - getCraftingResult(inv_craft, crafted, true, gamedef); + getCraftingResult(inv_craft, crafted, temp, true, gamedef); PLAYER_TO_SA(player)->item_OnCraft(crafted, player, &saved_craft_list, craft_inv); list_craftresult->addItem(0, crafted); mgr->setInventoryModified(craft_inv); - actionstream<getDescription() - <<" crafts " - <getItemDefManager(); + for (std::vector::iterator it = temp.begin(); + it != temp.end(); it++) { + for (std::vector::iterator jt = output_replacements.begin(); + jt != output_replacements.end(); jt++) { + if (it->name == jt->name) { + *it = jt->addItem(*it, itemdef); + if (it->empty()) + continue; + } + } + output_replacements.push_back(*it); + } + + actionstream << player->getDescription() + << " crafts " + << crafted.getItemString() + << std::endl; // Decrement counter - if(count_remaining == 1) + if (count_remaining == 1) break; - else if(count_remaining > 1) + else if (count_remaining > 1) count_remaining--; // Get next crafting result - found = getCraftingResult(inv_craft, crafted, false, gamedef); + found = getCraftingResult(inv_craft, crafted, temp, false, gamedef); PLAYER_TO_SA(player)->item_CraftPredict(crafted, player, list_craft, craft_inv); found = !crafted.empty(); } + // Put the replacements in the inventory or drop them on the floor, if + // the invenotry is full + for (std::vector::iterator it = output_replacements.begin(); + it != output_replacements.end(); it++) { + if (list_main) + *it = list_main->addItem(*it); + if (it->empty()) + continue; + u16 count = it->count; + do { + PLAYER_TO_SA(player)->item_OnDrop(*it, player, + player->getBasePosition() + v3f(0,1,0)); + if (count >= it->count) { + errorstream << "Couldn't drop replacement stack " << + it->getItemString() << " because drop loop didn't " + "decrease count." << std::endl; + + break; + } + } while (!it->empty()); + } + infostream<<"ICraftAction::apply(): crafted " <<" craft_inv=\""< &output_replacements, bool decrementInput, IGameDef *gamedef) { DSTACK(__FUNCTION_NAME); @@ -792,7 +833,7 @@ bool getCraftingResult(Inventory *inv, ItemStack& result, // Find out what is crafted and add it to result item slot CraftOutput co; bool found = gamedef->getCraftDefManager()->getCraftResult( - ci, co, decrementInput, gamedef); + ci, co, output_replacements, decrementInput, gamedef); if(found) result.deSerialize(co.item, gamedef->getItemDefManager()); diff --git a/src/inventorymanager.h b/src/inventorymanager.h index 39cc6e50f..a255e979a 100644 --- a/src/inventorymanager.h +++ b/src/inventorymanager.h @@ -242,6 +242,7 @@ struct ICraftAction : public InventoryAction // Crafting helper bool getCraftingResult(Inventory *inv, ItemStack& result, + std::vector &output_replacements, bool decrementInput, IGameDef *gamedef); #endif diff --git a/src/script/lua_api/l_craft.cpp b/src/script/lua_api/l_craft.cpp index b321fb32f..f59487727 100644 --- a/src/script/lua_api/l_craft.cpp +++ b/src/script/lua_api/l_craft.cpp @@ -303,18 +303,23 @@ int ModApiCraft::l_get_craft_result(lua_State *L) ICraftDefManager *cdef = gdef->cdef(); CraftInput input(method, width, items); CraftOutput output; - bool got = cdef->getCraftResult(input, output, true, gdef); + std::vector output_replacements; + bool got = cdef->getCraftResult(input, output, output_replacements, true, gdef); lua_newtable(L); // output table - if(got){ + if (got) { ItemStack item; item.deSerialize(output.item, gdef->idef()); LuaItemStack::create(L, item); lua_setfield(L, -2, "item"); setintfield(L, -1, "time", output.time); + push_items(L, output_replacements); + lua_setfield(L, -2, "replacements"); } else { LuaItemStack::create(L, ItemStack()); lua_setfield(L, -2, "item"); setintfield(L, -1, "time", 0); + lua_newtable(L); + lua_setfield(L, -2, "replacements"); } lua_newtable(L); // decremented input table lua_pushstring(L, method_s.c_str()); diff --git a/src/server.cpp b/src/server.cpp index 7cd92ed65..52bc9867a 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2698,7 +2698,8 @@ void Server::UpdateCrafting(Player* player) ItemStack preview; InventoryLocation loc; loc.setPlayer(player->getName()); - getCraftingResult(&player->inventory, preview, false, this); + std::vector output_replacements; + getCraftingResult(&player->inventory, preview, output_replacements, false, this); m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(), (&player->inventory)->getList("craft"), loc); // Put the new preview in