diff --git a/builtin/misc_register.lua b/builtin/misc_register.lua index f1d816124..aa8399f16 100644 --- a/builtin/misc_register.lua +++ b/builtin/misc_register.lua @@ -230,6 +230,20 @@ function minetest.register_biome(biome) register_biome_raw(biome) end +function minetest.on_craft(itemstack, player, old_craft_list, craft_inv) + for _, func in ipairs(minetest.registered_on_crafts) do + itemstack = func(itemstack, player, old_craft_list, craft_inv) or itemstack + end + return itemstack +end + +function minetest.craft_predict(itemstack, player, old_craft_list, craft_inv) + for _, func in ipairs(minetest.registered_craft_predicts) do + itemstack = func(itemstack, player, old_craft_list, craft_inv) or itemstack + end + return itemstack +end + -- Alias the forbidden item names to "" so they can't be -- created via itemstrings (e.g. /give) local name @@ -327,4 +341,6 @@ minetest.registered_on_joinplayers, minetest.register_on_joinplayer = make_regis minetest.registered_on_leaveplayers, minetest.register_on_leaveplayer = make_registration() minetest.registered_on_player_receive_fields, minetest.register_on_player_receive_fields = make_registration_reverse() minetest.registered_on_cheats, minetest.register_on_cheat = make_registration() +minetest.registered_on_crafts, minetest.register_on_craft = make_registration() +minetest.registered_craft_predicts, minetest.register_craft_predict = make_registration() diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 5a0d5817f..e94fd30a9 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1145,6 +1145,15 @@ minetest.register_on_player_receive_fields(func(player, formname, fields)) minetest.register_on_mapgen_init(func(MapgenParams)) ^ Called just before the map generator is initialized but before the environment is initialized ^ MapgenParams consists of a table with the fields mgname, seed, water_level, and flags +minetest.register_on_craft(func(itemstack, player, old_craft_grid, craft_inv)) +^ Called when player crafts something +^ itemstack is the output +^ old_craft_grid contains the recipe (Note: the one in the inventory is cleared) +^ craft_inv is the inventory with the crafting grid +^ Return either an ItemStack, to replace the output, or nil, to not modify it +minetest.register_craft_predict(func(itemstack, player, old_craft_grid, craft_inv)) +^ The same as before, except that it is called before the player crafts, to make +^ craft prediction, and it should not change anything. Other registration functions: minetest.register_chatcommand(cmd, chatcommand definition) diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index c81f7a19e..1c511e9cb 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -724,13 +724,19 @@ void ICraftAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGam } ItemStack crafted; + ItemStack craftresultitem; int count_remaining = count; bool found = getCraftingResult(inv_craft, crafted, false, gamedef); + PLAYER_TO_SA(player)->item_CraftPredict(crafted, player, list_craft, craft_inv); + found = !crafted.empty(); while(found && list_craftresult->itemFits(0, crafted)) { + InventoryList saved_craft_list = *list_craft; + // Decrement input and add crafting output getCraftingResult(inv_craft, crafted, true, gamedef); + PLAYER_TO_SA(player)->item_OnCraft(crafted, player, &saved_craft_list, craft_inv); list_craftresult->addItem(0, crafted); mgr->setInventoryModified(craft_inv); @@ -747,6 +753,8 @@ void ICraftAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGam // Get next crafting result found = getCraftingResult(inv_craft, crafted, false, gamedef); + PLAYER_TO_SA(player)->item_CraftPredict(crafted, player, list_craft, craft_inv); + found = !crafted.empty(); } infostream<<"ICraftAction::apply(): crafted " diff --git a/src/script/cpp_api/s_item.cpp b/src/script/cpp_api/s_item.cpp index b4536ac63..1d5f218cf 100644 --- a/src/script/cpp_api/s_item.cpp +++ b/src/script/cpp_api/s_item.cpp @@ -22,9 +22,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_converter.h" #include "common/c_content.h" #include "lua_api/l_item.h" +#include "lua_api/l_inventory.h" #include "server.h" #include "log.h" #include "util/pointedthing.h" +#include "inventory.h" +#include "inventorymanager.h" bool ScriptApiItem::item_OnDrop(ItemStack &item, ServerActiveObject *dropper, v3f pos) @@ -86,6 +89,54 @@ bool ScriptApiItem::item_OnUse(ItemStack &item, return true; } +bool ScriptApiItem::item_OnCraft(ItemStack &item, ServerActiveObject *user, + const InventoryList *old_craft_grid, const InventoryLocation &craft_inv) +{ + SCRIPTAPI_PRECHECKHEADER + + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "on_craft"); + LuaItemStack::create(L, item); + objectrefGetOrCreate(user); + + //Push inventory list + std::vector items; + for(u32 i=0; igetSize(); i++) + items.push_back(old_craft_grid->getItem(i)); + push_items(L, items); + + InvRef::create(L, craft_inv); + if(lua_pcall(L, 4, 1, 0)) + scriptError("error: %s", lua_tostring(L, -1)); + if(!lua_isnil(L, -1)) + item = read_item(L,-1, getServer()); + return true; +} + +bool ScriptApiItem::item_CraftPredict(ItemStack &item, ServerActiveObject *user, + const InventoryList *old_craft_grid, const InventoryLocation &craft_inv) +{ + SCRIPTAPI_PRECHECKHEADER + + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "craft_predict"); + LuaItemStack::create(L, item); + objectrefGetOrCreate(user); + + //Push inventory list + std::vector items; + for(u32 i=0; igetSize(); i++) + items.push_back(old_craft_grid->getItem(i)); + push_items(L, items); + + InvRef::create(L, craft_inv); + if(lua_pcall(L, 4, 1, 0)) + scriptError("error: %s", lua_tostring(L, -1)); + if(!lua_isnil(L, -1)) + item = read_item(L,-1, getServer()); + return true; +} + // Retrieves minetest.registered_items[name][callbackname] // If that is nil or on error, return false and stack is unchanged // If that is a function, returns true and pushes the diff --git a/src/script/cpp_api/s_item.h b/src/script/cpp_api/s_item.h index 0f2b16042..4964dd5b4 100644 --- a/src/script/cpp_api/s_item.h +++ b/src/script/cpp_api/s_item.h @@ -29,6 +29,8 @@ class ServerActiveObject; struct ItemDefinition; class LuaItemStack; class ModApiItemMod; +class InventoryList; +class InventoryLocation; class ScriptApiItem : virtual public ScriptApiBase @@ -40,6 +42,10 @@ public: ServerActiveObject *placer, const PointedThing &pointed); bool item_OnUse(ItemStack &item, ServerActiveObject *user, const PointedThing &pointed); + bool item_OnCraft(ItemStack &item, ServerActiveObject *user, + const InventoryList *old_craft_grid, const InventoryLocation &craft_inv); + bool item_CraftPredict(ItemStack &item, ServerActiveObject *user, + const InventoryList *old_craft_grid, const InventoryLocation &craft_inv); protected: friend class LuaItemStack; diff --git a/src/server.cpp b/src/server.cpp index 8350b7013..c29ec3d83 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -4775,7 +4775,10 @@ void Server::UpdateCrafting(u16 peer_id) // Get a preview for crafting ItemStack preview; + InventoryLocation loc; + loc.setPlayer(player->getName()); getCraftingResult(&player->inventory, preview, false, this); + m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(), (&player->inventory)->getList("craft"), loc); // Put the new preview in InventoryList *plist = player->inventory.getList("craftpreview");