Core/Items: Implement new item bonus types and fixed item level calculation with ITEM_BONUS_SCALING_CONFIG

This commit is contained in:
luis
2026-09-13 11:43:38 -03:00
parent 2fd5aa5490
commit 2f2523d59d
6 changed files with 97 additions and 28 deletions
@@ -72,7 +72,7 @@ AuctionsBucketKey AuctionsBucketKey::ForItem(Item const* item)
return
{
item->GetEntry(),
uint16(Item::GetItemLevel(itemTemplate, *item->GetBonus(), 0, item->GetRequiredLevel(), 0, 0, 0, false, 0)),
uint16(Item::GetItemLevel(itemTemplate, *item->GetBonus(), 0, item->GetRequiredLevel(), 0, 0, 0, false, 0, 0)),
uint16(item->GetModifier(ITEM_MODIFIER_BATTLE_PET_SPECIES_ID)),
uint16(item->GetBonus()->Suffix)
};
@@ -169,7 +169,7 @@ bool AuctionBotBuyer::RollBuyChance(BuyerItemInfo const* ahInfo, AuctionPosting
Item const* item = auction->Items[0];
float itemBuyPrice = float(auction->BuyoutOrUnitPrice);
float itemPrice;
if (uint32 itemSellPrice = item->GetSellPrice(item->GetTemplate(), item->GetQuality(), item->GetItemLevel(item->GetTemplate(), *item->GetBonus(), 0, 0, 0, 0, 0, false, 0)))
if (uint32 itemSellPrice = item->GetSellPrice(item->GetTemplate(), item->GetQuality(), item->GetItemLevel(item->GetTemplate(), *item->GetBonus(), 0, 0, 0, 0, 0, false, 0, 0)))
itemPrice = float(itemSellPrice);
else
itemPrice = float(GetVendorPrice(item->GetQuality()));
@@ -214,7 +214,7 @@ bool AuctionBotBuyer::RollBidChance(BuyerItemInfo const* ahInfo, AuctionPosting
Item const* item = auction->Items[0];
float itemBidPrice = float(bidPrice);
float itemPrice;
if (uint32 itemSellPrice = item->GetSellPrice(item->GetTemplate(), item->GetQuality(), item->GetItemLevel(item->GetTemplate(), *item->GetBonus(), 0, 0, 0, 0, 0, false, 0)))
if (uint32 itemSellPrice = item->GetSellPrice(item->GetTemplate(), item->GetQuality(), item->GetItemLevel(item->GetTemplate(), *item->GetBonus(), 0, 0, 0, 0, 0, false, 0, 0)))
itemPrice = float(itemSellPrice);
else
itemPrice = float(GetVendorPrice(item->GetQuality()));
+9 -4
View File
@@ -1287,15 +1287,17 @@ enum ItemBonusType
ITEM_BONUS_OVERRIDE_REQUIRED_LEVEL = 18,
ITEM_BONUS_AZERITE_TIER_UNLOCK_SET = 19,
ITEM_BONUS_SCRAPPING_LOOT_ID = 20, /*NYI*/
ITEM_BONUS_SALVAGE_LOOT_ID = 21,
ITEM_BONUS_OVERRIDE_CAN_DISENCHANT = 22,
ITEM_BONUS_OVERRIDE_CAN_SCRAP = 23,
ITEM_BONUS_ITEM_EFFECT_ID = 24,
ITEM_BONUS_OVERRIDE_CAN_DISENCHANT = 21,
ITEM_BONUS_OVERRIDE_CAN_SCRAP = 22,
ITEM_BONUS_ITEM_EFFECT_ID = 23,
ITEM_BONUS_OVERRIDE_CANT_UNEQUIP_IN_COMBAT = 24, /*NYI*/
ITEM_BONUS_MODIFIED_CRAFTING_STAT = 25, /*NYI*/
ITEM_BONUS_REQUIRED_LEVEL_CURVE = 27,
ITEM_BONUS_ICON_FILE_DATA_ID = 28,
ITEM_BONUS_MAX_UPGRADE_SEQUENCE_VALUE = 29, /*NYI*/
ITEM_BONUS_DESCRIPTION_TEXT = 30, // Item description
ITEM_BONUS_OVERRIDE_NAME = 31, // ItemNameDescription id
ITEM_BONUS_OVERRIDE_NO_CREATOR = 32, /*NYI*/
ITEM_BONUS_UPGRADE_SEQUENCE_VALUE = 33, /*NYI*/
ITEM_BONUS_ITEM_BONUS_LIST_GROUP = 34, /*NYI*/
ITEM_BONUS_ITEM_LIMIT_CATEGORY = 35,
@@ -1303,6 +1305,7 @@ enum ItemBonusType
ITEM_BONUS_ITEM_CONVERSION = 37, /*NYI*/
ITEM_BONUS_ITEM_HISTORY_SLOT = 38, /*NYI*/
ITEM_BONUS_OVERRIDE_CAN_SALVAGE = 39,
ITEM_BONUS_SALVAGE_LOOT_ID = 40, /*NYI*/
ITEM_BONUS_OVERRIDE_CAN_RECRAFT = 41,
ITEM_BONUS_ITEM_LEVEL_BASE = 42,
ITEM_BONUS_PVP_ITEM_LEVEL_BASE = 43,
@@ -1314,6 +1317,8 @@ enum ItemBonusType
ITEM_BONUS_SCALING_CONFIG_AND_REQ_LEVEL = 49,
ITEM_BONUS_ITEM_BONUS_LIST = 50,
ITEM_BONUS_SCALING_CONFIG = 51,
ITEM_BONUS_CRAFTED_ITEM_LEVEL = 52,
ITEM_BONUS_SCALING_ITEM_LEVEL_BONUS = 53, // Applied only when ItemScalingConfig is used
};
enum class ItemCollectionType : uint8
+74 -20
View File
@@ -2281,15 +2281,15 @@ uint32 Item::GetSellPrice(ItemTemplate const* proto, uint32 quality, uint32 item
uint32 Item::GetItemLevel(Player const* owner) const
{
ItemTemplate const* itemTemplate = GetTemplate();
uint32 minItemLevel = owner->m_unitData->MinItemLevel;
uint32 minItemLevelCutoff = owner->m_unitData->MinItemLevelCutoff;
int32 minItemLevel = owner->m_unitData->MinItemLevel;
int32 minItemLevelCutoff = owner->m_unitData->MinItemLevelCutoff;
bool pvpBonus = owner->IsUsingPvpItemLevels();
uint32 maxItemLevel = pvpBonus && itemTemplate->HasFlag(ITEM_FLAG3_IGNORE_ITEM_LEVEL_CAP_IN_PVP) ? 0 : owner->m_unitData->MaxItemLevel;
int32 maxItemLevel = pvpBonus && itemTemplate->HasFlag(ITEM_FLAG3_IGNORE_ITEM_LEVEL_CAP_IN_PVP) ? 0 : owner->m_unitData->MaxItemLevel;
uint32 azeriteLevel = 0;
if (AzeriteItem const* azeriteItem = ToAzeriteItem())
azeriteLevel = azeriteItem->GetEffectiveLevel();
uint32 itemLevel = Item::GetItemLevel(itemTemplate, _bonusData, owner->GetLevel(), GetModifier(ITEM_MODIFIER_TIMEWALKER_LEVEL),
minItemLevel, minItemLevelCutoff, maxItemLevel, pvpBonus, azeriteLevel);
minItemLevel, minItemLevelCutoff, maxItemLevel, pvpBonus, azeriteLevel, GetModifier(ITEM_MODIFIER_CONTENT_TUNING_ID));
if (itemTemplate->GetArtifactID())
if (int32 mod = owner->GetTotalAuraModifier(SPELL_AURA_MOD_ARTIFACT_ITEM_LEVEL); mod > 0)
@@ -2310,8 +2310,8 @@ uint32 Item::GetItemLevel(Player const* owner, bool ignoreSquish) const
uint32 azeriteLevel = 0;
if (AzeriteItem const* azeriteItem = ToAzeriteItem())
azeriteLevel = azeriteItem->GetEffectiveLevel();
uint32 itemLevel = Item::GetItemLevel(itemTemplate, bonusData, owner->GetLevel(), GetModifier(ITEM_MODIFIER_TIMEWALKER_LEVEL),
minItemLevel, minItemLevelCutoff, maxItemLevel, pvpBonus, azeriteLevel);
uint32 itemLevel = Item::GetItemLevel(itemTemplate, _bonusData, owner->GetLevel(), GetModifier(ITEM_MODIFIER_TIMEWALKER_LEVEL),
minItemLevel, minItemLevelCutoff, maxItemLevel, pvpBonus, azeriteLevel, GetModifier(ITEM_MODIFIER_CONTENT_TUNING_ID));
if (itemTemplate->GetArtifactID())
if (int32 mod = owner->GetTotalAuraModifier(SPELL_AURA_MOD_ARTIFACT_ITEM_LEVEL); mod > 0)
@@ -2321,12 +2321,12 @@ uint32 Item::GetItemLevel(Player const* owner, bool ignoreSquish) const
}
uint32 Item::GetItemLevel(ItemTemplate const* itemTemplate, BonusData const& bonusData, uint32 level, uint32 fixedLevel,
uint32 minItemLevel, uint32 minItemLevelCutoff, uint32 maxItemLevel, bool pvpBonus, uint32 azeriteLevel)
int32 minItemLevel, int32 minItemLevelCutoff, int32 maxItemLevel, bool pvpBonus, uint32 azeriteLevel, uint32 overrideContentTuningId)
{
if (!itemTemplate)
return MIN_ITEM_LEVEL;
uint32 itemLevel = bonusData.ItemLevel;
int32 itemLevel = bonusData.ItemLevel;
if (AzeriteLevelInfoEntry const* azeriteLevelInfo = sAzeriteLevelInfoStore.LookupEntry(azeriteLevel))
itemLevel = azeriteLevelInfo->ItemLevel;
@@ -2336,26 +2336,32 @@ uint32 Item::GetItemLevel(ItemTemplate const* itemTemplate, BonusData const& bon
{
if (fixedLevel)
level = fixedLevel;
else if (Optional<ContentTuningLevels> levels = sDB2Manager.GetContentTuningData(bonusData.ContentTuningId, {}, true))
else if (Optional<ContentTuningLevels> levels = sDB2Manager.GetContentTuningData(overrideContentTuningId ? overrideContentTuningId : bonusData.ContentTuningId, {}, true))
level = std::min(std::max(int16(level), levels->MinLevel), levels->MaxLevel);
itemLevel = uint32(std::round(sDB2Manager.GetCurveValueAt(bonusData.PlayerLevelToItemLevelCurveId, level)));
itemLevel = int32(std::round(sDB2Manager.GetCurveValueAt(bonusData.PlayerLevelToItemLevelCurveId, level)));
}
itemLevel += bonusData.ItemLevelBonus;
}
else
{
uint32 curveInput = bonusData.ItemLevelOffsetItemLevel;
if (!curveInput && bonusData.ItemLevelOffset < 0)
curveInput = itemTemplate->GetBaseItemLevel();
itemLevel = std::max(int32(MIN_ITEM_LEVEL), bonusData.ItemLevelOffset + int32(std::round(sDB2Manager.GetCurveValueAt(bonusData.ItemLevelOffsetCurveId, curveInput))));
uint32 scalingLevel = bonusData.ItemLevelOffsetItemLevel;
if (bonusData.ScalingConfigUsesPlayerLevel)
scalingLevel = fixedLevel ? fixedLevel : level;
if (bonusData.RestrictScalingToContentTuning)
if (Optional<ContentTuningLevels> levels = sDB2Manager.GetContentTuningData(overrideContentTuningId, {}, true))
scalingLevel = std::min(std::max(int16(scalingLevel), levels->MinLevel), levels->MaxLevel);
itemLevel = bonusData.ItemLevelOffset + int32(std::round(sDB2Manager.GetCurveValueAt(bonusData.ItemLevelOffsetCurveId, scalingLevel)));
itemLevel += bonusData.ScalingConfigItemLevelBonus;
}
for (uint32 i = 0; i < MAX_ITEM_PROTO_SOCKETS; ++i)
itemLevel += bonusData.GemItemLevelBonus[i];
uint32 itemLevelBeforeUpgrades = itemLevel;
int32 itemLevelBeforeUpgrades = itemLevel;
if (pvpBonus)
{
@@ -2382,11 +2388,14 @@ uint32 Item::GetItemLevel(ItemTemplate const* itemTemplate, BonusData const& bon
break;
if (squish->CurveID)
itemLevel = uint32(std::round(sDB2Manager.GetCurveValueAt(squish->CurveID, itemLevel)));
itemLevel = int32(std::round(sDB2Manager.GetCurveValueAt(squish->CurveID, itemLevel)));
}
}
}
if (bonusData.ItemLevelOffsetCurveId)
itemLevel += bonusData.ScalingConfigCraftingQualityItemLevelBonus;
if (itemTemplate->GetInventoryType() != INVTYPE_NON_EQUIP)
{
if (minItemLevel && (!minItemLevelCutoff || itemLevelBeforeUpgrades >= minItemLevelCutoff) && itemLevel < minItemLevel)
@@ -2396,7 +2405,7 @@ uint32 Item::GetItemLevel(ItemTemplate const* itemTemplate, BonusData const& bon
itemLevel = maxItemLevel;
}
return std::min(std::max(itemLevel, uint32(MIN_ITEM_LEVEL)), uint32(MAX_ITEM_LEVEL));
return std::min(std::max(itemLevel, int32(MIN_ITEM_LEVEL)), int32(MAX_ITEM_LEVEL));
}
float Item::GetItemStatValue(uint32 index, Player const* owner) const
@@ -2433,7 +2442,7 @@ Optional<uint32> Item::GetDisenchantLootId() const
return _bonusData.DisenchantLootId;
// ignore temporary item level scaling (pvp or timewalking)
uint32 itemLevel = GetItemLevel(GetTemplate(), _bonusData, _bonusData.RequiredLevel, GetModifier(ITEM_MODIFIER_TIMEWALKER_LEVEL), 0, 0, 0, false, 0);
uint32 itemLevel = GetItemLevel(GetTemplate(), _bonusData, _bonusData.RequiredLevel, GetModifier(ITEM_MODIFIER_TIMEWALKER_LEVEL), 0, 0, 0, false, 0, 0);
ItemDisenchantLootEntry const* disenchantLoot = GetBaseDisenchantLoot(GetTemplate(), GetQuality(), itemLevel);
if (!disenchantLoot)
@@ -2448,7 +2457,7 @@ Optional<uint16> Item::GetDisenchantSkillRequired() const
return {};
// ignore temporary item level scaling (pvp or timewalking)
uint32 itemLevel = GetItemLevel(GetTemplate(), _bonusData, _bonusData.RequiredLevel, GetModifier(ITEM_MODIFIER_TIMEWALKER_LEVEL), 0, 0, 0, false, 0);
uint32 itemLevel = GetItemLevel(GetTemplate(), _bonusData, _bonusData.RequiredLevel, GetModifier(ITEM_MODIFIER_TIMEWALKER_LEVEL), 0, 0, 0, false, 0, 0);
ItemDisenchantLootEntry const* disenchantLoot = GetBaseDisenchantLoot(GetTemplate(), GetQuality(), itemLevel);
if (!disenchantLoot)
@@ -2997,6 +3006,8 @@ void BonusData::Initialize(ItemTemplate const* proto)
ItemLevelOffsetItemLevel = proto->GetItemLevelOffsetItemLevel();
ItemLevelOffset = 0;
ItemSquishEraID = proto->GetItemSquishEraId();
ScalingConfigItemLevelBonus = 0;
ScalingConfigCraftingQualityItemLevelBonus = 0;
EffectCount = 0;
for (ItemEffectEntry const* itemEffect : proto->Effects)
@@ -3013,6 +3024,8 @@ void BonusData::Initialize(ItemTemplate const* proto)
CanRecraft = proto->HasFlag(ITEM_FLAG4_RECRAFTABLE);
CannotTradeBindOnPickup = proto->HasFlag(ITEM_FLAG2_NO_TRADE_BIND_ON_ACQUIRE);
IgnoreSquish = false;
RestrictScalingToContentTuning = false;
ScalingConfigUsesPlayerLevel = false;
_state.SuffixPriority = std::numeric_limits<int32>::max();
_state.AppearanceModPriority = std::numeric_limits<int32>::max();
@@ -3023,6 +3036,8 @@ void BonusData::Initialize(ItemTemplate const* proto)
_state.ItemLevelPriority = std::numeric_limits<int32>::max();
_state.PvpItemLevelPriority = std::numeric_limits<int32>::max();
_state.BondingPriority = std::numeric_limits<int32>::max();
_state.ScalingConfigItemLevelBonusPriority = std::numeric_limits<int32>::max();
_state.ScalingConfigCraftingQualityItemLevelBonusPriority = std::numeric_limits<int32>::max();
_state.HasQualityBonus = false;
_state.HasItemLimitCategory = false;
}
@@ -3212,6 +3227,7 @@ void BonusData::AddBonus(uint32 type, std::array<int32, 4> const& values)
case ITEM_BONUS_SCALING_CONFIG_AND_REQ_LEVEL:
if (values[1] < _state.ScalingStatDistributionPriority)
{
_state.ScalingStatDistributionPriority = values[1];
if (ItemScalingConfigEntry const* scalingConfig = sItemScalingConfigStore.LookupEntry(values[0]))
{
if (ItemOffsetCurveEntry const* itemOffsetCurve = sItemOffsetCurveStore.LookupEntry(scalingConfig->ItemOffsetCurveID))
@@ -3220,13 +3236,16 @@ void BonusData::AddBonus(uint32 type, std::array<int32, 4> const& values)
ItemLevelOffset = itemOffsetCurve->Offset;
}
ItemLevelOffsetItemLevel = scalingConfig->ItemLevel;
ScalingConfigUsesPlayerLevel = false;
ItemSquishEraID = scalingConfig->ItemSquishEraID;
if (scalingConfig->Flags & 0x1)
IgnoreSquish = true;
if (scalingConfig->Flags & 0x2)
RestrictScalingToContentTuning = true;
if (values[1] < _state.RequiredLevelCurvePriority)
{
ItemLevelOffsetItemLevel = scalingConfig->ItemLevel;
RequiredLevelOverride = scalingConfig->RequiredLevel;
RequiredLevelCurve = 0;
}
@@ -3239,6 +3258,7 @@ void BonusData::AddBonus(uint32 type, std::array<int32, 4> const& values)
case ITEM_BONUS_SCALING_CONFIG:
if (values[1] < _state.ScalingStatDistributionPriority)
{
_state.ScalingStatDistributionPriority = values[1];
if (ItemScalingConfigEntry const* scalingConfig = sItemScalingConfigStore.LookupEntry(values[0]))
{
if (ItemOffsetCurveEntry const* itemOffsetCurve = sItemOffsetCurveStore.LookupEntry(scalingConfig->ItemOffsetCurveID))
@@ -3247,13 +3267,47 @@ void BonusData::AddBonus(uint32 type, std::array<int32, 4> const& values)
ItemLevelOffset = itemOffsetCurve->Offset;
}
ScalingConfigUsesPlayerLevel = true;
ItemLevelOffsetItemLevel = 0;
ItemSquishEraID = scalingConfig->ItemSquishEraID;
if (scalingConfig->Flags & 0x1)
IgnoreSquish = true;
if (scalingConfig->Flags & 0x2)
RestrictScalingToContentTuning = true;
}
}
break;
case ITEM_BONUS_CRAFTED_ITEM_LEVEL:
if (values[3] < _state.ScalingConfigCraftingQualityItemLevelBonusPriority)
{
bool isSquished = false;
if (std::shared_ptr<Realm const> currentRealm = sRealmList->GetCurrentRealm())
{
int32 currentBuild = ClientBuild::GetMinorMajorBugfixVersionForBuild(currentRealm->Build);
// apply all squishes between items_squish and server_squish
for (uint32 squishId = values[1] + 1; squishId < sItemSquishEraStore.GetNumRows(); ++squishId)
{
ItemSquishEraEntry const* squish = sItemSquishEraStore.LookupEntry(squishId);
if (!squish || squish->Flags & 0x1)
continue;
isSquished = squish->Patch <= currentBuild;
break;
}
}
ScalingConfigCraftingQualityItemLevelBonus = values[isSquished ? 2 : 0];
_state.ScalingConfigCraftingQualityItemLevelBonusPriority = values[3];
}
break;
case ITEM_BONUS_SCALING_ITEM_LEVEL_BONUS:
if (values[1] < _state.ScalingConfigItemLevelBonusPriority)
{
ScalingConfigItemLevelBonus = values[0];
_state.ScalingConfigItemLevelBonusPriority = values[1];
}
break;
}
}
+7 -1
View File
@@ -87,6 +87,8 @@ struct BonusData
uint32 ItemLevelOffsetItemLevel;
int32 ItemLevelOffset;
uint32 ItemSquishEraID;
int32 ScalingConfigCraftingQualityItemLevelBonus;
int32 ScalingConfigItemLevelBonus;
std::array<ItemEffectEntry const*, 13> Effects;
std::size_t EffectCount;
uint32 LimitCategory;
@@ -97,6 +99,8 @@ struct BonusData
bool HasFixedLevel;
bool CannotTradeBindOnPickup;
bool IgnoreSquish;
bool RestrictScalingToContentTuning;
bool ScalingConfigUsesPlayerLevel;
void Initialize(ItemTemplate const* proto);
void Initialize(WorldPackets::Item::ItemInstance const& itemInstance);
@@ -115,6 +119,8 @@ private:
int32 ItemLevelPriority;
int32 PvpItemLevelPriority;
int32 BondingPriority;
int32 ScalingConfigItemLevelBonusPriority;
int32 ScalingConfigCraftingQualityItemLevelBonusPriority;
bool HasQualityBonus;
bool HasItemLimitCategory;
} _state;
@@ -355,7 +361,7 @@ class TC_GAME_API Item : public Object
uint32 GetItemLevel(Player const* owner) const;
uint32 GetItemLevel(Player const* owner, bool ignoreSquish) const;
static uint32 GetItemLevel(ItemTemplate const* itemTemplate, BonusData const& bonusData, uint32 level, uint32 fixedLevel,
uint32 minItemLevel, uint32 minItemLevelCutoff, uint32 maxItemLevel, bool pvpBonus, uint32 azeriteLevel);
int32 minItemLevel, int32 minItemLevelCutoff, int32 maxItemLevel, bool pvpBonus, uint32 azeriteLevel, uint32 overrideContentTuningId);
int32 GetRequiredLevel() const;
int32 GetItemStatType(uint32 index) const { ASSERT(index < MAX_ITEM_PROTO_STATS); return _bonusData.ItemStatType[index]; }
float GetItemStatValue(uint32 index, Player const* owner) const;
@@ -284,6 +284,10 @@ enum ItemModifier : uint16
ITEM_MODIFIER_REFORGE = 58,
ITEM_MODIFIER_DBID_HIGH = 59,
ITEM_MODIFIER_DBID_LOW = 60,
ITEM_MODIFIER_CURRENCY_WALLET_ID = 61,
ITEM_MODIFIER_CURRENCY_WALLET_QUANTITY = 62,
ITEM_MODIFIER_CURRENCY_WALLET_VERSION = 63,
ITEM_MODIFIER_REDIRECTED_BASE_STATS = 64,
MAX_ITEM_MODIFIERS
};