Răsfoiți Sursa

Add MoveSomewhere inventory action

Improve shift+click experience
est31 9 ani în urmă
părinte
comite
2c1fd29884
6 a modificat fișierele cu 180 adăugiri și 60 ștergeri
  1. 3 0
      src/client.h
  2. 33 22
      src/guiFormSpecMenu.cpp
  3. 42 5
      src/inventory.cpp
  4. 7 1
      src/inventory.h
  5. 73 23
      src/inventorymanager.cpp
  6. 22 9
      src/inventorymanager.h

+ 3 - 0
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);

+ 33 - 22
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
 

+ 42 - 5
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);
 }
 
 /*

+ 7 - 1
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<ItemStack> m_items;

+ 73 - 23
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="<<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;
+	infostream << "IMoveAction::apply(): moved"
+			<< " msom=" << move_somewhere
+			<< " caused=" << caused_by_move_somewhere
+			<< " 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;
 
 	/*
 		Record rollback information
@@ -480,7 +527,10 @@ void IMoveAction::clientApply(InventoryManager *mgr, IGameDef *gamedef)
 	if(!list_from || !list_to)
 		return;
 
-	list_from->moveItem(from_i, list_to, to_i, count);
+	if (!move_somewhere)
+		list_from->moveItem(from_i, list_to, to_i, count);
+	else
+		list_from->moveItemSomewhere(from_i, list_to, count);
 
 	mgr->setInventoryModified(from_inv);
 	if(inv_from != inv_to)

+ 22 - 9
src/inventorymanager.h

@@ -143,15 +143,24 @@ struct IMoveAction : public InventoryAction
 	InventoryLocation to_inv;
 	std::string to_list;
 	s16 to_i;
+	bool move_somewhere;
+
+	// treat these as private
+	// related to movement to somewhere
+	bool caused_by_move_somewhere;
+	u32 move_count;
 	
 	IMoveAction()
 	{
 		count = 0;
 		from_i = -1;
 		to_i = -1;
+		move_somewhere = false;
+		caused_by_move_somewhere = false;
+		move_count = 0;
 	}
 	
-	IMoveAction(std::istream &is);
+	IMoveAction(std::istream &is, bool somewhere);
 
 	u16 getType() const
 	{
@@ -160,14 +169,18 @@ struct IMoveAction : public InventoryAction
 
 	void serialize(std::ostream &os) const
 	{
-		os<<"Move ";
-		os<<count<<" ";
-		os<<from_inv.dump()<<" ";
-		os<<from_list<<" ";
-		os<<from_i<<" ";
-		os<<to_inv.dump()<<" ";
-		os<<to_list<<" ";
-		os<<to_i;
+		if (!move_somewhere)
+			os << "Move ";
+		else
+			os << "MoveSomewhere ";
+		os << count << " ";
+		os << from_inv.dump() << " ";
+		os << from_list << " ";
+		os << from_i << " ";
+		os << to_inv.dump() << " ";
+		os << to_list;
+		if (!move_somewhere)
+			os << " " << to_i;
 	}
 
 	void apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef);