diff options
-rw-r--r-- | builtin/item.lua | 41 | ||||
-rw-r--r-- | doc/lua_api.txt | 31 | ||||
-rw-r--r-- | src/guiInventoryMenu.cpp | 23 | ||||
-rw-r--r-- | src/inventorymanager.cpp | 91 | ||||
-rw-r--r-- | src/scriptapi.cpp | 97 | ||||
-rw-r--r-- | src/scriptapi.h | 16 |
6 files changed, 275 insertions, 24 deletions
diff --git a/builtin/item.lua b/builtin/item.lua index d30b439aa..eb8c556de 100644 --- a/builtin/item.lua +++ b/builtin/item.lua @@ -262,6 +262,41 @@ function minetest.node_dig(pos, node, digger) end end +function minetest.node_metadata_inventory_move_allow_all(pos, from_list, + from_index, to_list, to_index, count, player) + minetest.log("verbose", "node_metadata_inventory_move_allow_all") + local meta = minetest.env:get_meta(pos) + local inv = meta:get_inventory() + + local from_stack = inv:get_stack(from_list, from_index) + local taken_items = from_stack:take_item(count) + inv:set_stack(from_list, from_index, from_stack) + + local to_stack = inv:get_stack(to_list, to_index) + to_stack:add_item(taken_items) + inv:set_stack(to_list, to_index, to_stack) +end + +function minetest.node_metadata_inventory_offer_allow_all(pos, listname, index, stack, player) + minetest.log("verbose", "node_metadata_inventory_offer_allow_all") + local meta = minetest.env:get_meta(pos) + local inv = meta:get_inventory() + local the_stack = inv:get_stack(listname, index) + the_stack:add_item(stack) + inv:set_stack(listname, index, the_stack) + return ItemStack("") +end + +function minetest.node_metadata_inventory_take_allow_all(pos, listname, index, count, player) + minetest.log("verbose", "node_metadata_inventory_take_allow_all") + local meta = minetest.env:get_meta(pos) + local inv = meta:get_inventory() + local the_stack = inv:get_stack(listname, index) + local taken_items = the_stack:take_item(count) + inv:set_stack(listname, index, the_stack) + return taken_items +end + -- This is used to allow mods to redefine minetest.item_place and so on local function redef_wrapper(table, name) return function(...) @@ -295,6 +330,12 @@ minetest.nodedef_default = { on_punch = redef_wrapper(minetest, 'node_punch'), -- minetest.node_punch on_dig = redef_wrapper(minetest, 'node_dig'), -- minetest.node_dig + on_receive_fields = nil, + + on_metadata_inventory_move = minetest.node_metadata_inventory_move_allow_all, + on_metadata_inventory_offer = minetest.node_metadata_inventory_offer_allow_all, + on_metadata_inventory_take = minetest.node_metadata_inventory_take_allow_all, + -- Node properties drawtype = "normal", visual_scale = 1.0, diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 73d7b3641..f8615b130 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1039,7 +1039,38 @@ Node definition (register_node) on_receive_fields = func(pos, formname, fields, sender), ^ fields = {name1 = value1, name2 = value2, ...} + ^ Called when an UI form (eg. sign text input) returns data ^ default: nil + + on_metadata_inventory_move = func(pos, from_list, from_index, + to_list, to_index, count, player), + ^ Called when a player wants to move items inside the metadata + ^ Should move items, or some items, if permitted. If not, should do + nothing. + ^ The engine ensures the action is valid, i.e. the stack fits at the + given position + ^ default: minetest.node_metadata_inventory_move_allow_all + + on_metadata_inventory_offer = func(pos, listname, index, stack, player), + ^ Called when a player wants to put something into the metadata + inventory + ^ Should check if the action is permitted (the engine ensures the + action is valid, i.e. the stack fits at the given position) + ^ If permitted, modify the metadata inventory and return the + "leftover" stack (normally nil). + ^ If not permitted, return itemstack. + ^ default: minetest.node_metadata_inventory_offer_allow_all + + on_metadata_inventory_take = func(pos, listname, index, count, player), + ^ Called when a player wants to take something out of the metadata + inventory + ^ Should check if the action is permitted (the engine ensures the + action is valid, i.e. there's a stack of at least “count” items at + that position) + ^ If permitted, modify the metadata inventory and return the + stack of items + ^ If not permitted, return nil. + ^ default: minetest.node_metadata_inventory_take_allow_all } Recipe: (register_craft) diff --git a/src/guiInventoryMenu.cpp b/src/guiInventoryMenu.cpp index 823addd1b..51001eee3 100644 --- a/src/guiInventoryMenu.cpp +++ b/src/guiInventoryMenu.cpp @@ -526,24 +526,35 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event) u32 s_count = 0; if(s.isValid()) - { + do{ // breakable inv_s = m_invmgr->getInventory(s.inventoryloc); - assert(inv_s); + + if(!inv_s){ + errorstream<<"InventoryMenu: The selected inventory location " + <<"\""<<s.inventoryloc.dump()<<"\" doesn't exist" + <<std::endl; + s.i = -1; // make it invalid again + break; + } InventoryList *list = inv_s->getList(s.listname); if(list == NULL){ errorstream<<"InventoryMenu: The selected inventory list \"" <<s.listname<<"\" does not exist"<<std::endl; s.i = -1; // make it invalid again - } else if((u32)s.i >= list->getSize()){ + break; + } + + if((u32)s.i >= list->getSize()){ errorstream<<"InventoryMenu: The selected inventory list \"" <<s.listname<<"\" is too small (i="<<s.i<<", size=" <<list->getSize()<<")"<<std::endl; s.i = -1; // make it invalid again - } else{ - s_count = list->getItem(s.i).count; + break; } - } + + s_count = list->getItem(s.i).count; + }while(0); bool identical = (m_selected_item != NULL) && s.isValid() && (inv_selected == inv_s) && diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index b04a1c177..46f744f8b 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -27,6 +27,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "utility.h" #include "craftdef.h" +#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" + /* InventoryLocation */ @@ -197,27 +199,82 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame <<", to_list=\""<<to_list<<"\""<<std::endl; return; } - /* - This performs the actual movement - - If something is wrong (source item is empty, destination is the - same as source), nothing happens - */ - list_from->moveItem(from_i, list_to, to_i, count); + + // Handle node metadata move + if(from_inv.type == InventoryLocation::NODEMETA && + to_inv.type == InventoryLocation::NODEMETA && + from_inv.p == to_inv.p) + { + lua_State *L = player->getEnv()->getLua(); + int count0 = count; + if(count0 == 0) + count0 = list_from->getItem(from_i).count; + infostream<<player->getDescription()<<" moving "<<count0 + <<" items inside node at "<<PP(from_inv.p)<<std::endl; + scriptapi_node_on_metadata_inventory_move(L, from_inv.p, + from_list, from_i, to_list, to_i, count0, player); + } + // Handle node metadata take + else if(from_inv.type == InventoryLocation::NODEMETA) + { + lua_State *L = player->getEnv()->getLua(); + int count0 = count; + if(count0 == 0) + count0 = list_from->getItem(from_i).count; + infostream<<player->getDescription()<<" taking "<<count0 + <<" items from node at "<<PP(from_inv.p)<<std::endl; + ItemStack return_stack = scriptapi_node_on_metadata_inventory_take( + L, from_inv.p, from_list, from_i, count0, player); + if(return_stack.count == 0) + infostream<<"Node metadata gave no items"<<std::endl; + return_stack = list_to->addItem(to_i, return_stack); + list_to->addItem(return_stack); // Force return of everything + } + // Handle node metadata offer + else if(to_inv.type == InventoryLocation::NODEMETA) + { + lua_State *L = player->getEnv()->getLua(); + int count0 = count; + if(count0 == 0) + count0 = list_from->getItem(from_i).count; + ItemStack offer_stack = list_from->takeItem(from_i, count0); + infostream<<player->getDescription()<<" offering " + <<offer_stack.count<<" items to node at " + <<PP(to_inv.p)<<std::endl; + ItemStack reject_stack = scriptapi_node_on_metadata_inventory_offer( + L, to_inv.p, to_list, to_i, offer_stack, player); + if(reject_stack.count == offer_stack.count) + infostream<<"Node metadata rejected all items"<<std::endl; + else if(reject_stack.count != 0) + infostream<<"Node metadata rejected some items"<<std::endl; + reject_stack = list_from->addItem(from_i, reject_stack); + list_from->addItem(reject_stack); // Force return of everything + } + // Handle regular move + else + { + /* + This performs the actual movement + + If something is wrong (source item is empty, destination is the + same as source), nothing happens + */ + list_from->moveItem(from_i, list_to, to_i, count); + + infostream<<"IMoveAction::apply(): moved " + <<" count="<<count + <<" from inv=\""<<from_inv.dump()<<"\"" + <<" list=\""<<from_list<<"\"" + <<" i="<<from_i + <<" to inv=\""<<to_inv.dump()<<"\"" + <<" list=\""<<to_list<<"\"" + <<" i="<<to_i + <<std::endl; + } mgr->setInventoryModified(from_inv); if(inv_from != inv_to) mgr->setInventoryModified(to_inv); - - infostream<<"IMoveAction::apply(): moved at " - <<" count="<<count<<"\"" - <<" from inv=\""<<from_inv.dump()<<"\"" - <<" list=\""<<from_list<<"\"" - <<" i="<<from_i - <<" to inv=\""<<to_inv.dump()<<"\"" - <<" list=\""<<to_list<<"\"" - <<" i="<<to_i - <<std::endl; } void IMoveAction::clientApply(InventoryManager *mgr, IGameDef *gamedef) diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp index f9ec58582..213fb47f9 100644 --- a/src/scriptapi.cpp +++ b/src/scriptapi.cpp @@ -1470,7 +1470,7 @@ private: ItemStack &item = o->m_stack; u32 takecount = 1; if(!lua_isnone(L, 2)) - takecount = lua_tointeger(L, 2); + takecount = luaL_checkinteger(L, 2); ItemStack taken = item.takeItem(takecount); create(L, taken); return 1; @@ -5014,6 +5014,101 @@ void scriptapi_node_on_receive_fields(lua_State *L, v3s16 p, script_error(L, "error: %s", lua_tostring(L, -1)); } +void scriptapi_node_on_metadata_inventory_move(lua_State *L, v3s16 p, + const std::string &from_list, int from_index, + const std::string &to_list, int to_index, + int count, ServerActiveObject *player) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + INodeDefManager *ndef = get_server(L)->ndef(); + + // If node doesn't exist, we don't know what callback to call + MapNode node = get_env(L)->getMap().getNodeNoEx(p); + if(node.getContent() == CONTENT_IGNORE) + return; + + // Push callback function on stack + if(!get_item_callback(L, ndef->get(node).name.c_str(), + "on_metadata_inventory_move")) + return; + + // function(pos, from_list, from_index, to_list, to_index, count, player) + push_v3s16(L, p); + lua_pushstring(L, from_list.c_str()); + lua_pushinteger(L, from_index + 1); + lua_pushstring(L, to_list.c_str()); + lua_pushinteger(L, to_index + 1); + lua_pushinteger(L, count); + objectref_get_or_create(L, player); + if(lua_pcall(L, 7, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); +} + +ItemStack scriptapi_node_on_metadata_inventory_offer(lua_State *L, v3s16 p, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + INodeDefManager *ndef = get_server(L)->ndef(); + + // If node doesn't exist, we don't know what callback to call + MapNode node = get_env(L)->getMap().getNodeNoEx(p); + if(node.getContent() == CONTENT_IGNORE) + return stack; + + // Push callback function on stack + if(!get_item_callback(L, ndef->get(node).name.c_str(), + "on_metadata_inventory_offer")) + return stack; + + // Call function(pos, listname, index, stack, player) + push_v3s16(L, p); + lua_pushstring(L, listname.c_str()); + lua_pushinteger(L, index + 1); + LuaItemStack::create(L, stack); + objectref_get_or_create(L, player); + if(lua_pcall(L, 5, 1, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + return read_item(L, -1); +} + +ItemStack scriptapi_node_on_metadata_inventory_take(lua_State *L, v3s16 p, + const std::string &listname, int index, int count, + ServerActiveObject *player) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + INodeDefManager *ndef = get_server(L)->ndef(); + + // If node doesn't exist, we don't know what callback to call + MapNode node = get_env(L)->getMap().getNodeNoEx(p); + if(node.getContent() == CONTENT_IGNORE) + return ItemStack(); + + // Push callback function on stack + if(!get_item_callback(L, ndef->get(node).name.c_str(), + "on_metadata_inventory_take")) + return ItemStack(); + + // Call function(pos, listname, index, count, player) + push_v3s16(L, p); + lua_pushstring(L, listname.c_str()); + lua_pushinteger(L, index + 1); + lua_pushinteger(L, count); + objectref_get_or_create(L, player); + if(lua_pcall(L, 5, 1, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + return read_item(L, -1); +} + /* environment */ diff --git a/src/scriptapi.h b/src/scriptapi.h index e6c16eba6..13083500d 100644 --- a/src/scriptapi.h +++ b/src/scriptapi.h @@ -82,12 +82,28 @@ bool scriptapi_node_on_punch(lua_State *L, v3s16 p, MapNode node, ServerActiveObject *puncher); bool scriptapi_node_on_dig(lua_State *L, v3s16 p, MapNode node, ServerActiveObject *digger); +// Node constructor void scriptapi_node_on_construct(lua_State *L, v3s16 p, MapNode node); +// Node destructor void scriptapi_node_on_destruct(lua_State *L, v3s16 p, MapNode node); +// Called when a metadata form returns values void scriptapi_node_on_receive_fields(lua_State *L, v3s16 p, const std::string &formname, const std::map<std::string, std::string> &fields, ServerActiveObject *sender); +// Moves items +void scriptapi_node_on_metadata_inventory_move(lua_State *L, v3s16 p, + const std::string &from_list, int from_index, + const std::string &to_list, int to_index, + int count, ServerActiveObject *player); +// Inserts items, returns rejected items +ItemStack scriptapi_node_on_metadata_inventory_offer(lua_State *L, v3s16 p, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player); +// Takes items, returns taken items +ItemStack scriptapi_node_on_metadata_inventory_take(lua_State *L, v3s16 p, + const std::string &listname, int index, int count, + ServerActiveObject *player); /* luaentity */ // Returns true if succesfully added into Lua; false otherwise. |