Add list-rings 2795/head
authorest31 <MTest31@outlook.com>
Tue, 16 Jun 2015 08:48:54 +0000 (10:48 +0200)
committerest31 <MTest31@outlook.com>
Tue, 16 Jun 2015 12:51:26 +0000 (14:51 +0200)
Adds list-rings, a method to implement item sending between inventories via shift-click.
Nice insider feature: a ring consisting of a single inventory list serves as nice clean-up method.
Also adds them to minimal game, and the standard inventory.
Craft output slots are not supported.

doc/lua_api.txt
games/minimal/mods/default/init.lua
src/guiFormSpecMenu.cpp
src/guiFormSpecMenu.h
src/player.cpp

index 012c6eae19352e3c141b2477e778a881621300c0..a65c53c1a3266c93dfef20c43266ecace6317dca 100644 (file)
@@ -1340,6 +1340,17 @@ examples.
 #### `list[<inventory location>;<list name>;<X>,<Y>;<W>,<H>;<starting item index>]`
 * Show an inventory list
 
+#### `listring[<inventory location>;<list name>]`
+* Allows to create a ring of inventory lists
+* Shift-clicking on items in one element of the ring
+* will send them to the next inventory list inside the ring
+* The first occurrence of an element inside the ring will
+* determine the inventory where items will be sent to
+
+#### `listring[]`
+* Shorthand for doing `listring[<inventory location>;<list name>]`
+* for the last two inventory lists added by list[...]
+
 #### `listcolors[<slot_bg_normal>;<slot_bg_hover>]`
 * Sets background color of slots as `ColorString`
 * Sets background color of slots on mouse hovering
index cf0a1c6790972886768a84a8de66554c3c8caf42..c51af594f2d8412e6333d860f48ace342bf3ea83 100644 (file)
@@ -1158,7 +1158,8 @@ minetest.register_node("default:chest", {
                meta:set_string("formspec",
                                "size[8,9]"..
                                "list[current_name;main;0,0;8,4;]"..
-                               "list[current_player;main;0,5;8,4;]")
+                               "list[current_player;main;0,5;8,4;]" ..
+                               "listring[]")
                meta:set_string("infotext", "Chest")
                local inv = meta:get_inventory()
                inv:set_size("main", 8*4)
@@ -1197,7 +1198,8 @@ minetest.register_node("default:chest_locked", {
                meta:set_string("formspec",
                                "size[8,9]"..
                                "list[current_name;main;0,0;8,4;]"..
-                               "list[current_player;main;0,5;8,4;]")
+                               "list[current_player;main;0,5;8,4;]" ..
+                               "listring[]")
                meta:set_string("infotext", "Locked Chest")
                meta:set_string("owner", "")
                local inv = meta:get_inventory()
@@ -1261,7 +1263,11 @@ default.furnace_inactive_formspec =
        "list[current_name;fuel;2,3;1,1;]"..
        "list[current_name;src;2,1;1,1;]"..
        "list[current_name;dst;5,1;2,2;]"..
-       "list[current_player;main;0,5;8,4;]"
+       "list[current_player;main;0,5;8,4;]" ..
+       "listring[current_name;dst]" ..
+       "listring[current_player;main]" ..
+       "listring[current_name;src]" ..
+       "listring[current_player;main]"
 
 minetest.register_node("default:furnace", {
        description = "Furnace",
@@ -1398,7 +1404,11 @@ minetest.register_abm({
                                "list[current_name;fuel;2,3;1,1;]"..
                                "list[current_name;src;2,1;1,1;]"..
                                "list[current_name;dst;5,1;2,2;]"..
-                               "list[current_player;main;0,5;8,4;]")
+                               "list[current_player;main;0,5;8,4;]" ..
+                               "listring[current_name;dst]" ..
+                               "listring[current_player;main]" ..
+                               "listring[current_name;src]" ..
+                               "listring[current_player;main]")
                        return
                end
 
index ec829525bdb4787d31af265c2f325d5571667092..0414dc41f0e27cb1464462b82e3392ec84aba494 100644 (file)
@@ -352,6 +352,40 @@ void GUIFormSpecMenu::parseList(parserData* data,std::string element)
        errorstream<< "Invalid list element(" << parts.size() << "): '" << element << "'"  << std::endl;
 }
 
+void GUIFormSpecMenu::parseListRing(parserData* data, std::string element)
+{
+       if (m_gamedef == 0) {
+               errorstream << "WARNING: invalid use of 'listring' with m_gamedef==0" << std::endl;
+               return;
+       }
+
+       std::vector<std::string> parts = split(element, ';');
+
+       if (parts.size() == 2) {
+               std::string location = parts[0];
+               std::string listname = parts[1];
+
+               InventoryLocation loc;
+
+               if (location == "context" || location == "current_name")
+                       loc = m_current_inventory_location;
+               else
+                       loc.deSerialize(location);
+
+               m_inventory_rings.push_back(ListRingSpec(loc, listname));
+               return;
+       } else if ((element == "") && (m_inventorylists.size() > 1)) {
+               size_t siz = m_inventorylists.size();
+               // insert the last two inv list elements into the list ring
+               const ListDrawSpec &spa = m_inventorylists[siz - 2];
+               const ListDrawSpec &spb = m_inventorylists[siz - 1];
+               m_inventory_rings.push_back(ListRingSpec(spa.inventoryloc, spa.listname));
+               m_inventory_rings.push_back(ListRingSpec(spb.inventoryloc, spb.listname));
+       }
+       errorstream<< "Invalid list ring element(" << parts.size() << ", "
+               << m_inventorylists.size() << "): '" << element << "'"  << std::endl;
+}
+
 void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element)
 {
        std::vector<std::string> parts = split(element,';');
@@ -1661,6 +1695,11 @@ void GUIFormSpecMenu::parseElement(parserData* data, std::string element)
                return;
        }
 
+       if (type == "listring") {
+               parseListRing(data, description);
+               return;
+       }
+
        if (type == "checkbox") {
                parseCheckbox(data,description);
                return;
@@ -3145,6 +3184,10 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
                // from m_selected_item to s.
                u32 move_amount = 0;
 
+               // Set this number to a positive value to generate a move action
+               // from s to the next inventory ring.
+               u32 shift_move_amount = 0;
+
                // Set this number to a positive value to generate a drop action
                // from m_selected_item.
                u32 drop_amount = 0;
@@ -3166,18 +3209,29 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
                        }
                        else if(m_selected_item == NULL) {
                                if(s_count != 0) {
-                                       // Non-empty stack has been clicked: select it
+                                       // Non-empty stack has been clicked: select or shift-move it
                                        m_selected_item = new ItemSpec(s);
 
+                                       u32 count;
                                        if(button == 1)  // right
-                                               m_selected_amount = (s_count + 1) / 2;
+                                               count = (s_count + 1) / 2;
                                        else if(button == 2)  // middle
-                                               m_selected_amount = MYMIN(s_count, 10);
+                                               count = MYMIN(s_count, 10);
                                        else  // left
-                                               m_selected_amount = s_count;
+                                               count = s_count;
 
-                                       m_selected_dragging = true;
-                                       m_rmouse_auto_place = false;
+                                       if (!event.MouseInput.Shift) {
+                                               // no shift: select item
+                                               m_selected_amount = count;
+                                               m_selected_dragging = true;
+                                               m_rmouse_auto_place = false;
+                                       } else {
+                                               // shift pressed: move item
+                                               if (button != 1)
+                                                       shift_move_amount = count;
+                                               else // count of 1 at left click like after drag & drop
+                                                       shift_move_amount = 1;
+                                       }
                                }
                        }
                        else { // m_selected_item != NULL
@@ -3259,8 +3313,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
                }
 
                // Possibly send inventory action to server
-               if(move_amount > 0)
-               {
+               if (move_amount > 0) {
                        // Send IACTION_MOVE
 
                        assert(m_selected_item && m_selected_item->isValid());
@@ -3308,8 +3361,57 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
                        a->to_list = s.listname;
                        a->to_i = s.i;
                        m_invmgr->inventoryAction(a);
-               }
-               else if(drop_amount > 0) {
+               } else if (shift_move_amount > 0) {
+                       u32 mis = m_inventory_rings.size();
+                       u32 i = 0;
+                       for (; i < mis; i++) {
+                               const ListRingSpec &sp = m_inventory_rings[i];
+                               if (sp.inventoryloc == s.inventoryloc
+                                               && sp.listname == s.listname)
+                                       break;
+                       }
+                       do {
+                               if (i >= mis) // if not found
+                                       break;
+                               u32 to_inv_ind = (i + 1) % mis;
+                               const ListRingSpec &to_inv_sp = m_inventory_rings[to_inv_ind];
+                               InventoryList *list_from = inv_s->getList(s.listname);
+                               if (!list_from)
+                                       break;
+                               Inventory *inv_to = m_invmgr->getInventory(to_inv_sp.inventoryloc);
+                               if (!inv_to)
+                                       break;
+                               InventoryList *list_to = inv_to->getList(to_inv_sp.listname);
+                               if (!list_to)
+                                       break;
+                               ItemStack stack_from = list_from->getItem(s.i);
+                               assert(shift_move_amount <= stack_from.count);
+
+                               // find a place (or more than one) to add the new item
+                               u32 slot_to = 0;
+                               u32 ilt_size = list_to->getSize();
+                               ItemStack leftover;
+                               for (; slot_to < ilt_size && shift_move_amount > 0; slot_to++) {
+                                       list_to->itemFits(slot_to, stack_from, &leftover);
+                                       if (leftover.count < stack_from.count) {
+                                               infostream << "Handing IACTION_MOVE to manager" << std::endl;
+                                               IMoveAction *a = new IMoveAction();
+                                               a->count = MYMIN(shift_move_amount,
+                                                       (u32) (stack_from.count - leftover.count));
+                                               shift_move_amount -= a->count;
+                                               a->from_inv = s.inventoryloc;
+                                               a->from_list = s.listname;
+                                               a->from_i = s.i;
+                                               a->to_inv = to_inv_sp.inventoryloc;
+                                               a->to_list = to_inv_sp.listname;
+                                               a->to_i = slot_to;
+                                               m_invmgr->inventoryAction(a);
+                                               stack_from = leftover;
+                                       }
+                               }
+                       } while (0);
+
+               } else if (drop_amount > 0) {
                        m_selected_content_guess = ItemStack(); // Clear
 
                        // Send IACTION_DROP
index f04968aec465ac4b170cf54c347130fb665a80b3..2ba47f7ff5bac5c60648465886cfd4daec20f959 100644 (file)
@@ -121,6 +121,22 @@ class GUIFormSpecMenu : public GUIModalMenu
                s32 start_item_i;
        };
 
+       struct ListRingSpec
+       {
+               ListRingSpec()
+               {
+               }
+               ListRingSpec(const InventoryLocation &a_inventoryloc,
+                               const std::string &a_listname):
+                       inventoryloc(a_inventoryloc),
+                       listname(a_listname)
+               {
+               }
+
+               InventoryLocation inventoryloc;
+               std::string listname;
+       };
+
        struct ImageDrawSpec
        {
                ImageDrawSpec()
@@ -306,6 +322,7 @@ protected:
 
 
        std::vector<ListDrawSpec> m_inventorylists;
+       std::vector<ListRingSpec> m_inventory_rings;
        std::vector<ImageDrawSpec> m_backgrounds;
        std::vector<ImageDrawSpec> m_images;
        std::vector<ImageDrawSpec> m_itemimages;
@@ -384,6 +401,7 @@ private:
 
        void parseSize(parserData* data,std::string element);
        void parseList(parserData* data,std::string element);
+       void parseListRing(parserData* data,std::string element);
        void parseCheckbox(parserData* data,std::string element);
        void parseImage(parserData* data,std::string element);
        void parseItemImage(parserData* data,std::string element);
index 4a5e5ad91ed21f7ee94b224ad8c748d25c118587..26496259ccf77737fc27f9f1fa53343aeefba11a 100644 (file)
@@ -71,6 +71,7 @@ Player::Player(IGameDef *gamedef, const char *name):
                //"image[1,0.6;1,2;player.png]"
                "list[current_player;main;0,3.5;8,4;]"
                "list[current_player;craft;3,0;3,3;]"
+               "listring[]"
                "list[current_player;craftpreview;7,1;1,1;]";
 
        // Initialize movement settings at default values, so movement can work if the server fails to send them