Make inventory GUI do sane things when server-side inventory acts unusually
authorPerttu Ahola <celeron55@gmail.com>
Sun, 2 Sep 2012 19:51:38 +0000 (22:51 +0300)
committerPerttu Ahola <celeron55@gmail.com>
Sun, 2 Sep 2012 19:51:38 +0000 (22:51 +0300)
src/guiFormSpecMenu.cpp
src/guiFormSpecMenu.h
src/inventorymanager.cpp
src/inventorymanager.h

index 41ec0f3da3033a0b2a8f2f0287c5614080866492..ed44e441b4c562e29761165d97b18a86e7413ba1 100644 (file)
@@ -733,6 +733,54 @@ void GUIFormSpecMenu::drawMenu()
 
 void GUIFormSpecMenu::updateSelectedItem()
 {
+       // WARNING: BLACK MAGIC
+       // See if there is a stack suited for our current guess.
+       // If such stack does not exist, clear the guess.
+       if(m_selected_content_guess.name != "")
+       {
+               bool found = false;
+               for(u32 i=0; i<m_inventorylists.size() && !found; i++){
+                       const ListDrawSpec &s = m_inventorylists[i];
+                       Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
+                       if(!inv)
+                               continue;
+                       InventoryList *list = inv->getList(s.listname);
+                       if(!list)
+                               continue;
+                       for(s32 i=0; i<s.geom.X*s.geom.Y && !found; i++){
+                               u32 item_i = i + s.start_item_i;
+                               if(item_i >= list->getSize())
+                                       continue;
+                               ItemStack stack = list->getItem(item_i);
+                               if(stack.name == m_selected_content_guess.name &&
+                                               stack.count == m_selected_content_guess.count){
+                                       found = true;
+                                       if(m_selected_item){
+                                               // If guessed stack is already selected, all is fine
+                                               if(m_selected_item->inventoryloc == s.inventoryloc &&
+                                                               m_selected_item->listname == s.listname &&
+                                                               m_selected_item->i == (s32)item_i &&
+                                                               m_selected_amount == stack.count){
+                                                       break;
+                                               }
+                                               delete m_selected_item;
+                                               m_selected_item = NULL;
+                                       }
+                                       infostream<<"Client: Changing selected content guess to "
+                                                       <<s.inventoryloc.dump()<<" "<<s.listname
+                                                       <<" "<<item_i<<std::endl;
+                                       m_selected_item = new ItemSpec(s.inventoryloc, s.listname, item_i);
+                                       m_selected_amount = stack.count;
+                                       break;
+                               }
+                       }
+               }
+               if(!found){
+                       infostream<<"Client: Discarding selected content guess: "
+                                       <<m_selected_content_guess.getItemString()<<std::endl;
+                       m_selected_content_guess.name = "";
+               }
+       }
        // If the selected stack has become empty for some reason, deselect it.
        // If the selected stack has become smaller, adjust m_selected_amount.
        if(m_selected_item)
@@ -1054,21 +1102,28 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
                        // Check how many items can be moved
                        move_amount = stack_from.count = MYMIN(move_amount, stack_from.count);
                        ItemStack leftover = stack_to.addItem(stack_from, m_gamedef->idef());
-                       if(leftover.count == stack_from.count)
+                       // If source stack cannot be added to destination stack at all,
+                       // they are swapped
+                       if(leftover.count == stack_from.count && leftover.name == stack_from.name)
                        {
-                               // Swap the stacks
                                m_selected_amount = stack_to.count;
+                               // In case the server doesn't directly swap them but instead
+                               // moves stack_to somewhere else, set this
+                               m_selected_content_guess = stack_to;
+                               m_selected_content_guess_inventory = s.inventoryloc;
                        }
+                       // Source stack goes fully into destination stack
                        else if(leftover.empty())
                        {
-                               // Item fits
                                m_selected_amount -= move_amount;
+                               m_selected_content_guess = ItemStack(); // Clear
                        }
+                       // Source stack goes partly into destination stack
                        else
                        {
-                               // Item only fits partially
                                move_amount -= leftover.count;
                                m_selected_amount -= move_amount;
+                               m_selected_content_guess = ItemStack(); // Clear
                        }
 
                        infostream<<"Handing IACTION_MOVE to manager"<<std::endl;
@@ -1084,6 +1139,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
                }
                else if(drop_amount > 0)
                {
+                       m_selected_content_guess = ItemStack(); // Clear
+
                        // Send IACTION_DROP
 
                        assert(m_selected_item && m_selected_item->isValid());
@@ -1107,6 +1164,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
                }
                else if(craft_amount > 0)
                {
+                       m_selected_content_guess = ItemStack(); // Clear
+
                        // Send IACTION_CRAFT
 
                        assert(s.isValid());
@@ -1126,6 +1185,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
                        m_selected_item = NULL;
                        m_selected_amount = 0;
                        m_selected_dragging = false;
+                       m_selected_content_guess = ItemStack();
                }
        }
        if(event.EventType==EET_GUI_EVENT)
index f0a5988e95ce100fcf785a7fdda9ec16edb68b2e..5c01bdcd222ec606703e933da7abc5e5024a9347 100644 (file)
@@ -212,6 +212,12 @@ protected:
        ItemSpec *m_selected_item;
        u32 m_selected_amount;
        bool m_selected_dragging;
+       
+       // WARNING: BLACK MAGIC
+       // Used to guess and keep up with some special things the server can do.
+       // If name is "", no guess exists.
+       ItemStack m_selected_content_guess;
+       InventoryLocation m_selected_content_guess_inventory;
 
        v2s32 m_pointer;
        gui::IGUIStaticText *m_tooltip_element;
index e2e5378383f8d4a0956256d9aa061ec6db086dd1..1a7f56f315534d1791e691997b1eabb20ac95aff 100644 (file)
@@ -332,6 +332,18 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
 
        // If source is infinite, reset it's stack
        if(src_can_take_count == -1){
+               // If destination stack is of different type and there are leftover
+               // items, attempt to put the leftover items to a different place in the
+               // destination inventory.
+               // The client-side GUI will try to guess if this happens.
+               if(from_stack_was.name != to_stack_was.name){
+                       for(u32 i=0; i<list_to->getSize(); i++){
+                               if(list_to->getItem(i).empty()){
+                                       list_to->changeItem(i, to_stack_was);
+                                       break;
+                               }
+                       }
+               }
                list_from->deleteItem(from_i);
                list_from->addItem(from_i, from_stack_was);
        }
index dae14f1a6c25da91ddeba69142039933d71d3800..f81f5b972b8c4eceb49187893870ace0b6af07f8 100644 (file)
@@ -66,6 +66,29 @@ struct InventoryLocation
                name = name_;
        }
 
+       bool operator==(const InventoryLocation &other) const
+       {
+               if(type != other.type)
+                       return false;
+               switch(type){
+               case UNDEFINED:
+                       return false;
+               case CURRENT_PLAYER:
+                       return true;
+               case PLAYER:
+                       return (name == other.name);
+               case NODEMETA:
+                       return (p == other.p);
+               case DETACHED:
+                       return (name == other.name);
+               }
+               return false;
+       }
+       bool operator!=(const InventoryLocation &other) const
+       {
+               return !(*this == other);
+       }
+
        void applyCurrentPlayer(const std::string &name_)
        {
                if(type == CURRENT_PLAYER)