Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Core/Bags: Implement sort bags #27802

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
70 changes: 70 additions & 0 deletions src/server/game/Entities/Player/Player.cpp
Expand Up @@ -13269,6 +13269,10 @@ void Player::SplitItem(uint16 src, uint16 dst, uint32 count)

void Player::SwapItem(uint16 src, uint16 dst)
{
// If we want to swap the same item it is useless.
if (src == dst)
return;

uint8 srcbag = src >> 8;
uint8 srcslot = src & 255;

Expand Down Expand Up @@ -29287,3 +29291,69 @@ void Player::SendDisplayToast(uint32 entry, DisplayToastType type, bool isBonusR

SendDirectMessage(displayToast.Write());
}

void Player::ApplyOnBagsItems(std::function<bool(Player*, Item*, uint8 /*bag*/, uint8 /*slot*/)>&& function)
{
for (uint32 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
{
if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
{
if (!function(this, item, INVENTORY_SLOT_BAG_0, i))
return;
}
}

for (uint32 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
{
if (Bag* bag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i))
{
for (uint32 j = 0; j < bag->GetBagSize(); ++j)
{
if (Item* item = GetItemByPos(i, j))
{
if (!function(this, item, i, j))
return;
}
}
}
}
}

void Player::ApplyOnBankItems(std::function<bool(Player*, Item*, uint8 /*bag*/, uint8 /*slot*/)>&& function)
{
for (uint32 i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; ++i)
{
if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
{
if (!function(this, item, INVENTORY_SLOT_BAG_0, i))
return;
}
}

for (uint32 i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
{
if (Bag* bag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i))
{
for (uint32 j = 0; j < bag->GetBagSize(); ++j)
{
if (Item* item = GetItemByPos(i, j))
{
if (!function(this, item, i, j))
return;
}
}
}
}
}

void Player::ApplyOnReagentBankItems(std::function<bool(Player*, Item*, uint8 /*bag*/, uint8 /*slot*/)>&& function)
{
for (uint32 i = REAGENT_SLOT_START; i < REAGENT_SLOT_END; ++i)
{
if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
{
if (!function(this, item, INVENTORY_SLOT_BAG_0, i))
return;
}
}
}
5 changes: 5 additions & 0 deletions src/server/game/Entities/Player/Player.h
Expand Up @@ -1417,6 +1417,11 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void AutoStoreLoot(uint32 loot_id, LootStore const& store, ItemContext context = ItemContext::NONE, bool broadcast = false, bool createdByPlayer = false) { AutoStoreLoot(NULL_BAG, NULL_SLOT, loot_id, store, context, broadcast, createdByPlayer); }
void StoreLootItem(uint8 lootSlot, Loot* loot, AELootResult* aeResult = nullptr);

// Sort Bags
void ApplyOnBagsItems(std::function<bool(Player*, Item*, uint8 /*bag*/, uint8 /*slot*/)>&& function);
void ApplyOnBankItems(std::function<bool(Player*, Item*, uint8 /*bag*/, uint8 /*slot*/)>&& function);
void ApplyOnReagentBankItems(std::function<bool(Player*, Item*, uint8 /*bag*/, uint8 /*slot*/)>&& function);

InventoryResult CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, uint32* no_space_count = nullptr, uint32* offendingItemId = nullptr) const;
InventoryResult CanStoreItem(uint8 bag, uint8 slot, ItemPosCountVec& dest, uint32 entry, uint32 count, Item* pItem = nullptr, bool swap = false, uint32* no_space_count = nullptr) const;

Expand Down
171 changes: 150 additions & 21 deletions src/server/game/Handlers/ItemHandler.cpp
Expand Up @@ -1214,27 +1214,6 @@ void WorldSession::HandleUseCritterItem(WorldPackets::Item::UseCritterItem& useC
_player->DestroyItem(item->GetBagSlot(), item->GetSlot(), true);
}

void WorldSession::HandleSortBags(WorldPackets::Item::SortBags& /*sortBags*/)
{
// TODO: Implement sorting
// Placeholder to prevent completely locking out bags clientside
SendPacket(WorldPackets::Item::BagCleanupFinished().Write());
}

void WorldSession::HandleSortBankBags(WorldPackets::Item::SortBankBags& /*sortBankBags*/)
{
// TODO: Implement sorting
// Placeholder to prevent completely locking out bags clientside
SendPacket(WorldPackets::Item::BagCleanupFinished().Write());
}

void WorldSession::HandleSortReagentBankBags(WorldPackets::Item::SortReagentBankBags& /*sortReagentBankBags*/)
{
// TODO: Implement sorting
// Placeholder to prevent completely locking out bags clientside
SendPacket(WorldPackets::Item::BagCleanupFinished().Write());
}

void WorldSession::HandleRemoveNewItem(WorldPackets::Item::RemoveNewItem& removeNewItem)
{
Item* item = _player->GetItemByGuid(removeNewItem.ItemGuid);
Expand All @@ -1250,3 +1229,153 @@ void WorldSession::HandleRemoveNewItem(WorldPackets::Item::RemoveNewItem& remove
item->SetState(ITEM_CHANGED, _player);
}
}

// This anonymous namespace contains utility functions for handling BagAutoSort.
namespace
Shauren marked this conversation as resolved.
Show resolved Hide resolved
{
// Look in the bag if the item can be store or stacked.
bool StoreItemAndStack(Player* player, Item* item, uint8 bag)
{
uint16 src = item->GetPos();

ItemPosCountVec dest;
InventoryResult result = player->CanStoreItem(bag, NULL_SLOT, dest, item, false);
if (result == EQUIP_ERR_OK && !(dest.size() == 1 && dest[0].pos == src))
{
player->RemoveItem(item->GetBagSlot(), item->GetSlot(), true);
player->StoreItem(dest, item, true);

return true;
}

return false;
}

bool BankItemAndStack(Player* player, Item* item, uint8 bag, bool reagent = false)
{
uint16 src = item->GetPos();

ItemPosCountVec dest;
InventoryResult result = player->CanBankItem(reagent ? NULL_BAG : bag, NULL_SLOT, dest, item, false, true, reagent);

if (result == EQUIP_ERR_OK && !(dest.size() == 1 && dest[0].pos == src))
{
player->RemoveItem(item->GetBagSlot(), item->GetSlot(), true);
player->BankItem(dest, item, true);

return true;
}

return false;
}

// Loop through all the bags to see if it can be stack or not.
void StoreItemInBags(Player* player, Item* item)
{
if (StoreItemAndStack(player, item, INVENTORY_SLOT_BAG_0))
return;

for (uint32 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
{
if (StoreItemAndStack(player, item, i))
break;
}
}

void StoreItemInBank(Player* player, Item* item)
{
if (BankItemAndStack(player, item, NULL_SLOT))
return;

for (uint32 i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
{
if (BankItemAndStack(player, item, i))
break;
}
}

void StoreItemInReagentBank(Player* player, Item* item)
{
if (BankItemAndStack(player, item, NULL_SLOT, true))
return;

for (uint32 i = REAGENT_SLOT_START; i < REAGENT_SLOT_END; i++)
{
if (BankItemAndStack(player, item, i, true))
break;
}
}

void SortBags(Player* player, void(Player::* fn)(std::function<bool(Player*, Item*, uint8 /*bag*/, uint8 /*slot*/)>&&))
{
// First pass to stack items in caller.
std::unordered_map<uint32, uint32> itemsQuality;
typedef std::multimap<uint32, Item*> SortItemsContainer;
SortItemsContainer items;

// Second pass, we collect the informations for sorting.
(player->*fn)([&items, &itemsQuality](Player* player, Item* item, uint8 /*bag*/, uint8 /*slot*/)
{
// We get the number of non-distinct items and item level for sorting.
items.insert(std::make_pair(item->GetEntry(), item));
itemsQuality[item->GetEntry()] = item->GetItemLevel(player);

return true;
});

// We get advantage of the multimap properties to sort our items.
std::multimap<uint32, SortItemsContainer::value_type> resultMap;
for (auto const& pair : items)
resultMap.insert(std::make_pair(itemsQuality[pair.first], pair));

// Third pass to swap all the items correctly.
auto itr = std::begin(resultMap);
(player->*fn)([&resultMap, &itr](Player* player, Item* /*item*/, uint8 bag, uint8 slot)
{
if (itr == std::end(resultMap))
return false;

uint16 pos = itr->second.second->GetPos();
player->SwapItem(pos, (bag << 8) | slot);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: wasn't there a macro or utility function to centralize this and avoid copy pastes in the unlikely event this math changes

++itr;

return true;
});
}
}

void WorldSession::HandleSortBags(WorldPackets::Item::SortBags& /*sortBags*/)
{
_player->ApplyOnBagsItems([](Player* player, Item* item, uint8 /*bag*/, uint8 /*slot*/)
{
StoreItemInBags(player, item);
return true;
});

SortBags(_player, &Player::ApplyOnBagsItems);
SendPacket(WorldPackets::Item::BagCleanupFinished().Write());
}

void WorldSession::HandleSortBankBags(WorldPackets::Item::SortBankBags& /*sortBankBags*/)
{
_player->ApplyOnBankItems([](Player* player, Item* item, uint8 /*bag*/, uint8 /*slot*/)
{
StoreItemInBank(player, item);
return true;
});

SortBags(_player, &Player::ApplyOnBankItems);
SendPacket(WorldPackets::Item::BagCleanupFinished().Write());
}

void WorldSession::HandleSortReagentBankBags(WorldPackets::Item::SortReagentBankBags& /*sortReagentBankBags*/)
{
_player->ApplyOnReagentBankItems([](Player* player, Item* item, uint8 /*bag*/, uint8 /*slot*/)
{
StoreItemInReagentBank(player, item);
return true;
});

SortBags(_player, &Player::ApplyOnReagentBankItems);
SendPacket(WorldPackets::Item::BagCleanupFinished().Write());
}
6 changes: 3 additions & 3 deletions src/server/game/Server/Protocol/Opcodes.cpp
Expand Up @@ -830,9 +830,9 @@ void OpcodeTable::Initialize()
DEFINE_HANDLER(CMSG_SIGN_PETITION, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSignPetition);
DEFINE_HANDLER(CMSG_SILENCE_PARTY_TALKER, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_SOCKET_GEMS, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleSocketGems);
DEFINE_HANDLER(CMSG_SORT_BAGS, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleSortBags);
DEFINE_HANDLER(CMSG_SORT_BANK_BAGS, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleSortBankBags);
DEFINE_HANDLER(CMSG_SORT_REAGENT_BANK_BAGS, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleSortReagentBankBags);
DEFINE_HANDLER(CMSG_SORT_BAGS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSortBags);
DEFINE_HANDLER(CMSG_SORT_BANK_BAGS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSortBankBags);
DEFINE_HANDLER(CMSG_SORT_REAGENT_BANK_BAGS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSortReagentBankBags);
DEFINE_HANDLER(CMSG_SPELL_CLICK, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleSpellClick);
DEFINE_HANDLER(CMSG_SPIRIT_HEALER_ACTIVATE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSpiritHealerActivate);
DEFINE_HANDLER(CMSG_SPLIT_GUILD_BANK_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSplitGuildBankItem);
Expand Down