From cad2a7fa88da231b4afc4e0fbfee2de32d2cc7cc Mon Sep 17 00:00:00 2001 From: hondacrx Date: Wed, 24 Mar 2021 15:44:49 -0400 Subject: [PATCH] Core/Items: Addes support for calculating item level & cleanup iterating over items. Based on https://github.com/TrinityCore/TrinityCore/pull/26276 --- Source/Framework/Constants/PlayerConst.cs | 12 +- Source/Game/Entities/Player/Player.Items.cs | 900 +++++++------------- Source/Game/Entities/Player/Player.cs | 46 +- Source/Game/Entities/Unit/Unit.Fields.cs | 2 +- Source/Game/Handlers/AzeriteHandler.cs | 2 +- Source/Game/Handlers/CharacterHandler.cs | 3 + 6 files changed, 362 insertions(+), 603 deletions(-) diff --git a/Source/Framework/Constants/PlayerConst.cs b/Source/Framework/Constants/PlayerConst.cs index dd93fbcd6..8552f3cf1 100644 --- a/Source/Framework/Constants/PlayerConst.cs +++ b/Source/Framework/Constants/PlayerConst.cs @@ -759,13 +759,13 @@ namespace Framework.Constants public enum ItemSearchLocation { - InEquipment = 0x01, - InInventory = 0x02, - InBank = 0x04, - InReagentBank = 0x08, + Equipment = 0x01, + Inventory = 0x02, + Bank = 0x04, + ReagentBank = 0x08, - Default = InEquipment | InInventory, - Everywhere = InEquipment | InInventory | InBank | InReagentBank + Default = Equipment | Inventory, + Everywhere = Equipment | Inventory | Bank | ReagentBank } public enum ZonePVPTypeOverride diff --git a/Source/Game/Entities/Player/Player.Items.cs b/Source/Game/Entities/Player/Player.Items.cs index 1a66fd2e9..21e7065d9 100644 --- a/Source/Game/Entities/Player/Player.Items.cs +++ b/Source/Game/Entities/Player/Player.Items.cs @@ -1318,6 +1318,8 @@ namespace Game.Entities } } } + + UpdateAverageItemLevelTotal(); } return item; } @@ -1505,97 +1507,47 @@ namespace Game.Entities //Equip/Unequip Item InventoryResult CanUnequipItems(uint item, uint count) { - uint tempcount = 0; - InventoryResult res = InventoryResult.Ok; - for (byte i = EquipmentSlot.Start; i < InventorySlots.BagEnd; ++i) + uint tempcount = 0; + bool result = ForEachStorageItem(ItemSearchLocation.Equipment, (pItem, equipmentSlots, location) => { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem != null) + if (pItem.GetEntry() == item) { - if (pItem.GetEntry() == item) - { - InventoryResult ires = CanUnequipItem((ushort)(InventorySlots.Bag0 << 8 | i), false); - if (ires == InventoryResult.Ok) - { - tempcount += pItem.GetCount(); - if (tempcount >= count) - return InventoryResult.Ok; - } - else - res = ires; - } - } - } - - int inventoryEnd = InventorySlots.ItemStart + GetInventorySlotCount(); - for (byte i = InventorySlots.ItemStart; i < inventoryEnd; ++i) - { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem != null) - { - if (pItem.GetEntry() == item) + InventoryResult ires = CanUnequipItem((ushort)(InventorySlots.Bag0 << 8 | equipmentSlots), false); + if (ires != InventoryResult.Ok) + res = ires; + else { tempcount += pItem.GetCount(); if (tempcount >= count) - return InventoryResult.Ok; + return false; } } - } + return true; + }); - for (byte i = InventorySlots.ReagentStart; i < InventorySlots.ReagentEnd; ++i) + if (!result) // we stopped early due to a sucess + return InventoryResult.Ok; + + ItemSearchLocation location = ItemSearchLocation.Inventory | ItemSearchLocation.Bank | ItemSearchLocation.ReagentBank; + result = ForEachStorageItem(location, (pItem, equipmentSlots, location) => { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem) + if (pItem.GetEntry() == item) { - if (pItem.GetEntry() == item) - { - tempcount += pItem.GetCount(); - if (tempcount >= count) - return InventoryResult.Ok; - } + tempcount += pItem.GetCount(); + if (tempcount >= count) + return false; } - } + return true; + }); - for (byte i = InventorySlots.ChildEquipmentStart; i < InventorySlots.ChildEquipmentEnd; ++i) - { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem) - { - if (pItem.GetEntry() == item) - { - tempcount += pItem.GetCount(); - if (tempcount >= count) - return InventoryResult.Ok; - } - } - } + if (!result) // we stopped early due to a sucess + return InventoryResult.Ok; - for (byte i = InventorySlots.BagStart; i < InventorySlots.BagEnd; ++i) - { - Bag pBag = GetBagByPos(i); - if (pBag != null) - { - for (byte j = 0; j < pBag.GetBagSize(); ++j) - { - Item pItem = GetItemByPos(i, j); - if (pItem != null) - { - if (pItem.GetEntry() == item) - { - tempcount += pItem.GetCount(); - if (tempcount >= count) - return InventoryResult.Ok; - } - } - } - } - } - - // not found req. item count and have unequippable items - return res; + return res; // return latest error if any } + Item EquipNewItem(ushort pos, uint item, ItemContext context, bool update) { Item pItem = Item.CreateItem(item, 1, context, this); @@ -1713,6 +1665,8 @@ namespace Game.Entities UpdateCriteria(CriteriaTypes.EquipItem, pItem.GetEntry()); UpdateCriteria(CriteriaTypes.EquipEpicItem, slot, pItem.GetEntry()); + UpdateAverageItemLevelEquipped(); + return pItem; } public void EquipChildItem(byte parentBag, byte parentSlot, Item parentItem) @@ -1977,6 +1931,9 @@ namespace Game.Entities pItem.SendUpdateToPlayer(this); AutoUnequipChildItem(pItem); + + if (bag == InventorySlots.Bag0) + UpdateAverageItemLevelEquipped(); } } public void SplitItem(ushort src, ushort dst, uint count) @@ -2671,165 +2628,42 @@ namespace Game.Entities } public Item GetItemByGuid(ObjectGuid guid) { - int inventoryEnd = InventorySlots.ItemStart + GetInventorySlotCount(); - for (byte i = EquipmentSlot.Start; i < inventoryEnd; ++i) + Item result = null; + ForEachStorageItem(ItemSearchLocation.Everywhere, (pItem, equipmentSlots, location) => { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem != null) - if (pItem.GetGUID() == guid) - return pItem; - } + if (pItem.GetGUID() == guid) + { + result = pItem; + return false; + } + return true; + }); - for (byte i = InventorySlots.BankItemStart; i < InventorySlots.BankBagEnd; ++i) - { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem != null) - if (pItem.GetGUID() == guid) - return pItem; - } - - for (byte i = InventorySlots.ReagentStart; i < InventorySlots.ReagentEnd; ++i) - { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem) - if (pItem.GetGUID() == guid) - return pItem; - } - - for (byte i = InventorySlots.ChildEquipmentStart; i < InventorySlots.ChildEquipmentEnd; ++i) - { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem) - if (pItem.GetGUID() == guid) - return pItem; - } - - for (byte i = InventorySlots.BagStart; i < InventorySlots.BagEnd; ++i) - { - Bag pBag = GetBagByPos(i); - if (pBag != null) - for (byte j = 0; j < pBag.GetBagSize(); ++j) - { - Item pItem = pBag.GetItemByPos(j); - if (pItem != null) - if (pItem.GetGUID() == guid) - return pItem; - } - } - - for (byte i = InventorySlots.BankBagStart; i < InventorySlots.BankBagEnd; ++i) - { - Bag pBag = GetBagByPos(i); - if (pBag != null) - for (byte j = 0; j < pBag.GetBagSize(); ++j) - { - Item pItem = pBag.GetItemByPos(j); - if (pItem != null) - if (pItem.GetGUID() == guid) - return pItem; - } - } - return null; + return result; } public uint GetItemCount(uint item, bool inBankAlso = false, Item skipItem = null) { - uint count = 0; - int inventoryEnd = InventorySlots.ItemStart + GetInventorySlotCount(); - for (byte i = EquipmentSlot.Start; i < inventoryEnd; i++) - { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem != null) - if (pItem != skipItem && pItem.GetEntry() == item) - count += pItem.GetCount(); - } - - for (var i = InventorySlots.BagStart; i < InventorySlots.BagEnd; ++i) - { - Bag pBag = GetBagByPos(i); - if (pBag != null) - count += pBag.GetItemCount(item, skipItem); - } - - if (skipItem != null && skipItem.GetTemplate().GetGemProperties() != 0) - { - for (byte i = EquipmentSlot.Start; i < inventoryEnd; ++i) - { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem != null) - if (pItem != skipItem && pItem.GetSocketColor(0) != 0) - count += pItem.GetGemCountWithID(item); - } - } - + bool skipItemHasGemProps = skipItem != null && skipItem.GetTemplate().GetGemProperties() != 0; + ItemSearchLocation location = ItemSearchLocation.Equipment | ItemSearchLocation.Inventory | ItemSearchLocation.ReagentBank; if (inBankAlso) + location |= ItemSearchLocation.Bank; + + uint count = 0; + ForEachStorageItem(location, (pItem, equipmentSlots, location) => { - // checking every item from 39 to 74 (including bank bags) - for (var i = InventorySlots.BankItemStart; i < InventorySlots.BankBagEnd; ++i) + if (pItem != skipItem) { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem != null) - if (pItem != skipItem && pItem.GetEntry() == item) - count += pItem.GetCount(); - } - - for (var i = InventorySlots.BankBagStart; i < InventorySlots.BankBagEnd; ++i) - { - Bag pBag = GetBagByPos(i); - if (pBag != null) - count += pBag.GetItemCount(item, skipItem); - } - - if (skipItem != null && skipItem.GetTemplate().GetGemProperties() != 0) - { - for (var i = InventorySlots.BankItemStart; i < InventorySlots.BankItemEnd; ++i) - { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem != null) - if (pItem != skipItem && pItem.GetSocketColor(0) != 0) - count += pItem.GetGemCountWithID(item); - } - } - } - - for (byte i = InventorySlots.ReagentStart; i < InventorySlots.ReagentEnd; ++i) - { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem) - if (pItem != skipItem && pItem.GetEntry() == item) + if (pItem.GetEntry() == item) count += pItem.GetCount(); - } - - if (skipItem && skipItem.GetTemplate().GetGemProperties() != 0) - { - for (byte i = InventorySlots.ReagentStart; i < InventorySlots.ReagentEnd; ++i) - { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem) - if (pItem != skipItem && pItem.GetSocketColor(0) != 0) - count += pItem.GetGemCountWithID(item); + else if (skipItemHasGemProps && pItem.GetSocketColor(0) != 0) + count += pItem.GetGemCountWithID(item); } - } - - for (byte i = InventorySlots.ChildEquipmentStart; i < InventorySlots.ChildEquipmentEnd; ++i) + return true; + }, (pBag, equipmentSlots, location) => { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem) - { - if (pItem != skipItem && pItem.GetEntry() == item) - count += pItem.GetCount(); - } - } - - if (skipItem && skipItem.GetTemplate().GetGemProperties() != 0) - { - for (byte i = InventorySlots.ChildEquipmentStart; i < InventorySlots.ChildEquipmentEnd; ++i) - { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem) - if (pItem != skipItem && pItem.GetSocketColor(0) != 0) - count += pItem.GetGemCountWithID(item); - } - } + count += pBag.GetItemCount(item, skipItem); + return true; + }); return count; } @@ -2864,235 +2698,54 @@ namespace Game.Entities } public Item GetItemByEntry(uint entry, ItemSearchLocation where = ItemSearchLocation.Default) { - if (where.HasAnyFlag(ItemSearchLocation.InEquipment)) + Item result = null; + ForEachStorageItem(where, (item, equipmentSlots, location) => { - for (byte i = EquipmentSlot.Start; i < InventorySlots.BagEnd; ++i) + if (item.GetEntry() == entry) { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem != null) - if (pItem.GetEntry() == entry) - return pItem; + result = item; + return false; } - } + return true; + }); - if (where.HasAnyFlag(ItemSearchLocation.InInventory)) - { - int inventoryEnd = InventorySlots.ItemStart + GetInventorySlotCount(); - for (byte i = InventorySlots.ItemStart; i < inventoryEnd; ++i) - { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem != null) - if (pItem.GetEntry() == entry) - return pItem; - } - - for (byte i = InventorySlots.BagStart; i < InventorySlots.BagEnd; ++i) - { - Bag pBag = GetBagByPos(i); - if (pBag != null) - for (byte j = 0; j < pBag.GetBagSize(); ++j) - { - Item pItem = pBag.GetItemByPos(j); - if (pItem != null) - { - if (pItem.GetEntry() == entry) - return pItem; - } - } - } - - for (byte i = InventorySlots.ChildEquipmentStart; i < InventorySlots.ChildEquipmentEnd; ++i) - { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem) - if (pItem.GetEntry() == entry) - return pItem; - } - } - - if (where.HasAnyFlag(ItemSearchLocation.InBank)) - { - for (byte i = InventorySlots.BankItemStart; i < InventorySlots.BankItemEnd; ++i) - { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem != null) - if (pItem.GetEntry() == entry) - return pItem; - } - - for (byte i = InventorySlots.BankBagStart; i < InventorySlots.BankBagEnd; ++i) - { - Bag pBag = GetBagByPos(i); - if (pBag != null) - { - for (byte j = 0; j < pBag.GetBagSize(); ++j) - { - Item pItem = pBag.GetItemByPos(j); - if (pItem != null) - if (pItem.GetEntry() == entry) - return pItem; - } - } - } - } - - if (where.HasAnyFlag(ItemSearchLocation.InReagentBank)) - { - for (byte i = InventorySlots.ReagentStart; i < InventorySlots.ReagentEnd; ++i) - { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem != null) - if (pItem.GetEntry() == entry) - return pItem; - } - } - - return null; + return result; } public List GetItemListByEntry(uint entry, bool inBankAlso = false) { - List itemList = new(); - - int inventoryEnd = InventorySlots.ItemStart + GetInventorySlotCount(); - for (byte i = InventorySlots.ItemStart; i < inventoryEnd; ++i) - { - Item item = GetItemByPos(InventorySlots.Bag0, i); - if (item != null) - if (item.GetEntry() == entry) - itemList.Add(item); - } - - for (byte i = InventorySlots.BagStart; i < InventorySlots.BagEnd; ++i) - { - Bag bag = GetBagByPos(i); - if (bag) - { - for (byte j = 0; j < bag.GetBagSize(); ++j) - { - Item item = bag.GetItemByPos(j); - if (item != null) - if (item.GetEntry() == entry) - itemList.Add(item); - } - } - } - - for (byte i = EquipmentSlot.Start; i < InventorySlots.BagEnd; ++i) - { - Item item = GetItemByPos(InventorySlots.Bag0, i); - if (item) - if (item.GetEntry() == entry) - itemList.Add(item); - } - + ItemSearchLocation location = ItemSearchLocation.Equipment | ItemSearchLocation.Inventory | ItemSearchLocation.ReagentBank; if (inBankAlso) - { - for (byte i = InventorySlots.BankItemStart; i < InventorySlots.BankBagEnd; ++i) - { - Item item = GetItemByPos(InventorySlots.Bag0, i); - if (item) - if (item.GetEntry() == entry) - itemList.Add(item); - } - } + location |= ItemSearchLocation.Bank; - for (byte i = InventorySlots.ChildEquipmentStart; i < InventorySlots.ChildEquipmentEnd; ++i) + List itemList = new(); + ForEachStorageItem(location, (item, equipmentSlots, location) => { - Item item = GetItemByPos(InventorySlots.Bag0, i); - if (item) - if (item.GetEntry() == entry) - itemList.Add(item); - } + if (item.GetEntry() == entry) + itemList.Add(item); + return true; + }); return itemList; + } public bool HasItemCount(uint item, uint count = 1, bool inBankAlso = false) { - uint tempcount = 0; - int inventoryEnd = InventorySlots.ItemStart + GetInventorySlotCount(); - for (byte i = EquipmentSlot.Start; i < inventoryEnd; i++) - { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem != null && pItem.GetEntry() == item && !pItem.IsInTrade()) - { - tempcount += pItem.GetCount(); - if (tempcount >= count) - return true; - } - } - - for (byte i = InventorySlots.BagStart; i < InventorySlots.BagEnd; i++) - { - Bag pBag = GetBagByPos(i); - if (pBag != null) - { - for (byte j = 0; j < pBag.GetBagSize(); j++) - { - Item pItem = GetItemByPos(i, j); - if (pItem != null && pItem.GetEntry() == item && !pItem.IsInTrade()) - { - tempcount += pItem.GetCount(); - if (tempcount >= count) - return true; - } - } - } - } - + ItemSearchLocation location = ItemSearchLocation.Equipment | ItemSearchLocation.Inventory | ItemSearchLocation.ReagentBank; if (inBankAlso) - { - for (byte i = InventorySlots.BankItemStart; i < InventorySlots.BankBagEnd; i++) - { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem != null && pItem.GetEntry() == item && !pItem.IsInTrade()) - { - tempcount += pItem.GetCount(); - if (tempcount >= count) - return true; - } - } - for (byte i = InventorySlots.BankBagStart; i < InventorySlots.BankBagEnd; i++) - { - Bag pBag = GetBagByPos(i); - if (pBag != null) - { - for (byte j = 0; j < pBag.GetBagSize(); j++) - { - Item pItem = GetItemByPos(i, j); - if (pItem != null && pItem.GetEntry() == item && !pItem.IsInTrade()) - { - tempcount += pItem.GetCount(); - if (tempcount >= count) - return true; - } - } - } - } - } + location |= ItemSearchLocation.Bank; - for (byte i = InventorySlots.ReagentStart; i < InventorySlots.ReagentEnd; i++) + uint currentCount = 0; + return !ForEachStorageItem(location, (pItem, equipmentSlots, location) => { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); if (pItem && pItem.GetEntry() == item && !pItem.IsInTrade()) { - tempcount += pItem.GetCount(); - if (tempcount >= count) - return true; - } - } - for (byte i = InventorySlots.ChildEquipmentStart; i < InventorySlots.ChildEquipmentEnd; i++) - { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem && pItem.GetEntry() == item && !pItem.IsInTrade()) - { - tempcount += pItem.GetCount(); - if (tempcount >= count) - return true; + currentCount += pItem.GetCount(); + if (currentCount >= count) + return false; } - } - - return false; + return true; + }); } public static bool IsChildEquipmentPos(byte bag, byte slot) { @@ -3151,102 +2804,38 @@ namespace Game.Entities public Item GetChildItemByGuid(ObjectGuid guid) { - int inventoryEnd = InventorySlots.ItemStart + GetInventorySlotCount(); - for (byte i = EquipmentSlot.Start; i < inventoryEnd; ++i) + Item result = null; + ForEachStorageItem(ItemSearchLocation.Equipment, (pItem, equipmentSlots, location) => { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem) - if (pItem.GetGUID() == guid) - return pItem; - } + if (pItem.GetGUID() == guid) + { + result = pItem; + return false; + } + return true; + }); - for (byte i = InventorySlots.ChildEquipmentStart; i < InventorySlots.ChildEquipmentEnd; ++i) - { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem) - if (pItem.GetGUID() == guid) - return pItem; - } - - return null; + return result; } uint GetItemCountWithLimitCategory(uint limitCategory, Item skipItem) { uint count = 0; - int inventoryEnd = InventorySlots.ItemStart + GetInventorySlotCount(); - for (byte i = EquipmentSlot.Start; i < inventoryEnd; ++i) + ForEachStorageItem(ItemSearchLocation.Everywhere, (pItem, equipmentSlots, location) => { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem) + if (pItem != skipItem) { - if (pItem != skipItem) - { - ItemTemplate pProto = pItem.GetTemplate(); - if (pProto != null) - if (pProto.GetItemLimitCategory() == limitCategory) - count += pItem.GetCount(); - } + ItemTemplate pProto = pItem.GetTemplate(); + if (pProto != null) + if (pProto.GetItemLimitCategory() == limitCategory) + count += pItem.GetCount(); } - } - - for (byte i = InventorySlots.BagStart; i < InventorySlots.BagEnd; ++i) + return true; + }, (pBag, equipmentSlots, location) => { - Bag pBag = GetBagByPos(i); - if (pBag) - count += pBag.GetItemCountWithLimitCategory(limitCategory, skipItem); - } + count += pBag.GetItemCountWithLimitCategory(limitCategory, skipItem); - for (byte i = InventorySlots.BankItemStart; i < InventorySlots.BankItemEnd; ++i) - { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem) - { - if (pItem != skipItem) - { - ItemTemplate pProto = pItem.GetTemplate(); - if (pProto != null) - if (pProto.GetItemLimitCategory() == limitCategory) - count += pItem.GetCount(); - } - } - } - - for (byte i = InventorySlots.BankBagStart; i < InventorySlots.BankBagEnd; ++i) - { - Bag pBag = GetBagByPos(i); - if (pBag) - count += pBag.GetItemCountWithLimitCategory(limitCategory, skipItem); - } - - for (byte i = InventorySlots.ReagentStart; i < InventorySlots.ReagentEnd; ++i) - { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem) - { - if (pItem != skipItem) - { - ItemTemplate pProto = pItem.GetTemplate(); - if (pProto != null) - if (pProto.GetItemLimitCategory() == limitCategory) - count += pItem.GetCount(); - } - } - } - - for (byte i = InventorySlots.ChildEquipmentStart; i < InventorySlots.ChildEquipmentEnd; ++i) - { - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem) - { - if (pItem != skipItem) - { - ItemTemplate pProto = pItem.GetTemplate(); - if (pProto != null) - if (pProto.GetItemLimitCategory() == limitCategory) - count += pItem.GetCount(); - } - } - } + return true; + }); return count; } @@ -5546,7 +5135,7 @@ namespace Game.Entities AzeriteEmpoweredItem azeriteEmpoweredItem = item.ToAzeriteEmpoweredItem(); if (azeriteEmpoweredItem) { - if (!apply || GetItemByEntry(PlayerConst.ItemIdHeartOfAzeroth, ItemSearchLocation.InEquipment)) + if (!apply || GetItemByEntry(PlayerConst.ItemIdHeartOfAzeroth, ItemSearchLocation.Equipment)) { for (int i = 0; i < SharedConst.MaxAzeriteEmpoweredTier; ++i) { @@ -5642,92 +5231,63 @@ namespace Game.Entities public bool HasItemOrGemWithIdEquipped(uint item, uint count, byte except_slot = ItemConst.NullSlot) { uint tempcount = 0; - for (byte i = EquipmentSlot.Start; i < EquipmentSlot.End; ++i) - { - if (i == except_slot) - continue; - - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem != null && pItem.GetEntry() == item) - { - tempcount += pItem.GetCount(); - if (tempcount >= count) - return true; - } - } - ItemTemplate pProto = Global.ObjectMgr.GetItemTemplate(item); - if (pProto != null && pProto.GetGemProperties() != 0) + bool hasGemProps = pProto?.GetGemProperties() != 0; + return !ForEachStorageItem(ItemSearchLocation.Equipment, (pItem, equipmentSlots, location) => { - for (byte i = EquipmentSlot.Start; i < EquipmentSlot.End; ++i) + if (equipmentSlots != except_slot) { - if (i == except_slot) - continue; - - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (pItem != null && pItem.GetSocketColor(0) != 0) - { + if (pItem.GetEntry() == item) + tempcount += pItem.GetCount(); + else if (pItem?.GetSocketColor(0) != 0) tempcount += pItem.GetGemCountWithID(item); - if (tempcount >= count) - return true; - } - } - } - return false; + if (tempcount >= count) + return false; + } + return true; + }); } bool HasItemWithLimitCategoryEquipped(uint limitCategory, uint count, byte except_slot) { uint tempcount = 0; - for (byte i = EquipmentSlot.Start; i < EquipmentSlot.End; ++i) + return !ForEachStorageItem(ItemSearchLocation.Equipment, (pItem, equipmentSlots, location) => { - if (i == except_slot) - continue; - - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (!pItem) - continue; + if (equipmentSlots == except_slot) + return true; ItemTemplate pProto = pItem.GetTemplate(); if (pProto == null) - continue; + return true; - if (pProto.GetItemLimitCategory() == limitCategory) - { - tempcount += pItem.GetCount(); - if (tempcount >= count) - return true; - } - } + if (pProto.GetItemLimitCategory() != limitCategory) + return true; - return false; + tempcount += pItem.GetCount(); + return (tempcount < count); + }); } bool HasGemWithLimitCategoryEquipped(uint limitCategory, uint count, byte except_slot) { uint tempcount = 0; - for (byte i = EquipmentSlot.Start; i < EquipmentSlot.End; ++i) + return !ForEachStorageItem(ItemSearchLocation.Equipment, (pItem, equipmentSlots, location) => { - if (i == except_slot) - continue; - - Item pItem = GetItemByPos(InventorySlots.Bag0, i); - if (!pItem) - continue; + if (equipmentSlots == except_slot) + return true; ItemTemplate pProto = pItem.GetTemplate(); if (pProto == null) - continue; + return true; if (pItem.GetSocketColor(0) != 0 || pItem.GetEnchantmentId(EnchantmentSlot.Prismatic) != 0) { tempcount += pItem.GetGemCountWithLimitCategory(limitCategory); if (tempcount >= count) - return true; + return false; } - } - - return false; + return true; + }); } //Visual @@ -5863,6 +5423,10 @@ namespace Game.Entities pItem.SetContainedIn(ObjectGuid.Empty); pItem.SetSlot(ItemConst.NullSlot); pItem.SetState(ItemUpdateState.Removed, this); + + UpdateAverageItemLevelTotal(); + if (bag == InventorySlots.Bag0) + UpdateAverageItemLevelEquipped(); } } @@ -6961,5 +6525,197 @@ namespace Game.Entities } // @todo other types of power scaling such as timewalking } + + bool ForEachStorageItem(ItemSearchLocation location, Func callback, Func bagCallback) + { + if (location.HasAnyFlag(ItemSearchLocation.Equipment)) + { + for (byte i = EquipmentSlot.Start; i < EquipmentSlot.End; i++) + { + Item item = GetItemByPos(InventorySlots.Bag0, i); + if (item != null) + if (!callback(item, i, ItemSearchLocation.Equipment)) + return false; + } + } + + if (location.HasAnyFlag(ItemSearchLocation.Inventory)) + { + for (byte i = InventorySlots.ItemStart; i < InventorySlots.ItemStart + GetInventorySlotCount(); i++) + { + Item item = GetItemByPos(InventorySlots.Bag0, i); + if (item != null) + if (!callback(item, i, ItemSearchLocation.Inventory)) + return false; + } + + for (byte i = InventorySlots.ChildEquipmentStart; i < InventorySlots.ChildEquipmentEnd; ++i) + { + Item item = GetItemByPos(InventorySlots.Bag0, i); + if (item != null) + if (!callback(item, i, ItemSearchLocation.Inventory)) + return false; + } + + for (byte i = InventorySlots.BagStart; i < InventorySlots.BagEnd; i++) + { + Bag bag = GetBagByPos(i); + if (bag != null) + if (!bagCallback(bag, i, ItemSearchLocation.Inventory)) + return false; + } + } + + if (location.HasAnyFlag(ItemSearchLocation.Bank)) + { + for (byte i = InventorySlots.BankItemStart; i < InventorySlots.BankItemEnd; ++i) + { + Item item = GetItemByPos(InventorySlots.Bag0, i); + if (item != null) + if (!callback(item, i, ItemSearchLocation.Bank)) + return false; + } + + for (byte i = InventorySlots.BankBagStart; i < InventorySlots.BankBagEnd; ++i) + { + Bag bag = GetBagByPos(i); + if (bag != null) + if (!bagCallback(bag, i, ItemSearchLocation.Bank)) + return false; + } + } + + if (location.HasAnyFlag(ItemSearchLocation.ReagentBank)) + { + for (byte i = InventorySlots.ReagentStart; i < InventorySlots.ReagentEnd; ++i) + { + Item item = GetItemByPos(InventorySlots.Bag0, i); + if (item != null) + if (!callback(item, i, ItemSearchLocation.ReagentBank)) + return false; + } + } + + return true; + } + + bool ForEachStorageItem(ItemSearchLocation location, Func callback) + { + return ForEachStorageItem(location, callback, (pBag, equipmentSlots, callbackLocation) => + { + for (byte j = 0; j < pBag.GetBagSize(); j++) + { + Item pItem = pBag.GetItemByPos(j); + if (pItem != null) + if (!callback(pItem, equipmentSlots, callbackLocation)) + return false; + } + return true; + }); + } + + bool ForEachEquipmentSlot(InventoryType inventoryType, bool canDualWield, bool canTitanGrip, Action callback) + { + switch (inventoryType) + { + case InventoryType.Head: callback(EquipmentSlot.Head); return true; + case InventoryType.Neck: callback(EquipmentSlot.Neck); return true; + case InventoryType.Shoulders: callback(EquipmentSlot.Shoulders); return true; + case InventoryType.Body: callback(EquipmentSlot.Shirt); return true; + case InventoryType.Robe: + case InventoryType.Chest: callback(EquipmentSlot.Chest); return true; + case InventoryType.Waist: callback(EquipmentSlot.Waist); return true; + case InventoryType.Legs: callback(EquipmentSlot.Legs); return true; + case InventoryType.Feet: callback(EquipmentSlot.Feet); return true; + case InventoryType.Wrists: callback(EquipmentSlot.Wrist); return true; + case InventoryType.Hands: callback(EquipmentSlot.Hands); return true; + case InventoryType.Cloak: callback(EquipmentSlot.Cloak); return true; + case InventoryType.Finger: + callback(EquipmentSlot.Finger1); + callback(EquipmentSlot.Finger2); + return true; + case InventoryType.Trinket: + callback(EquipmentSlot.Trinket1); + callback(EquipmentSlot.Trinket2); + return true; + case InventoryType.Weapon: + callback(EquipmentSlot.MainHand); + if (canDualWield) + callback(EquipmentSlot.OffHand); + return true; + case InventoryType.Weapon2Hand: + callback(EquipmentSlot.MainHand); + if (canDualWield && canTitanGrip) + callback(EquipmentSlot.OffHand); + return true; + case InventoryType.Ranged: + case InventoryType.RangedRight: + case InventoryType.WeaponMainhand: callback(EquipmentSlot.MainHand); return true; + case InventoryType.Shield: + case InventoryType.Holdable: + case InventoryType.WeaponOffhand: callback(EquipmentSlot.OffHand); return true; + default: + return false; + } + } + + public void UpdateAverageItemLevelTotal() + { + Dictionary> bestItemLevels = new(); + float sum = 0; + + ForEachStorageItem(ItemSearchLocation.Everywhere, (item, equipmentSlots, location) => + { + ItemTemplate itemTemplate = item.GetTemplate(); + if (itemTemplate != null) + { + if (CanEquipItem(ItemConst.NullSlot, out ushort dest, item, true, false) == InventoryResult.Ok) + { + uint itemLevel = item.GetItemLevel(this); + InventoryType inventoryType = itemTemplate.GetInventoryType(); + ForEachEquipmentSlot(inventoryType, m_canDualWield, m_canTitanGrip, slot => + { + if (!bestItemLevels.TryGetValue(slot, out Tuple pair)) + { + bestItemLevels[slot] = Tuple.Create(inventoryType, itemLevel); + sum += itemLevel; + } + else if (itemLevel > pair.Item2) + { + sum += itemLevel - pair.Item2; + bestItemLevels[slot] = Tuple.Create(pair.Item1, itemLevel); + } + }); + } + } + return true; + }); + + // If main hand is a 2h weapon, count it twice + if (!bestItemLevels.TryGetValue(EquipmentSlot.MainHand, out Tuple mainHand) && mainHand.Item1 == InventoryType.Weapon2Hand) + sum += mainHand.Item2; + + sum /= 16.0f; + UpdateAverageItemLevelTotal(sum); + } + + public void UpdateAverageItemLevelEquipped() + { + float totalItemLevel = 0; + for (byte i = EquipmentSlot.Start; i < EquipmentSlot.End; i++) + { + Item item = GetItemByPos(InventorySlots.Bag0, i); + if (item != null) + { + uint itemLevel = item.GetItemLevel(this); + totalItemLevel += itemLevel; + if (i == EquipmentSlot.MainHand && item.GetTemplate().GetInventoryType() == InventoryType.Weapon2Hand) // 2h weapon counts twice + totalItemLevel += itemLevel; + } + } + + totalItemLevel /= 16.0f; + UpdateAverageItemLevelEquipped(totalItemLevel); + } } } diff --git a/Source/Game/Entities/Player/Player.cs b/Source/Game/Entities/Player/Player.cs index e464ad52b..ce2adf3da 100644 --- a/Source/Game/Entities/Player/Player.cs +++ b/Source/Game/Entities/Player/Player.cs @@ -302,7 +302,7 @@ namespace Game.Entities if (msg == InventoryResult.Ok) { RemoveItem(InventorySlots.Bag0, i, true); - pItem = StoreItem(sDest, pItem, true); + StoreItem(sDest, pItem, true); } } } @@ -1854,7 +1854,7 @@ namespace Game.Entities public uint GetStartLevel(Race race, Class playerClass, Optional characterTemplateId = default) { uint startLevel = WorldConfig.GetUIntValue(WorldCfg.StartPlayerLevel); - if (CliDB.ChrRacesStorage.LookupByKey(race).GetFlags().HasFlag(ChrRacesFlag.AlliedRace)) + if (CliDB.ChrRacesStorage.LookupByKey(race).GetFlags().HasAnyFlag(ChrRacesFlag.AlliedRace)) startLevel = WorldConfig.GetUIntValue(WorldCfg.StartAlliedRaceLevel); if (playerClass == Class.Deathknight) @@ -2345,8 +2345,8 @@ namespace Game.Entities if (canTalk) { - string strOptionText = ""; - string strBoxText = ""; + string strOptionText; + string strBoxText; BroadcastTextRecord optionBroadcastText = CliDB.BroadcastTextStorage.LookupByKey(menuItems.OptionBroadcastTextId); BroadcastTextRecord boxBroadcastText = CliDB.BroadcastTextStorage.LookupByKey(menuItems.BoxBroadcastTextId); Locale locale = GetSession().GetSessionDbLocaleIndex(); @@ -4024,7 +4024,7 @@ namespace Game.Entities return false; } - + public override bool IsNeverVisibleFor(WorldObject seer) { if (base.IsNeverVisibleFor(seer)) @@ -4807,7 +4807,7 @@ namespace Game.Entities if (pet != null) pet.RemoveAurasDueToSpell(petSpell.GetAura(pet.GetEntry())); } - + public bool InArena() { Battleground bg = GetBattleground(); @@ -4955,7 +4955,7 @@ namespace Game.Entities reward.ItemChoices.Add(rewardEntry); } - + playerChoiceResponse.Reward.Set(reward); displayPlayerChoice.Responses[i] = playerChoiceResponse; } @@ -6141,7 +6141,7 @@ namespace Game.Entities { ushort areaLevel = (ushort)Math.Min(Math.Max((ushort)GetLevel(), areaLevels.Value.MinLevel), areaLevels.Value.MaxLevel); int diff = (int)(GetLevel()) - areaLevel; - uint XP = 0; + uint XP; if (diff < -5) { XP = (uint)(Global.ObjectMgr.GetBaseXP(GetLevel() + 5) * WorldConfig.GetFloatValue(WorldCfg.RateXpExplore)); @@ -6523,13 +6523,13 @@ namespace Game.Entities switch (modGroup) { - case BaseModGroup.CritPercentage: - UpdateCritPercentage(WeaponAttackType.BaseAttack); + case BaseModGroup.CritPercentage: + UpdateCritPercentage(WeaponAttackType.BaseAttack); break; - case BaseModGroup.RangedCritPercentage: - UpdateCritPercentage(WeaponAttackType.RangedAttack); + case BaseModGroup.RangedCritPercentage: + UpdateCritPercentage(WeaponAttackType.RangedAttack); break; - case BaseModGroup.OffhandCritPercentage: + case BaseModGroup.OffhandCritPercentage: UpdateCritPercentage(WeaponAttackType.OffAttack); break; default: @@ -6558,7 +6558,7 @@ namespace Game.Entities return m_auraBaseFlatMod[(int)modGroup] * m_auraBasePctMod[(int)modGroup]; } - + public void AddComboPoints(sbyte count, Spell spell = null) { if (count == 0) @@ -7208,15 +7208,12 @@ namespace Game.Entities } public static WeaponAttackType GetAttackBySlot(byte slot, InventoryType inventoryType) { - switch (slot) + return slot switch { - case EquipmentSlot.MainHand: - return inventoryType != InventoryType.Ranged && inventoryType != InventoryType.RangedRight ? WeaponAttackType.BaseAttack : WeaponAttackType.RangedAttack; - case EquipmentSlot.OffHand: - return WeaponAttackType.OffAttack; - default: - return WeaponAttackType.Max; - } + EquipmentSlot.MainHand => inventoryType != InventoryType.Ranged && inventoryType != InventoryType.RangedRight ? WeaponAttackType.BaseAttack : WeaponAttackType.RangedAttack, + EquipmentSlot.OffHand => WeaponAttackType.OffAttack, + _ => WeaponAttackType.Max, + }; } public void AutoUnequipOffhandIfNeed(bool force = false) { @@ -7290,6 +7287,9 @@ namespace Game.Entities public void RemovePlayerFlagEx(PlayerFlagsEx flags) { RemoveUpdateFieldFlagValue(m_values.ModifyValue(m_playerData).ModifyValue(m_playerData.PlayerFlagsEx), (uint)flags); } public void SetPlayerFlagsEx(PlayerFlagsEx flags) { SetUpdateFieldValue(m_values.ModifyValue(m_playerData).ModifyValue(m_playerData.PlayerFlagsEx), (uint)flags); } + public void UpdateAverageItemLevelTotal(float newItemLevel) { SetUpdateFieldValue(ref m_values.ModifyValue(m_playerData).ModifyValue(m_playerData.AvgItemLevel, 0), newItemLevel); } + public void UpdateAverageItemLevelEquipped(float newItemLevel) { SetUpdateFieldValue(ref m_values.ModifyValue(m_playerData).ModifyValue(m_playerData.AvgItemLevel, 1), newItemLevel); } + public uint GetCustomizationChoice(uint chrCustomizationOptionId) { int choiceIndex = m_playerData.Customizations.FindIndexIf(choice => @@ -7317,7 +7317,7 @@ namespace Game.Entities AddDynamicUpdateFieldValue(m_values.ModifyValue(m_playerData).ModifyValue(m_playerData.Customizations), newChoice); } } - + public Gender GetNativeSex() { return (Gender)(byte)m_playerData.NativeSex; } public void SetNativeSex(Gender sex) { SetUpdateFieldValue(m_values.ModifyValue(m_playerData).ModifyValue(m_playerData.NativeSex), (byte)sex); } public void SetPvpTitle(byte pvpTitle) { SetUpdateFieldValue(m_values.ModifyValue(m_playerData).ModifyValue(m_playerData.PvpTitle), pvpTitle); } diff --git a/Source/Game/Entities/Unit/Unit.Fields.cs b/Source/Game/Entities/Unit/Unit.Fields.cs index eb55a6dc1..bb767b105 100644 --- a/Source/Game/Entities/Unit/Unit.Fields.cs +++ b/Source/Game/Entities/Unit/Unit.Fields.cs @@ -66,7 +66,7 @@ namespace Game.Entities public float ModMeleeHitChance { get; set; } public float ModRangedHitChance { get; set; } public float ModSpellHitChance { get; set; } - bool m_canDualWield; + public bool m_canDualWield; public int BaseSpellCritChance { get; set; } public uint RegenTimer { get; set; } uint combatTimer; diff --git a/Source/Game/Handlers/AzeriteHandler.cs b/Source/Game/Handlers/AzeriteHandler.cs index 0604e262b..22243f5d6 100644 --- a/Source/Game/Handlers/AzeriteHandler.cs +++ b/Source/Game/Handlers/AzeriteHandler.cs @@ -65,7 +65,7 @@ namespace Game ActivateEssenceFailed activateEssenceResult = new(); activateEssenceResult.AzeriteEssenceID = azeriteEssenceActivateEssence.AzeriteEssenceID; - Item item = _player.GetItemByEntry(PlayerConst.ItemIdHeartOfAzeroth, ItemSearchLocation.InEquipment); + Item item = _player.GetItemByEntry(PlayerConst.ItemIdHeartOfAzeroth, ItemSearchLocation.Equipment); if (item == null) { activateEssenceResult.Reason = AzeriteEssenceActivateResult.NotEquipped; diff --git a/Source/Game/Handlers/CharacterHandler.cs b/Source/Game/Handlers/CharacterHandler.cs index ed12392cd..33d300226 100644 --- a/Source/Game/Handlers/CharacterHandler.cs +++ b/Source/Game/Handlers/CharacterHandler.cs @@ -1024,6 +1024,9 @@ namespace Game if (!pCurrChar.IsStandState() && !pCurrChar.HasUnitState(UnitState.Stunned)) pCurrChar.SetStandState(UnitStandStateType.Stand); + pCurrChar.UpdateAverageItemLevelTotal(); + pCurrChar.UpdateAverageItemLevelEquipped(); + m_playerLoading.Clear(); Global.ScriptMgr.OnPlayerLogin(pCurrChar);