From 2c1fd29884adec17564d39c0f7792633cbc55f9a Mon Sep 17 00:00:00 2001 From: est31 Date: Sat, 20 Jun 2015 12:55:48 +0200 Subject: [PATCH] Add MoveSomewhere inventory action Improve shift+click experience --- src/client.h | 3 ++ src/guiFormSpecMenu.cpp | 55 ++++++++++++++--------- src/inventory.cpp | 47 +++++++++++++++++--- src/inventory.h | 8 +++- src/inventorymanager.cpp | 96 ++++++++++++++++++++++++++++++---------- src/inventorymanager.h | 31 +++++++++---- 6 files changed, 180 insertions(+), 60 deletions(-) diff --git a/src/client.h b/src/client.h index 56f040909..daeef3985 100644 --- a/src/client.h +++ b/src/client.h @@ -493,6 +493,9 @@ public: bool mediaReceived() { return m_media_downloader == NULL; } + u8 getProtoVersion() + { return m_proto_ver; } + float mediaReceiveProgress(); void afterContentReceived(IrrlichtDevice *device); diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index c11fc303a..f16830619 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -3384,31 +3384,42 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) 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 ilt_size = list_to->getSize(); - ItemStack leftover; - for (u32 slot_to = 0; 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; + if (m_client->getProtoVersion() >= 25) { + infostream << "Handing IACTION_MOVE to manager" << std::endl; + IMoveAction *a = new IMoveAction(); + a->count = shift_move_amount; + 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->move_somewhere = true; + m_invmgr->inventoryAction(a); + } else { + // find a place (or more than one) to add the new item + u32 ilt_size = list_to->getSize(); + ItemStack leftover; + for (u32 slot_to = 0; 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 diff --git a/src/inventory.cpp b/src/inventory.cpp index 7941b3a2a..ff2c15b65 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -782,11 +782,48 @@ ItemStack InventoryList::peekItem(u32 i, u32 peekcount) const return m_items[i].peekItem(peekcount); } -void InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i, u32 count) +void InventoryList::moveItemSomewhere(u32 i, InventoryList *dest, u32 count) { - if(this == dest && i == dest_i) + // Take item from source list + ItemStack item1; + if (count == 0) + item1 = changeItem(i, ItemStack()); + else + item1 = takeItem(i, count); + + if (item1.empty()) return; + // Try to add the item to destination list + u32 oldcount = item1.count; + u32 dest_size = dest->getSize(); + // First try all the non-empty slots + for (u32 dest_i = 0; dest_i < dest_size; dest_i++) { + if (!m_items[dest_i].empty()) { + item1 = dest->addItem(dest_i, item1); + if (item1.empty()) return; + } + } + + // Then try all the empty ones + for (u32 dest_i = 0; dest_i < dest_size; dest_i++) { + if (m_items[dest_i].empty()) { + item1 = dest->addItem(dest_i, item1); + if (item1.empty()) return; + } + } + + // If we reach this, the item was not fully added + // Add the remaining part back to the source item + addItem(i, item1); +} + +u32 InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i, + u32 count, bool swap_if_needed) +{ + if(this == dest && i == dest_i) + return count; + // Take item from source list ItemStack item1; if(count == 0) @@ -795,7 +832,7 @@ void InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i, u32 count) item1 = takeItem(i, count); if(item1.empty()) - return; + return 0; // Try to add the item to destination list u32 oldcount = item1.count; @@ -813,8 +850,7 @@ void InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i, u32 count) // If olditem is returned, nothing was added. // Swap the items - if(nothing_added) - { + if (nothing_added && swap_if_needed) { // Take item from source list item1 = changeItem(i, ItemStack()); // Adding was not possible, swap the items. @@ -823,6 +859,7 @@ void InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i, u32 count) changeItem(i, item2); } } + return (oldcount - item1.count); } /* diff --git a/src/inventory.h b/src/inventory.h index faaa5ef95..e3c994fc3 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -244,7 +244,13 @@ public: // Move an item to a different list (or a different stack in the same list) // count is the maximum number of items to move (0 for everything) - void moveItem(u32 i, InventoryList *dest, u32 dest_i, u32 count = 0); + // returns number of moved items + u32 moveItem(u32 i, InventoryList *dest, u32 dest_i, + u32 count = 0, bool swap_if_needed = true); + + // like moveItem, but without a fixed destination index + // also with optional rollback recording + void moveItemSomewhere(u32 i, InventoryList *dest, u32 count); private: std::vector m_items; diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index 640e3395b..96ce48086 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -121,16 +121,13 @@ InventoryAction * InventoryAction::deSerialize(std::istream &is) InventoryAction *a = NULL; - if(type == "Move") - { - a = new IMoveAction(is); - } - else if(type == "Drop") - { + if (type == "Move") { + a = new IMoveAction(is, false); + } else if (type == "MoveSomewhere") { + a = new IMoveAction(is, true); + } else if (type == "Drop") { a = new IDropAction(is); - } - else if(type == "Craft") - { + } else if(type == "Craft") { a = new ICraftAction(is); } @@ -141,9 +138,12 @@ InventoryAction * InventoryAction::deSerialize(std::istream &is) IMoveAction */ -IMoveAction::IMoveAction(std::istream &is) +IMoveAction::IMoveAction(std::istream &is, bool somewhere) { std::string ts; + move_somewhere = somewhere; + caused_by_move_somewhere = false; + move_count = 0; std::getline(is, ts, ' '); count = stoi(ts); @@ -161,8 +161,10 @@ IMoveAction::IMoveAction(std::istream &is) std::getline(is, to_list, ' '); - std::getline(is, ts, ' '); - to_i = stoi(ts); + if (!somewhere) { + std::getline(is, ts, ' '); + to_i = stoi(ts); + } } void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef) @@ -202,6 +204,48 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame return; } + if (move_somewhere) { + s16 old_to_i = to_i; + u16 old_count = count; + caused_by_move_somewhere = true; + move_somewhere = false; + + infostream << "IMoveAction::apply(): moving item somewhere" + << " msom=" << move_somewhere + << " count=" << count + << " from inv=\"" << from_inv.dump() << "\"" + << " list=\"" << from_list << "\"" + << " i=" << from_i + << " to inv=\"" << to_inv.dump() << "\"" + << " list=\"" << to_list << "\"" + << std::endl; + + // Try to add the item to destination list + s16 dest_size = list_to->getSize(); + // First try all the non-empty slots + for (s16 dest_i = 0; dest_i < dest_size && count > 0; dest_i++) { + if (!list_to->getItem(dest_i).empty()) { + to_i = dest_i; + apply(mgr, player, gamedef); + count -= move_count; + } + } + + // Then try all the empty ones + for (s16 dest_i = 0; dest_i < dest_size && count > 0; dest_i++) { + if (list_to->getItem(dest_i).empty()) { + to_i = dest_i; + apply(mgr, player, gamedef); + count -= move_count; + } + } + + to_i = old_to_i; + count = old_count; + caused_by_move_somewhere = false; + move_somewhere = true; + return; + } /* Do not handle rollback if both inventories are that of the same player */ @@ -324,7 +368,8 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame 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); + move_count = list_from->moveItem(from_i, + list_to, to_i, count, !caused_by_move_somewhere); // If source is infinite, reset it's stack if(src_can_take_count == -1){ @@ -352,15 +397,17 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame list_from->takeItem(from_i, count); } - infostream<<"IMoveAction::apply(): moved" - <<" count="<