`{ 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)
// 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)
{
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);
}
}
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
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
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);
}
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
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
}
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;
// 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;
}
// 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;
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;
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;
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; }
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;
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;
// 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;
// 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;
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;
// Crafting helper
bool getCraftingResult(Inventory *inv, ItemStack& result,
+ std::vector<ItemStack> &output_replacements,
bool decrementInput, IGameDef *gamedef)
{
DSTACK(__FUNCTION_NAME);
// 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());
// Crafting helper
bool getCraftingResult(Inventory *inv, ItemStack& result,
+ std::vector<ItemStack> &output_replacements,
bool decrementInput, IGameDef *gamedef);
#endif
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());
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