on_metadata_inventory_{move,offer,take}
authorPerttu Ahola <celeron55@gmail.com>
Fri, 1 Jun 2012 21:33:51 +0000 (00:33 +0300)
committerPerttu Ahola <celeron55@gmail.com>
Sun, 3 Jun 2012 19:31:01 +0000 (22:31 +0300)
builtin/item.lua
doc/lua_api.txt
src/guiInventoryMenu.cpp
src/inventorymanager.cpp
src/scriptapi.cpp
src/scriptapi.h

index d30b439aa53f1ff485c761d110300b954dd42b08..eb8c556de87b418c26d42f2f16d30b5255df97ef 100644 (file)
@@ -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,
index 73d7b3641c48e8a339b738d72c2a02f6924f1a5e..f8615b1308f3413cb01a93c59c7524bb30c819af 100644 (file)
@@ -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)
index 823addd1b91c56b58b3375d4ae4fc75bc0fc0c8b..51001eee3c3657737b88eafc08a686e358535e11 100644 (file)
@@ -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) &&
index b04a1c1777522aec1af58e6399d78c796d4dcb23..46f744f8b77b660cf395f4915d10be5da7758432 100644 (file)
@@ -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)
index f9ec585826068a74b643ff29cb901a147fcd3810..213fb47f9ae941a0a64d5223b721d60e3b5f5dfe 100644 (file)
@@ -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
 */
index e6c16eba665e8a13812fe30e9ee18478ff7c3fe8..13083500d7eac25583b03213d3a21fe8180ffe61 100644 (file)
@@ -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.