diff --git a/Framework/Constants/ItemConst.cs b/Framework/Constants/ItemConst.cs index 6a63c55fd..2adfca9e7 100644 --- a/Framework/Constants/ItemConst.cs +++ b/Framework/Constants/ItemConst.cs @@ -339,8 +339,8 @@ namespace Framework.Constants ScalingStatDistribution = 11, DisenchantLootId = 12, ScalingStatDistribution2 = 13, - ItemLevelOverride = 14, - RandomEnchantment = 15, // Responsible for showing "" or "+%d Rank Random Minor Trait" in the tooltip before item is obtained + ItemLevelCanIncrease = 14, // Displays a + next to item level indicating it can warforge + RandomEnchantment = 15, // Responsible for showing "" or "+%d Rank Random Minor Trait" in the tooltip before item is obtained Bounding = 16, RelicType = 17 } @@ -751,7 +751,7 @@ namespace Framework.Constants Unk13 = 0x00040000, // ? Child = 0x00080000, Unk15 = 0x00100000, // ? - Unk16 = 0x00200000, // ? + NewItem = 0x00200000, // Item glows in inventory Unk17 = 0x00400000, // ? Unk18 = 0x00800000, // ? Unk19 = 0x01000000, // ? diff --git a/Framework/Database/Databases/HotfixDatabase.cs b/Framework/Database/Databases/HotfixDatabase.cs index d77c2b3cc..efcd9c9ce 100644 --- a/Framework/Database/Databases/HotfixDatabase.cs +++ b/Framework/Database/Databases/HotfixDatabase.cs @@ -476,6 +476,9 @@ namespace Framework.Database "RequiredCurrency2, RequiredCurrency3, RequiredCurrency4, RequiredCurrency5, RequiredArenaSlot, RequiredFactionId, RequiredFactionStanding, " + "RequirementFlags, RequiredAchievement FROM item_extended_cost ORDER BY ID DESC"); + // ItemLevelSelector.db2 + PrepareStatement(HotfixStatements.SEL_ITEM_LEVEL_SELECTOR, "SELECT ID, ItemLevel FROM item_level_selector ORDER BY ID DESC"); + // ItemLimitCategory.db2 PrepareStatement(HotfixStatements.SEL_ITEM_LIMIT_CATEGORY, "SELECT ID, Name, Quantity, Flags FROM item_limit_category ORDER BY ID DESC"); PrepareStatement(HotfixStatements.SEL_ITEM_LIMIT_CATEGORY_LOCALE, "SELECT ID, Name_lang FROM item_limit_category_locale WHERE locale = ?"); @@ -1255,6 +1258,8 @@ namespace Framework.Database SEL_ITEM_EXTENDED_COST, + SEL_ITEM_LEVEL_SELECTOR, + SEL_ITEM_LIMIT_CATEGORY, SEL_ITEM_LIMIT_CATEGORY_LOCALE, diff --git a/Game/DataStorage/CliDB.cs b/Game/DataStorage/CliDB.cs index 01809cbb4..8185b6d7f 100644 --- a/Game/DataStorage/CliDB.cs +++ b/Game/DataStorage/CliDB.cs @@ -140,6 +140,7 @@ namespace Game.DataStorage ItemEffectStorage = DB6Reader.Read("ItemEffect.db2", DB6Metas.ItemEffectMeta, HotfixStatements.SEL_ITEM_EFFECT); ItemStorage = DB6Reader.Read("Item.db2", DB6Metas.ItemMeta, HotfixStatements.SEL_ITEM); ItemExtendedCostStorage = DB6Reader.Read("ItemExtendedCost.db2", DB6Metas.ItemExtendedCostMeta, HotfixStatements.SEL_ITEM_EXTENDED_COST); + ItemLevelSelectorStorage = DB6Reader.Read("ItemLevelSelector.db2", DB6Metas.ItemLevelSelectorMeta, HotfixStatements.SEL_ITEM_LEVEL_SELECTOR); ItemLimitCategoryStorage = DB6Reader.Read("ItemLimitCategory.db2", DB6Metas.ItemLimitCategoryMeta, HotfixStatements.SEL_ITEM_LIMIT_CATEGORY, HotfixStatements.SEL_ITEM_LIMIT_CATEGORY_LOCALE); ItemModifiedAppearanceStorage = DB6Reader.Read("ItemModifiedAppearance.db2", DB6Metas.ItemModifiedAppearanceMeta, HotfixStatements.SEL_ITEM_MODIFIED_APPEARANCE); ItemPriceBaseStorage = DB6Reader.Read("ItemPriceBase.db2", DB6Metas.ItemPriceBaseMeta, HotfixStatements.SEL_ITEM_PRICE_BASE); @@ -464,6 +465,7 @@ namespace Game.DataStorage public static DB6Storage ItemEffectStorage; public static DB6Storage ItemStorage; public static DB6Storage ItemExtendedCostStorage; + public static DB6Storage ItemLevelSelectorStorage; public static DB6Storage ItemLimitCategoryStorage; public static DB6Storage ItemModifiedAppearanceStorage; public static DB6Storage ItemPriceBaseStorage; diff --git a/Game/DataStorage/DB2Manager.cs b/Game/DataStorage/DB2Manager.cs index 0f792c78a..515cf6753 100644 --- a/Game/DataStorage/DB2Manager.cs +++ b/Game/DataStorage/DB2Manager.cs @@ -777,6 +777,11 @@ namespace Game.DataStorage public List GetItemBonusTree(uint itemId, uint itemBonusTreeMod) { List bonusListIDs = new List(); + + ItemSparseRecord proto = CliDB.ItemSparseStorage.LookupByKey(itemId); + if (proto == null) + return bonusListIDs; + var itemIdRange = _itemToBonusTree.LookupByKey(itemId); if (itemIdRange.Empty()) return bonusListIDs; @@ -788,8 +793,27 @@ namespace Game.DataStorage continue; foreach (ItemBonusTreeNodeRecord bonusTreeNode in treeList) - if (bonusTreeNode.BonusTreeModID == itemBonusTreeMod) + { + if (bonusTreeNode.BonusTreeModID != itemBonusTreeMod) + continue; + + if (bonusTreeNode.BonusListID != 0) + { bonusListIDs.Add(bonusTreeNode.BonusListID); + } + else if (bonusTreeNode.ItemLevelSelectorID != 0) + { + ItemLevelSelectorRecord selector = CliDB.ItemLevelSelectorStorage.LookupByKey(bonusTreeNode.ItemLevelSelectorID); + if (selector == null) + continue; + + short delta = (short)(selector.ItemLevel - proto.ItemLevel); + + uint bonus = GetItemBonusListForItemLevelDelta(delta); + if (bonus != 0) + bonusListIDs.Add(bonus); + } + } } return bonusListIDs; diff --git a/Game/DataStorage/Structs/I_Records.cs b/Game/DataStorage/Structs/I_Records.cs index be6b5ac2a..aead4c47c 100644 --- a/Game/DataStorage/Structs/I_Records.cs +++ b/Game/DataStorage/Structs/I_Records.cs @@ -199,6 +199,12 @@ namespace Game.DataStorage public byte RequiredAchievement; } + public sealed class ItemLevelSelectorRecord + { + public uint ID; + public ushort ItemLevel; + } + public sealed class ItemLimitCategoryRecord { public uint Id; diff --git a/Game/Entities/Item/Item.cs b/Game/Entities/Item/Item.cs index 4dc229821..edb26acb4 100644 --- a/Game/Entities/Item/Item.cs +++ b/Game/Entities/Item/Item.cs @@ -1969,24 +1969,23 @@ namespace Game.Entities return 1; uint itemLevel = stats.GetBaseItemLevel(); - if (_bonusData.HasItemLevelBonus || _bonusData.ItemLevelOverride == 0) + ScalingStatDistributionRecord ssd = CliDB.ScalingStatDistributionStorage.LookupByKey(GetScalingStatDistribution()); + if (ssd != null) { - ScalingStatDistributionRecord ssd = CliDB.ScalingStatDistributionStorage.LookupByKey(GetScalingStatDistribution()); - if (ssd != null) - { - uint level = owner.getLevel(); - uint fixedLevel = GetModifier(ItemModifier.ScalingStatDistributionFixedLevel); - if (fixedLevel != 0) - level = fixedLevel; - uint heirloomIlvl = (uint)Global.DB2Mgr.GetCurveValueAt(ssd.ItemLevelCurveID, level); - if (heirloomIlvl != 0) - itemLevel = heirloomIlvl; - } + uint level = owner.getLevel(); - itemLevel += (uint)_bonusData.ItemLevelBonus; + uint fixedLevel = GetModifier(ItemModifier.ScalingStatDistributionFixedLevel); + if (fixedLevel != 0) + level = fixedLevel; + else + level = Math.Min(Math.Max(level, ssd.MinLevel), ssd.MaxLevel); + + uint heirloomIlvl = (uint)Global.DB2Mgr.GetCurveValueAt(ssd.ItemLevelCurveID, level); + if (heirloomIlvl != 0) + itemLevel = heirloomIlvl; } - else - itemLevel = _bonusData.ItemLevelOverride; + + itemLevel += (uint)_bonusData.ItemLevelBonus; ItemUpgradeRecord upgrade = CliDB.ItemUpgradeStorage.LookupByKey(GetModifier(ItemModifier.UpgradeId)); if (upgrade != null) @@ -2716,13 +2715,11 @@ namespace Game.Entities AppearanceModID = 0; RepairCostMultiplier = 1.0f; ScalingStatDistribution = proto.GetScalingStatDistribution(); - ItemLevelOverride = 0; RelicType = -1; HasItemLevelBonus = false; _state.AppearanceModPriority = int.MaxValue; _state.ScalingStatDistributionPriority = int.MaxValue; - _state.ItemLevelOverridePriority = int.MaxValue; _state.HasQualityBonus = false; } @@ -2807,13 +2804,6 @@ namespace Game.Entities _state.ScalingStatDistributionPriority = values[1]; } break; - case ItemBonusType.ItemLevelOverride: - if (values[1] < _state.ItemLevelOverridePriority) - { - ItemLevelOverride = (uint)values[0]; - _state.ItemLevelOverridePriority = values[1]; - } - break; case ItemBonusType.Bounding: Bonding = (ItemBondingType)values[0]; break; @@ -2835,7 +2825,6 @@ namespace Game.Entities public uint AppearanceModID; public float RepairCostMultiplier; public uint ScalingStatDistribution; - public uint ItemLevelOverride; public uint[] GemItemLevelBonus = new uint[ItemConst.MaxGemSockets]; public int[] GemRelicType = new int[ItemConst.MaxGemSockets]; public ushort[] GemRelicRankBonus = new ushort[ItemConst.MaxGemSockets]; @@ -2847,7 +2836,6 @@ namespace Game.Entities { public int AppearanceModPriority; public int ScalingStatDistributionPriority; - public int ItemLevelOverridePriority; public bool HasQualityBonus; } } diff --git a/Game/Entities/Player/Player.Items.cs b/Game/Entities/Player/Player.Items.cs index 3c45012d0..1fa604824 100644 --- a/Game/Entities/Player/Player.Items.cs +++ b/Game/Entities/Player/Player.Items.cs @@ -1271,6 +1271,7 @@ namespace Game.Entities UpdateCriteria(CriteriaTypes.ReceiveEpicItem, itemId, count); UpdateCriteria(CriteriaTypes.OwnItem, itemId, 1); + item.SetFlag(ItemFields.Flags, ItemFieldFlags.NewItem); item.SetItemRandomProperties(randomPropertyId); uint upgradeID = Global.DB2Mgr.GetRulesetItemUpgrade(itemId); diff --git a/Game/Handlers/ItemHandler.cs b/Game/Handlers/ItemHandler.cs index da5ea2b52..017843544 100644 --- a/Game/Handlers/ItemHandler.cs +++ b/Game/Handlers/ItemHandler.cs @@ -1152,5 +1152,22 @@ namespace Game // Placeholder to prevent completely locking out bags clientside SendPacket(new SortBagsResult()); } + + [WorldPacketHandler(ClientOpcodes.RemoveNewItem)] + void HandleRemoveNewItem(RemoveNewItem removeNewItem) + { + Item item = _player.GetItemByGuid(removeNewItem.ItemGuid); + if (!item) + { + Log.outDebug(LogFilter.Network, $"WorldSession.HandleRemoveNewItem: Item ({removeNewItem.ItemGuid.ToString()}) not found for {GetPlayerInfo()}!"); + return; + } + + if (item.HasFlag(ItemFields.Flags, ItemFieldFlags.NewItem)) + { + item.RemoveFlag(ItemFields.Flags, ItemFieldFlags.NewItem); + item.SetState(ItemUpdateState.Changed, _player); + } + } } } diff --git a/Game/Network/Packets/ItemPackets.cs b/Game/Network/Packets/ItemPackets.cs index 71bb15978..e85b29933 100644 --- a/Game/Network/Packets/ItemPackets.cs +++ b/Game/Network/Packets/ItemPackets.cs @@ -692,6 +692,18 @@ namespace Game.Network.Packets public override void Write() { } } + class RemoveNewItem : ClientPacket + { + public RemoveNewItem(WorldPacket packet) : base(packet) { } + + public override void Read() + { + ItemGuid = _worldPacket.ReadPackedGuid(); + } + + public ObjectGuid ItemGuid { get; set; } + } + //Structs public class ItemBonusInstanceData {