Add MoveSomewhere inventory action

Improve shift+click experience
This commit is contained in:
est31 2015-06-20 12:55:48 +02:00
parent bc55ef337c
commit 2c1fd29884
6 changed files with 180 additions and 60 deletions

@ -493,6 +493,9 @@ public:
bool mediaReceived() bool mediaReceived()
{ return m_media_downloader == NULL; } { return m_media_downloader == NULL; }
u8 getProtoVersion()
{ return m_proto_ver; }
float mediaReceiveProgress(); float mediaReceiveProgress();
void afterContentReceived(IrrlichtDevice *device); void afterContentReceived(IrrlichtDevice *device);

@ -3384,31 +3384,42 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
break; break;
ItemStack stack_from = list_from->getItem(s.i); ItemStack stack_from = list_from->getItem(s.i);
assert(shift_move_amount <= stack_from.count); assert(shift_move_amount <= stack_from.count);
if (m_client->getProtoVersion() >= 25) {
// find a place (or more than one) to add the new item infostream << "Handing IACTION_MOVE to manager" << std::endl;
u32 ilt_size = list_to->getSize(); IMoveAction *a = new IMoveAction();
ItemStack leftover; a->count = shift_move_amount;
for (u32 slot_to = 0; slot_to < ilt_size a->from_inv = s.inventoryloc;
&& shift_move_amount > 0; slot_to++) { a->from_list = s.listname;
list_to->itemFits(slot_to, stack_from, &leftover); a->from_i = s.i;
if (leftover.count < stack_from.count) { a->to_inv = to_inv_sp.inventoryloc;
infostream << "Handing IACTION_MOVE to manager" << std::endl; a->to_list = to_inv_sp.listname;
IMoveAction *a = new IMoveAction(); a->move_somewhere = true;
a->count = MYMIN(shift_move_amount, m_invmgr->inventoryAction(a);
(u32) (stack_from.count - leftover.count)); } else {
shift_move_amount -= a->count; // find a place (or more than one) to add the new item
a->from_inv = s.inventoryloc; u32 ilt_size = list_to->getSize();
a->from_list = s.listname; ItemStack leftover;
a->from_i = s.i; for (u32 slot_to = 0; slot_to < ilt_size
a->to_inv = to_inv_sp.inventoryloc; && shift_move_amount > 0; slot_to++) {
a->to_list = to_inv_sp.listname; list_to->itemFits(slot_to, stack_from, &leftover);
a->to_i = slot_to; if (leftover.count < stack_from.count) {
m_invmgr->inventoryAction(a); infostream << "Handing IACTION_MOVE to manager" << std::endl;
stack_from = leftover; 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); } while (0);
} else if (drop_amount > 0) { } else if (drop_amount > 0) {
m_selected_content_guess = ItemStack(); // Clear m_selected_content_guess = ItemStack(); // Clear

@ -782,10 +782,47 @@ ItemStack InventoryList::peekItem(u32 i, u32 peekcount) const
return m_items[i].peekItem(peekcount); 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)
{
// 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) if(this == dest && i == dest_i)
return; return count;
// Take item from source list // Take item from source list
ItemStack item1; ItemStack item1;
@ -795,7 +832,7 @@ void InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i, u32 count)
item1 = takeItem(i, count); item1 = takeItem(i, count);
if(item1.empty()) if(item1.empty())
return; return 0;
// Try to add the item to destination list // Try to add the item to destination list
u32 oldcount = item1.count; 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. // If olditem is returned, nothing was added.
// Swap the items // Swap the items
if(nothing_added) if (nothing_added && swap_if_needed) {
{
// Take item from source list // Take item from source list
item1 = changeItem(i, ItemStack()); item1 = changeItem(i, ItemStack());
// Adding was not possible, swap the items. // 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); changeItem(i, item2);
} }
} }
return (oldcount - item1.count);
} }
/* /*

@ -244,7 +244,13 @@ public:
// Move an item to a different list (or a different stack in the same list) // 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) // 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: private:
std::vector<ItemStack> m_items; std::vector<ItemStack> m_items;

@ -121,16 +121,13 @@ InventoryAction * InventoryAction::deSerialize(std::istream &is)
InventoryAction *a = NULL; InventoryAction *a = NULL;
if(type == "Move") if (type == "Move") {
{ a = new IMoveAction(is, false);
a = new IMoveAction(is); } else if (type == "MoveSomewhere") {
} a = new IMoveAction(is, true);
else if(type == "Drop") } else if (type == "Drop") {
{
a = new IDropAction(is); a = new IDropAction(is);
} } else if(type == "Craft") {
else if(type == "Craft")
{
a = new ICraftAction(is); a = new ICraftAction(is);
} }
@ -141,9 +138,12 @@ InventoryAction * InventoryAction::deSerialize(std::istream &is)
IMoveAction IMoveAction
*/ */
IMoveAction::IMoveAction(std::istream &is) IMoveAction::IMoveAction(std::istream &is, bool somewhere)
{ {
std::string ts; std::string ts;
move_somewhere = somewhere;
caused_by_move_somewhere = false;
move_count = 0;
std::getline(is, ts, ' '); std::getline(is, ts, ' ');
count = stoi(ts); count = stoi(ts);
@ -161,8 +161,10 @@ IMoveAction::IMoveAction(std::istream &is)
std::getline(is, to_list, ' '); std::getline(is, to_list, ' ');
std::getline(is, ts, ' '); if (!somewhere) {
to_i = stoi(ts); std::getline(is, ts, ' ');
to_i = stoi(ts);
}
} }
void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef) void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef)
@ -202,6 +204,48 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
return; 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 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 If something is wrong (source item is empty, destination is the
same as source), nothing happens 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 source is infinite, reset it's stack
if(src_can_take_count == -1){ if(src_can_take_count == -1){
@ -352,15 +397,17 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
list_from->takeItem(from_i, count); list_from->takeItem(from_i, count);
} }
infostream<<"IMoveAction::apply(): moved" infostream << "IMoveAction::apply(): moved"
<<" count="<<count << " msom=" << move_somewhere
<<" from inv=\""<<from_inv.dump()<<"\"" << " caused=" << caused_by_move_somewhere
<<" list=\""<<from_list<<"\"" << " count=" << count
<<" i="<<from_i << " from inv=\"" << from_inv.dump() << "\""
<<" to inv=\""<<to_inv.dump()<<"\"" << " list=\"" << from_list << "\""
<<" list=\""<<to_list<<"\"" << " i=" << from_i
<<" i="<<to_i << " to inv=\"" << to_inv.dump() << "\""
<<std::endl; << " list=\"" << to_list << "\""
<< " i=" << to_i
<< std::endl;
/* /*
Record rollback information Record rollback information
@ -480,7 +527,10 @@ void IMoveAction::clientApply(InventoryManager *mgr, IGameDef *gamedef)
if(!list_from || !list_to) if(!list_from || !list_to)
return; 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); mgr->setInventoryModified(from_inv);
if(inv_from != inv_to) if(inv_from != inv_to)

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