diff options
20 files changed, 198 insertions, 179 deletions
diff --git a/src/server/game/Chat/Commands/Debugcmds.cpp b/src/server/game/Chat/Commands/Debugcmds.cpp index a6c973ea002..64fa3efdcba 100644 --- a/src/server/game/Chat/Commands/Debugcmds.cpp +++ b/src/server/game/Chat/Commands/Debugcmds.cpp @@ -104,7 +104,7 @@ bool ChatHandler::HandleDebugSendEquipErrorCommand(const char* args) return false; uint8 msg = atoi(args); - m_session->GetPlayer()->SendEquipError(msg, 0, 0); + m_session->GetPlayer()->SendEquipError(msg, NULL, NULL); return true; } diff --git a/src/server/game/Chat/Commands/Level2.cpp b/src/server/game/Chat/Commands/Level2.cpp index c66311f1277..15534e801aa 100644 --- a/src/server/game/Chat/Commands/Level2.cpp +++ b/src/server/game/Chat/Commands/Level2.cpp @@ -1959,10 +1959,10 @@ bool ChatHandler::HandleItemMoveCommand(const char* args) if (srcslot == dstslot) return true; - if (!m_session->GetPlayer()->IsValidPos(INVENTORY_SLOT_BAG_0,srcslot)) + if (!m_session->GetPlayer()->IsValidPos(INVENTORY_SLOT_BAG_0, srcslot, true)) return false; - if (!m_session->GetPlayer()->IsValidPos(INVENTORY_SLOT_BAG_0,dstslot)) + if (!m_session->GetPlayer()->IsValidPos(INVENTORY_SLOT_BAG_0, dstslot, false)) return false; uint16 src = ((INVENTORY_SLOT_BAG_0 << 8) | srcslot); diff --git a/src/server/game/Chat/Commands/Level3.cpp b/src/server/game/Chat/Commands/Level3.cpp index 860718d4908..8cade4bb397 100644 --- a/src/server/game/Chat/Commands/Level3.cpp +++ b/src/server/game/Chat/Commands/Level3.cpp @@ -2961,7 +2961,7 @@ bool ChatHandler::HandleAddItemSetCommand(const char *args) } else { - pl->SendEquipError(msg, NULL, NULL); + pl->SendEquipError(msg, NULL, NULL, pProto->ItemId); PSendSysMessage(LANG_ITEM_CANNOT_CREATE, pProto->ItemId, 1); } } diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index bd72026d753..8caa77cddff 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -336,6 +336,12 @@ enum ItemEnchantmentType ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET = 8 }; +enum ItemLimitCategoryMode +{ + ITEM_LIMIT_CATEGORY_MODE_HAVE = 0, // limit applied to amount items in inventory/bank + ITEM_LIMIT_CATEGORY_MODE_EQUIP = 1, // limit applied to amount equipped items (including used gems) +}; + enum TotemCategoryType { TOTEM_CATEGORY_TYPE_KNIFE = 1, diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index c8eb3701c33..562cd740c72 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -1060,7 +1060,7 @@ struct ItemLimitCategoryEntry //char* name[16] // 1-16 m_name_lang // 17 name flags uint32 maxCount; // 18, max allowed equipped as item or in gem slot - //uint32 unk; // 19, 1 for gems only... + uint32 mode; // 19, 0 = have, 1 = equip (enum ItemLimitCategoryMode) }; struct ItemRandomPropertiesEntry diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index b83ea207804..1fb0fcbdf65 100644 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -69,7 +69,7 @@ const char ItemBagFamilyfmt[]="nxxxxxxxxxxxxxxxxx"; //const char ItemDisplayTemplateEntryfmt[]="nxxxxxxxxxxixxxxxxxxxxx"; //const char ItemCondExtCostsEntryfmt[]="xiii"; const char ItemExtendedCostEntryfmt[]="niiiiiiiiiiiiiix"; -const char ItemLimitCategoryEntryfmt[]="nxxxxxxxxxxxxxxxxxix"; +const char ItemLimitCategoryEntryfmt[]="nxxxxxxxxxxxxxxxxxii"; const char ItemRandomPropertiesfmt[]="nxiiiiissssssssssssssssx"; const char ItemRandomSuffixfmt[]="nssssssssssssssssxxiiiiiiiiii"; const char ItemSetEntryfmt[]="dssssssssssssssssxiiiiiiiiiixxxxxxxiiiiiiiiiiiiiiiiii"; diff --git a/src/server/game/Entities/Item/Container/Bag.cpp b/src/server/game/Entities/Item/Container/Bag.cpp index 2d7aa490e32..6afd3260c08 100644 --- a/src/server/game/Entities/Item/Container/Bag.cpp +++ b/src/server/game/Entities/Item/Container/Bag.cpp @@ -213,6 +213,19 @@ uint32 Bag::GetItemCount(uint32 item, Item* eItem) const return count; } +uint32 Bag::GetItemCountWithLimitCategory(uint32 limitCategory, Item* skipItem) const +{ + uint32 count = 0; + for (uint32 i = 0; i < GetBagSize(); ++i) + if (Item *pItem = m_bagslot[i]) + if (pItem != skipItem) + if (ItemPrototype const *pProto = pItem->GetProto()) + if (pProto->ItemLimitCategory == limitCategory) + count += m_bagslot[i]->GetCount(); + + return count; +} + uint8 Bag::GetSlotByItemGUID(uint64 guid) const { for (uint32 i = 0; i < GetBagSize(); ++i) diff --git a/src/server/game/Entities/Item/Container/Bag.h b/src/server/game/Entities/Item/Container/Bag.h index 4937b307547..6d4f65cf8cc 100644 --- a/src/server/game/Entities/Item/Container/Bag.h +++ b/src/server/game/Entities/Item/Container/Bag.h @@ -45,6 +45,7 @@ class Bag : public Item Item* GetItemByPos(uint8 slot) const; uint32 GetItemCount(uint32 item, Item* eItem = NULL) const; + uint32 GetItemCountWithLimitCategory(uint32 limitCategory, Item* skipItem = NULL) const; uint8 GetSlotByItemGUID(uint64 guid) const; bool IsEmpty() const; diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h index 22409a6813b..c2b18badf9b 100644 --- a/src/server/game/Entities/Item/Item.h +++ b/src/server/game/Entities/Item/Item.h @@ -120,10 +120,15 @@ enum InventoryChangeFailure EQUIP_ERR_NOT_DURING_ARENA_MATCH = 78, EQUIP_ERR_CANNOT_TRADE_THAT = 79, EQUIP_ERR_PERSONAL_ARENA_RATING_TOO_LOW = 80, - // no output = 81, + EQUIP_ERR_EVENT_AUTOEQUIP_BIND_CONFIRM = 81, EQUIP_ERR_ARTEFACTS_ONLY_FOR_OWN_CHARACTERS = 82, // no output = 83, - // crash client = 84, + EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_COUNT_EXCEEDED = 84, + EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_SOCKETED_EXCEEDED = 85, + EQUIP_ERR_SCALING_STAT_ITEM_LEVEL_EXCEEDED = 86, + EQUIP_ERR_PURCHASE_LEVEL_TOO_LOW = 87, + EQUIP_ERR_CANT_EQUIP_NEED_TALENT = 88, + EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED = 89 }; enum BuyFailure diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 0d1918d36fd..301b5cfd2ae 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -9483,6 +9483,41 @@ uint32 Player::GetItemCount(uint32 item, bool inBankAlso, Item* skipItem) const return count; } +uint32 Player::GetItemCountWithLimitCategory(uint32 limitCategory, Item* skipItem) const +{ + uint32 count = 0; + for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i) + if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + if (pItem != skipItem) + if (ItemPrototype const *pProto = pItem->GetProto()) + if (pProto->ItemLimitCategory == limitCategory) + count += pItem->GetCount(); + + for (int i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i) + if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + if (pItem != skipItem) + if (ItemPrototype const *pProto = pItem->GetProto()) + if (pProto->ItemLimitCategory == limitCategory) + count += pItem->GetCount(); + + for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) + if (Bag* pBag = (Bag*) GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + count += pBag->GetItemCountWithLimitCategory(limitCategory, skipItem); + + for (int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; ++i) + if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + if (pItem != skipItem) + if (ItemPrototype const *pProto = pItem->GetProto()) + if (pProto->ItemLimitCategory == limitCategory) + count += pItem->GetCount(); + + for (int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i) + if (Bag* pBag = (Bag*) GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + count += pBag->GetItemCountWithLimitCategory(limitCategory, skipItem); + + return count; +} + Item* Player::GetItemByGuid(uint64 guid) const { for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i) @@ -9634,16 +9669,16 @@ bool Player::IsBagPos(uint16 pos) return false; } -bool Player::IsValidPos(uint8 bag, uint8 slot) +bool Player::IsValidPos(uint8 bag, uint8 slot, bool explicit_pos) { // post selected - if (bag == NULL_BAG) + if (bag == NULL_BAG && !explicit_pos) return true; if (bag == INVENTORY_SLOT_BAG_0) { // any post selected - if (slot == NULL_SLOT) + if (slot == NULL_SLOT && !explicit_pos) return true; // equipment @@ -9681,7 +9716,7 @@ bool Player::IsValidPos(uint8 bag, uint8 slot) return false; // any post selected - if (slot == NULL_SLOT) + if (slot == NULL_SLOT && !explicit_pos) return true; return slot < pBag->GetBagSize(); @@ -9695,7 +9730,7 @@ bool Player::IsValidPos(uint8 bag, uint8 slot) return false; // any post selected - if (slot == NULL_SLOT) + if (slot == NULL_SLOT && !explicit_pos) return true; return slot < pBag->GetBagSize(); @@ -9850,96 +9885,6 @@ bool Player::HasItemOrGemWithLimitCategoryEquipped(uint32 limitCategory, uint32 return false; } -uint8 Player::CountItemWithLimitCategory(uint32 limitCategory, Item* skipItem) const -{ - uint8 tempcount = 0; - for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i) - { - Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (!pItem) - continue; - - if (pItem == skipItem) - continue; - - ItemPrototype const *pProto = pItem->GetProto(); - if (pProto && pProto->ItemLimitCategory == limitCategory) - tempcount += pItem->GetCount(); - } - - for (int i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i) - { - Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (!pItem) - continue; - - if (pItem == skipItem) - continue; - - ItemPrototype const *pProto = pItem->GetProto(); - if (pProto && pProto->ItemLimitCategory == limitCategory) - tempcount += pItem->GetCount(); - } - - for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) - { - Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (!pBag) - continue; - - for (uint32 j = 0; j < pBag->GetBagSize(); ++j) - { - Item *pItem = GetItemByPos(i, j); - - if (!pItem) - continue; - - if (pItem == skipItem) - continue; - - if (pItem->GetProto()->ItemLimitCategory == limitCategory) - tempcount += pItem->GetCount(); - } - } - - for (int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; ++i) - { - Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (!pItem) - continue; - - if (pItem == skipItem) - continue; - - ItemPrototype const *pProto = pItem->GetProto(); - if (pProto && pProto->ItemLimitCategory == limitCategory) - tempcount += pItem->GetCount(); - } - - for (int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i) - { - Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (!pBag) - continue; - - for (uint32 j = 0; j < pBag->GetBagSize(); ++j) - { - Item *pItem = GetItemByPos(i, j); - - if (!pItem) - continue; - - if (pItem == skipItem) - continue; - - if (pItem->GetProto()->ItemLimitCategory == limitCategory) - tempcount += pItem->GetCount(); - } - } - - return tempcount; -} - uint8 Player::_CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, uint32* no_space_count) const { ItemPrototype const *pProto = objmgr.GetItemPrototype(entry); @@ -9957,30 +9902,37 @@ uint8 Player::_CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, if (pProto->MaxCount <= 0 && pProto->ItemLimitCategory == 0 || pProto->MaxCount == 2147483647) return EQUIP_ERR_OK; - uint32 curcount; - if (pProto->ItemLimitCategory) - curcount = CountItemWithLimitCategory(pProto->ItemLimitCategory,pItem); - else - curcount = GetItemCount(pProto->ItemId,true,pItem); - - if ((curcount + count > uint32(pProto->MaxCount)) && !(pProto->ItemLimitCategory)) + if (pProto->MaxCount > 0) { - if (no_space_count) - *no_space_count = count +curcount - pProto->MaxCount; - return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; + uint32 curcount = GetItemCount(pProto->ItemId, true, pItem); + if (curcount + count > uint32(pProto->MaxCount)) + { + if (no_space_count) + *no_space_count = count + curcount - pProto->MaxCount; + return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; + } } - if (pProto->ItemLimitCategory && pProto->Class != ITEM_CLASS_GEM) // gem have equip check (maybe sort by bonding?) + // check unique-equipped limit + if (pProto->ItemLimitCategory) { ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(pProto->ItemLimitCategory); if (!limitEntry) - return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; - - if (curcount + count > limitEntry->maxCount) { if (no_space_count) - *no_space_count = count + curcount - limitEntry->maxCount; - return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; // attempt add too many limit category items + *no_space_count = count; + return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED; + } + + if (limitEntry->mode == ITEM_LIMIT_CATEGORY_MODE_HAVE) + { + uint32 curcount = GetItemCountWithLimitCategory(pProto->ItemLimitCategory, pItem); + if (curcount + count > uint32(limitEntry->maxCount)) + { + if (no_space_count) + *no_space_count = count + curcount - limitEntry->maxCount; + return EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_COUNT_EXCEEDED; + } } } @@ -10094,8 +10046,9 @@ uint8 Player::_CanStoreItem_InBag(uint8 bag, ItemPosCountVec &dest, ItemPrototyp if (bag == skip_bag) return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; + // skip not existed bag or self targeted bag Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, bag); - if (!pBag) + if (!pBag || pBag == pSrcItem) return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG; ItemPrototype const* pBagProto = pBag->GetProto(); @@ -10588,6 +10541,9 @@ uint8 Player::_CanStoreItem(uint8 bag, uint8 slot, ItemPosCountVec &dest, uint32 } } + if (pItem && pItem->IsBag() && !((Bag*)pItem)->IsEmpty()) + return EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG; + // search free slot res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,INVENTORY_SLOT_ITEM_END,dest,pProto,count,false,pItem,bag,slot); if (res != EQUIP_ERR_OK) @@ -11401,7 +11357,7 @@ void Player::SetAmmo(uint32 item) uint8 msg = CanUseAmmo(item); if (msg != EQUIP_ERR_OK) { - SendEquipError(msg, NULL, NULL); + SendEquipError(msg, NULL, NULL, item); return; } } @@ -12752,7 +12708,7 @@ void Player::RemoveItemFromBuyBackSlot(uint32 slot, bool del) } } -void Player::SendEquipError(uint8 msg, Item* pItem, Item *pItem2) +void Player::SendEquipError(uint8 msg, Item* pItem, Item *pItem2, uint32 itemid) { sLog.outDebug("WORLD: Sent SMSG_INVENTORY_CHANGE_FAILURE (%u)", msg); WorldPacket data(SMSG_INVENTORY_CHANGE_FAILURE, (msg == EQUIP_ERR_CANT_EQUIP_LEVEL_I ? 22 : 18)); @@ -12762,17 +12718,34 @@ void Player::SendEquipError(uint8 msg, Item* pItem, Item *pItem2) { data << uint64(pItem ? pItem->GetGUID() : 0); data << uint64(pItem2 ? pItem2->GetGUID() : 0); - data << uint8(0); // not 0 there... + data << uint8(0); // bag type subclass, used with EQUIP_ERR_EVENT_AUTOEQUIP_BIND_CONFIRM and EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG2 - if (msg == EQUIP_ERR_CANT_EQUIP_LEVEL_I) + switch(msg) { - uint8 level = 0; - - if (pItem) - if (ItemPrototype const* proto = pItem->GetProto()) - level = proto->RequiredLevel; - - data << uint32(level); // new 2.4.0 + case EQUIP_ERR_CANT_EQUIP_LEVEL_I: + case EQUIP_ERR_PURCHASE_LEVEL_TOO_LOW: + { + ItemPrototype const* proto = pItem ? pItem->GetProto() : objmgr.GetItemPrototype(itemid); + data << uint32(proto ? proto->RequiredLevel : 0); + break; + } + case EQUIP_ERR_EVENT_AUTOEQUIP_BIND_CONFIRM: // no idea about this one... + { + data << uint64(0); + data << uint32(0); + data << uint64(0); + break; + } + case EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_COUNT_EXCEEDED: + case EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_SOCKETED_EXCEEDED: + case EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED: + { + ItemPrototype const* proto = pItem ? pItem->GetProto() : objmgr.GetItemPrototype(itemid); + data << uint32(proto ? proto->ItemLimitCategory : 0); + break; + } + default: + break; } } GetSession()->SendPacket(&data); @@ -14049,7 +14022,7 @@ bool Player::CanAddQuest(Quest const *pQuest, bool msg) return true; else if (msg2 != EQUIP_ERR_OK) { - SendEquipError(msg2, NULL, NULL); + SendEquipError(msg2, NULL, NULL, srcitem); return false; } } @@ -14165,7 +14138,7 @@ bool Player::CanRewardQuest(Quest const *pQuest, bool msg) GetItemCount(pQuest->ReqItemId[i]) < pQuest->ReqItemCount[i]) { if (msg) - SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL); + SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL, pQuest->ReqItemId[i]); return false; } } @@ -14192,7 +14165,7 @@ bool Player::CanRewardQuest(Quest const *pQuest, uint32 reward, bool msg) uint8 res = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, pQuest->RewChoiceItemId[reward], pQuest->RewChoiceItemCount[reward]); if (res != EQUIP_ERR_OK) { - SendEquipError(res, NULL, NULL); + SendEquipError(res, NULL, NULL, pQuest->RewChoiceItemId[reward]); return false; } } @@ -14208,7 +14181,7 @@ bool Player::CanRewardQuest(Quest const *pQuest, uint32 reward, bool msg) uint8 res = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, pQuest->RewItemId[i], pQuest->RewItemCount[i]); if (res != EQUIP_ERR_OK) { - SendEquipError(res, NULL, NULL); + SendEquipError(res, NULL, NULL, pQuest->RewItemId[i]); return false; } } @@ -14929,7 +14902,7 @@ bool Player::GiveQuestSourceItem(Quest const *pQuest) else if (msg == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS) return true; else - SendEquipError(msg, NULL, NULL); + SendEquipError(msg, NULL, NULL, srcitem); return false; } @@ -14950,11 +14923,11 @@ bool Player::TakeQuestSourceItem(uint32 quest_id, bool msg) // exist one case when destroy source quest item not possible: // non un-equippable item (equipped non-empty bag, for example) - uint8 res = CanUnequipItems(srcitem,count); + uint8 res = CanUnequipItems(srcitem, count); if (res != EQUIP_ERR_OK) { if (msg) - SendEquipError(res, NULL, NULL); + SendEquipError(res, NULL, NULL, srcitem); return false; } @@ -19717,7 +19690,7 @@ bool Player::BuyItemFromVendorSlot(uint64 vendorguid, uint32 vendorslot, uint32 uint8 msg = CanStoreNewItem(bag, slot, dest, item, pProto->BuyCount * count); if (msg != EQUIP_ERR_OK) { - SendEquipError(msg, NULL, NULL); + SendEquipError(msg, NULL, NULL, item); return false; } @@ -19774,7 +19747,7 @@ bool Player::BuyItemFromVendorSlot(uint64 vendorguid, uint32 vendorslot, uint32 uint8 msg = CanEquipNewItem(slot, dest, item, false); if (msg != EQUIP_ERR_OK) { - SendEquipError(msg, NULL, NULL); + SendEquipError(msg, NULL, NULL, item); return false; } @@ -22608,18 +22581,18 @@ void Player::AutoStoreLoot(uint8 bag, uint8 slot, uint32 loot_id, LootStore cons LootItem* lootItem = loot.LootItemInSlot(i,this); ItemPosCountVec dest; - uint8 msg = CanStoreNewItem (bag,slot,dest,lootItem->itemid,lootItem->count); + uint8 msg = CanStoreNewItem(bag, slot, dest,lootItem->itemid, lootItem->count); if (msg != EQUIP_ERR_OK && slot != NULL_SLOT) - msg = CanStoreNewItem(bag, NULL_SLOT,dest,lootItem->itemid,lootItem->count); + msg = CanStoreNewItem(bag, NULL_SLOT, dest, lootItem->itemid, lootItem->count); if (msg != EQUIP_ERR_OK && bag != NULL_BAG) - msg = CanStoreNewItem(NULL_BAG, NULL_SLOT,dest,lootItem->itemid,lootItem->count); + msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, lootItem->itemid, lootItem->count); if (msg != EQUIP_ERR_OK) { - SendEquipError(msg, NULL, NULL); + SendEquipError(msg, NULL, NULL, lootItem->itemid); continue; } - Item* pItem = StoreNewItem (dest,lootItem->itemid,true,lootItem->randomPropertyId); + Item* pItem = StoreNewItem(dest,lootItem->itemid, true, lootItem->randomPropertyId); SendNewItem(pItem, lootItem->count, false, false, broadcast); } } @@ -22823,14 +22796,16 @@ uint8 Player::CanEquipUniqueItem(ItemPrototype const* itemProto, uint8 except_sl { ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(itemProto->ItemLimitCategory); if (!limitEntry) - return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE; + return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED; + + // NOTE: limitEntry->mode not checked because if item have have-limit then it applied and to equip case if (limit_count > limitEntry->maxCount) - return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE; // attempt add too many limit category items (gems) + return EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED; // there is an equip limit on this item - if (HasItemOrGemWithLimitCategoryEquipped(itemProto->ItemLimitCategory,limitEntry->maxCount-limit_count+1,except_slot)) - return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE; + if (HasItemOrGemWithLimitCategoryEquipped(itemProto->ItemLimitCategory, limitEntry->maxCount - limit_count + 1, except_slot)) + return EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED; } return EQUIP_ERR_OK; diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 85f69219195..4f8da387fb2 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1120,6 +1120,7 @@ class Player : public Unit, public GridObject<Player> void SetSheath(SheathState sheathed); // overwrite Unit version uint8 FindEquipSlot(ItemPrototype const* proto, uint32 slot, bool swap) const; uint32 GetItemCount(uint32 item, bool inBankAlso = false, Item* skipItem = NULL) const; + uint32 GetItemCountWithLimitCategory(uint32 limitCategory, Item* skipItem = NULL) const; Item* GetItemByGuid(uint64 guid) const; Item* GetItemByEntry(uint32 entry) const; Item* GetItemByPos(uint16 pos) const; @@ -1141,8 +1142,8 @@ class Player : public Unit, public GridObject<Player> static bool IsBagPos(uint16 pos); static bool IsBankPos(uint16 pos) { return IsBankPos(pos >> 8,pos & 255); } static bool IsBankPos(uint8 bag, uint8 slot); - bool IsValidPos(uint16 pos) { return IsBankPos(pos >> 8,pos & 255); } - bool IsValidPos(uint8 bag, uint8 slot); + bool IsValidPos(uint16 pos, bool explicit_pos) { return IsValidPos(pos >> 8, pos & 255, explicit_pos); } + bool IsValidPos(uint8 bag, uint8 slot, bool explicit_pos); uint8 GetBankBagSlotCount() const { return GetByteValue(PLAYER_BYTES_2, 2); } void SetBankBagSlotCount(uint8 count) { SetByteValue(PLAYER_BYTES_2, 2, count); } bool HasItemCount(uint32 item, uint32 count, bool inBankAlso = false) const; @@ -1150,7 +1151,6 @@ class Player : public Unit, public GridObject<Player> bool CanNoReagentCast(SpellEntry const* spellInfo) const; bool HasItemOrGemWithIdEquipped(uint32 item, uint32 count, uint8 except_slot = NULL_SLOT) const; bool HasItemOrGemWithLimitCategoryEquipped(uint32 limitCategory, uint32 count, uint8 except_slot = NULL_SLOT) const; - uint8 CountItemWithLimitCategory(uint32 limitCategory, Item* skipItem = NULL) const; uint8 CanTakeMoreSimilarItems(Item* pItem) const { return _CanTakeMoreSimilarItems(pItem->GetEntry(),pItem->GetCount(),pItem); } uint8 CanTakeMoreSimilarItems(uint32 entry, uint32 count) const { return _CanTakeMoreSimilarItems(entry,count,NULL); } uint8 CanStoreNewItem(uint8 bag, uint8 slot, ItemPosCountVec& dest, uint32 item, uint32 count, uint32* no_space_count = NULL) const @@ -1223,7 +1223,7 @@ class Player : public Unit, public GridObject<Player> Item* GetItemFromBuyBackSlot(uint32 slot); void RemoveItemFromBuyBackSlot(uint32 slot, bool del); uint32 GetMaxKeyringSize() const { return KEYRING_SLOT_END-KEYRING_SLOT_START; } - void SendEquipError(uint8 msg, Item* pItem, Item *pItem2); + void SendEquipError(uint8 msg, Item* pItem, Item *pItem2 = NULL, uint32 itemid = 0); void SendBuyError(uint8 msg, Creature* pCreature, uint32 item, uint32 param); void SendSellError(uint8 msg, Creature* pCreature, uint64 guid, uint32 param); void AddWeaponProficiency(uint32 newflag) { m_WeaponProficiency |= newflag; } diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index 52dbe80cd0a..b1cd770a538 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -925,7 +925,7 @@ void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers) else { item->is_blocked = false; - player->SendEquipError(msg, NULL, NULL); + player->SendEquipError(msg, NULL, NULL, roll->itemid); } } } @@ -977,7 +977,7 @@ void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers) else { item->is_blocked = false; - player->SendEquipError(msg, NULL, NULL); + player->SendEquipError(msg, NULL, NULL, roll->itemid); } } else if (rollvote == DISENCHANT) diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index fda97fed3ec..f6b66427f6a 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -3500,7 +3500,7 @@ void Map::ScriptsProcess() pReceiver->SendNewItem(item, step.script->datalong2, false, true); } else - pReceiver->SendEquipError(msg, NULL, NULL); + pReceiver->SendEquipError(msg, NULL, NULL, step.script->datalong); } break; diff --git a/src/server/game/Server/Protocol/Handlers/ItemHandler.cpp b/src/server/game/Server/Protocol/Handlers/ItemHandler.cpp index 262679cda6c..5087e541c0b 100644 --- a/src/server/game/Server/Protocol/Handlers/ItemHandler.cpp +++ b/src/server/game/Server/Protocol/Handlers/ItemHandler.cpp @@ -47,13 +47,13 @@ void WorldSession::HandleSplitItemOpcode(WorldPacket & recv_data) if (count == 0) return; //check count - if zero it's fake packet - if (!_player->IsValidPos(srcbag,srcslot)) + if (!_player->IsValidPos(srcbag, srcslot, true)) { _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL); return; } - if (!_player->IsValidPos(dstbag,dstslot)) + if (!_player->IsValidPos(dstbag, dstslot, false)) // can be autostore pos { _player->SendEquipError(EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL); return; @@ -74,13 +74,13 @@ void WorldSession::HandleSwapInvItemOpcode(WorldPacket & recv_data) if (srcslot == dstslot) return; - if (!_player->IsValidPos(INVENTORY_SLOT_BAG_0,srcslot)) + if (!_player->IsValidPos(INVENTORY_SLOT_BAG_0, srcslot, true)) { _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL); return; } - if (!_player->IsValidPos(INVENTORY_SLOT_BAG_0,dstslot)) + if (!_player->IsValidPos(INVENTORY_SLOT_BAG_0, dstslot, true)) { _player->SendEquipError(EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL); return; @@ -126,13 +126,13 @@ void WorldSession::HandleSwapItem(WorldPacket & recv_data) if (src == dst) return; - if (!_player->IsValidPos(srcbag,srcslot)) + if (!_player->IsValidPos(srcbag, srcslot, true)) { _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL); return; } - if (!_player->IsValidPos(dstbag,dstslot)) + if (!_player->IsValidPos(dstbag, dstslot, true)) { _player->SendEquipError(EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL); return; @@ -739,7 +739,11 @@ void WorldSession::SendListInventory(uint64 vendorguid) VendorItemData const* vItems = pCreature->GetVendorItems(); if (!vItems) { - _player->SendSellError(SELL_ERR_CANT_FIND_VENDOR, NULL, 0, 0); + WorldPacket data(SMSG_LIST_INVENTORY, (8+1+1)); + data << uint64(vendorguid); + data << uint8(0); // count==0, next will be error code + data << uint8(0); // "Vendor has no inventory" + SendPacket(&data); return; } @@ -786,8 +790,12 @@ void WorldSession::SendListInventory(uint64 vendorguid) } } - if (count == 0 || data.size() != 8 + 1 + size_t(count) * 8 * 4) + if (count == 0) + { + data << uint8(0); + SendPacket(&data); return; + } data.put<uint8>(count_pos, count); SendPacket(&data); @@ -805,7 +813,7 @@ void WorldSession::HandleAutoStoreBagItemOpcode(WorldPacket & recv_data) if (!pItem) return; - if (!_player->IsValidPos(dstbag,NULL_SLOT)) + if (!_player->IsValidPos(dstbag, NULL_SLOT, false)) // can be autostore pos { _player->SendEquipError(EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL); return; @@ -917,6 +925,12 @@ void WorldSession::HandleAutoBankItemOpcode(WorldPacket& recvPacket) return; } + if (dest.size() == 1 && dest[0].pos == pItem->GetPos()) + { + _player->SendEquipError(EQUIP_ERR_NONE, pItem, NULL); + return; + } + _player->RemoveItem(srcbag, srcslot, true); _player->BankItem(dest, pItem, true); } @@ -1255,6 +1269,7 @@ void WorldSession::HandleSocketOpcode(WorldPacket& recv_data) { if (ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(iGemProto->ItemLimitCategory)) { + // NOTE: limitEntry->mode is not checked because if item has limit then it is applied in equip case for (int j = 0; j < MAX_GEM_SOCKETS; ++j) { if (Gems[j]) diff --git a/src/server/game/Server/Protocol/Handlers/LootHandler.cpp b/src/server/game/Server/Protocol/Handlers/LootHandler.cpp index 0b438a15b6a..9ba7c98197a 100644 --- a/src/server/game/Server/Protocol/Handlers/LootHandler.cpp +++ b/src/server/game/Server/Protocol/Handlers/LootHandler.cpp @@ -155,7 +155,7 @@ void WorldSession::HandleAutostoreLootItemOpcode(WorldPacket & recv_data) player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM, item->itemid, item->count); } else - player->SendEquipError(msg, NULL, NULL); + player->SendEquipError(msg, NULL, NULL, item->itemid); } void WorldSession::HandleLootMoneyOpcode(WorldPacket & /*recv_data*/) @@ -526,8 +526,9 @@ void WorldSession::HandleLootMasterGiveOpcode(WorldPacket & recv_data) uint8 msg = target->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, item.itemid, item.count); if (msg != EQUIP_ERR_OK) { - target->SendEquipError(msg, NULL, NULL); - _player->SendEquipError(msg, NULL, NULL); // send duplicate of error massage to master looter + target->SendEquipError(msg, NULL, NULL, item.itemid); + // send duplicate of error massage to master looter + _player->SendEquipError(msg, NULL, NULL, item.itemid); return; } diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index ef85b293ac7..e3667913051 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -6097,7 +6097,7 @@ void AuraEffect::HandleChannelDeathItem(AuraApplication const * aurApp, uint8 mo if (msg != EQUIP_ERR_OK) { count-=noSpaceForCount; - plCaster->SendEquipError(msg, NULL, NULL); + plCaster->SendEquipError(msg, NULL, NULL, GetSpellProto()->EffectItemType[m_effIndex]); if (count == 0) return; } diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 521781ba489..cad47652544 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -6116,9 +6116,10 @@ SpellCastResult Spell::CheckItems() if (msg != EQUIP_ERR_OK) { ItemPrototype const *pProto = objmgr.GetItemPrototype(m_spellInfo->EffectItemType[i]); + // TODO: Needs review if (pProto && !(pProto->ItemLimitCategory)) { - p_caster->SendEquipError(msg, NULL, NULL); + p_caster->SendEquipError(msg, NULL, NULL, m_spellInfo->EffectItemType[i]); return SPELL_FAILED_DONT_REPORT; } else @@ -6149,7 +6150,7 @@ SpellCastResult Spell::CheckItems() uint8 msg = p_caster->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, m_spellInfo->EffectItemType[i], 1); if (msg != EQUIP_ERR_OK) { - p_caster->SendEquipError(msg, NULL, NULL); + p_caster->SendEquipError(msg, NULL, NULL, m_spellInfo->EffectItemType[i]); return SPELL_FAILED_DONT_REPORT; } } diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 4bfd8a44175..9fd56495db3 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -2878,7 +2878,7 @@ void Spell::DoCreateItem(uint32 /*i*/, uint32 itemtype) else { // if not created by another reason from full inventory or unique items amount limitation - player->SendEquipError(msg, NULL, NULL); + player->SendEquipError(msg, NULL, NULL, newitemid); return; } } diff --git a/src/server/scripts/Outland/zangarmarsh.cpp b/src/server/scripts/Outland/zangarmarsh.cpp index db50e255118..9909b81fbec 100644 --- a/src/server/scripts/Outland/zangarmarsh.cpp +++ b/src/server/scripts/Outland/zangarmarsh.cpp @@ -225,13 +225,14 @@ bool GossipSelect_npc_elder_kuruti(Player* pPlayer, Creature* pCreature, uint32 if (!pPlayer->HasItemCount(24573,1)) { ItemPosCountVec dest; - uint8 msg = pPlayer->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, 24573, 1, false); + uint32 itemId = 24573; + uint8 msg = pPlayer->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, 1, false); if (msg == EQUIP_ERR_OK) { - pPlayer->StoreNewItem(dest, 24573, true); + pPlayer->StoreNewItem(dest, itemId, true); } else - pPlayer->SendEquipError(msg,NULL,NULL); + pPlayer->SendEquipError(msg, NULL, NULL, itemId); } pPlayer->SEND_GOSSIP_MENU(9231, pCreature->GetGUID()); break; diff --git a/src/server/scripts/World/item_scripts.cpp b/src/server/scripts/World/item_scripts.cpp index 013888f909d..1c70d9b2d3d 100644 --- a/src/server/scripts/World/item_scripts.cpp +++ b/src/server/scripts/World/item_scripts.cpp @@ -96,13 +96,14 @@ bool ItemUse_item_draenei_fishing_net(Player* pPlayer, Item* /*pItem*/, SpellCas else { ItemPosCountVec dest; - uint8 msg = pPlayer->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, 23614, 1); + uint32 itemId = 23614; + uint8 msg = pPlayer->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, 1); if (msg == EQUIP_ERR_OK) { - if (Item* item = pPlayer->StoreNewItem(dest,23614,true)) - pPlayer->SendNewItem(item,1,false,true); + if (Item* item = pPlayer->StoreNewItem(dest, itemId, true)) + pPlayer->SendNewItem(item, 1, false, true); } else - pPlayer->SendEquipError(msg,NULL,NULL); + pPlayer->SendEquipError(msg, NULL, NULL, itemId); } } //} |