Prechádzať zdrojové kódy

Fix bug when craft input isn't replaced

TeTpaAka 9 rokov pred
rodič
commit
17ba584fe2

+ 2 - 0
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)

+ 35 - 24
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<ItemStack> &output_replacements,
 		const CraftReplacements &replacements,
 		IGameDef *gamedef)
 {
@@ -213,26 +214,30 @@ static void craftDecrementOrReplaceInput(CraftInput &input,
 	for (std::vector<ItemStack>::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<std::pair<std::string, std::string> >::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<std::pair<std::string, std::string> >::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<ItemStack> &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<ItemStack> &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<ItemStack> &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<ItemStack> &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<ItemStack> &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<ItemStack> &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;
 				}

+ 14 - 6
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<ItemStack> &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<ItemStack> &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<ItemStack> &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<ItemStack> &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<ItemStack> &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<ItemStack> &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<ItemStack> &output_replacements,
 			bool decrementInput, IGameDef *gamedef) const=0;
 	virtual std::vector<CraftDefinition*> 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<ItemStack> &output_replacements,
 			bool decrementInput, IGameDef *gamedef) const=0;
 	virtual std::vector<CraftDefinition*> getCraftRecipes(CraftOutput &output,
 			IGameDef *gamedef, unsigned limit=0) const=0;

+ 67 - 26
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=\""<<craft_inv.dump()<<"\""<<std::endl;
+	if (!inv_craft) {
+		infostream << "ICraftAction::apply(): FAIL: inventory not found: "
+				<< "craft_inv=\"" << craft_inv.dump() << "\"" << std::endl;
 		return;
 	}
 
 	InventoryList *list_craft = inv_craft->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=\""<<craft_inv.dump()<<"\""<<std::endl;
+	if (!list_craft) {
+		infostream << "ICraftAction::apply(): FAIL: craft list not found: "
+				<< "craft_inv=\"" << craft_inv.dump() << "\"" << std::endl;
 		return;
 	}
-	if(!list_craftresult){
-		infostream<<"ICraftAction::apply(): FAIL: craftresult list not found: "
-				<<"craft_inv=\""<<craft_inv.dump()<<"\""<<std::endl;
+	if (!list_craftresult) {
+		infostream << "ICraftAction::apply(): FAIL: craftresult list not found: "
+				<< "craft_inv=\"" << craft_inv.dump() << "\"" << std::endl;
 		return;
 	}
-	if(list_craftresult->getSize() < 1){
-		infostream<<"ICraftAction::apply(): FAIL: craftresult list too short: "
-				<<"craft_inv=\""<<craft_inv.dump()<<"\""<<std::endl;
+	if (list_craftresult->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<ItemStack> 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<ItemStack> 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<<player->getDescription()
-				<<" crafts "
-				<<crafted.getItemString()
-				<<std::endl;
+		// Add the new replacements to the list
+		IItemDefManager *itemdef = gamedef->getItemDefManager();
+		for (std::vector<ItemStack>::iterator it = temp.begin();
+				it != temp.end(); it++) {
+			for (std::vector<ItemStack>::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<ItemStack>::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=\""<<craft_inv.dump()<<"\""
 			<<std::endl;
@@ -771,6 +811,7 @@ void ICraftAction::clientApply(InventoryManager *mgr, IGameDef *gamedef)
 
 // Crafting helper
 bool getCraftingResult(Inventory *inv, ItemStack& result,
+		std::vector<ItemStack> &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());
 

+ 1 - 0
src/inventorymanager.h

@@ -242,6 +242,7 @@ struct ICraftAction : public InventoryAction
 
 // Crafting helper
 bool getCraftingResult(Inventory *inv, ItemStack& result,
+		std::vector<ItemStack> &output_replacements,
 		bool decrementInput, IGameDef *gamedef);
 
 #endif

+ 7 - 2
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<ItemStack> 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());

+ 2 - 1
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<ItemStack> 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