- This isn't final as of Jan 19th 2025 - this approach is based on testing on prepatch PTR and Midnight beta using my own characters or premades. I'll get a lot more feedback tomorrow when prepatch goes live on Raidbots and bug reports start showing up.
- Timewalking items might be funky. Need to see some new items that drop once prepatch launches.
- Some special items don't quite work (Heart of Azeroth is weird - seems to apply bonuses in a different order than expected)
- Handling future eras is speculative but it seems to make sense
- Not sure how to handle bonuses with squishEra of 0. Looks like if these bonuses are used, the era squish is skipped. But that seems extremely suspicious since it would cause weird issues in the future if my speculation about the system is correct.
Determining the item level of an item in Midnight using bonus IDs has significantly changed in Midnight in order to handle the item level squish.
I'll provide some info on what I've changed in the Raidbots bonuses.json and how I process bonus IDs using that data.
Strap in!
The big concept is what Blizzard is calling an "item squish era" which buckets items into a pre-midnight or post-midnight timespan that can be scaled accordingly. It's my speculation that this concept can handle multiple eras so it's possible they will use this again in the future when item levels get too large again.
Each era defines a scaling curve to be used for items in the previous era. So if you have a pre-midnight item (era 1), you apply the Midnight (era 2) curve to the item level to scale it down. Midnight items do not need to have any curve applied since it's "baked into" the item level. Theoretically in the future there will be an era 3 at which point Midnight items would need to be scaled using that curve. It's likely that an era 1 item would need to be scaled using the era 2 curve and then the era 3 curve in sequence.
Most new item bonuses no longer use the base level from the Item data - instead, there will be a bonus ID that sets the base level and then potentially additional bonus IDs that offset that base level. Many of these bonus IDs include their item squish era which allows you to infer which era the item came from. Existing items from recent The War Within seasons will usually have a couple of era 1 bonus IDs that set their base level. Some older items will use the "legacy" bonus IDs that still require knowing the base level of the item.
I've updated the Raidbots bonuses.json to provide this data and am using it to determine/manipulate item levels on Raidbots.
Here are some of the new fields:
itemLevel properties set the base item level
levelOffset properties apply an offset to the base level
levelOffsetSecondary properties apply an additional level offset (for things like augment/matrix items from season turbo boost)
dropLevelCurve properties are used for items that vary based on their drop level (the level the character was at when the item was acquired)
These fields have additional properties:
itemLevel: {
amount: int, // the base level to set
priority: int, // when multiple itemLevels are present, use the one with the lowest priority
squishEra: int, // the squish era for the bonus ID
curveId: int, // an additional scaling curve to use on the level before other processing
}
levelOffset: {
amount: int, // the offset to apply
squishEra: int, // the squish era for the bonus ID
craftingQuality: int, // crafting quality of the item if crafted
}
levelOffsetSecondary: {
amount: int, // the offset to apply after era processing
}
dropLevelCurve: {
curveId: int, // the drop level lookup curve to use
offset: int, // level offset to apply
priority: int, // lowest priority wins in case of duplicates
squishEra: int, // squish era for the bonus ID
}
Here's some pseudo-code for how I process these to determine item level:
// set the base item level from equippable-items.json (which comes from ItemSparse DBC)
itemLevel = item.baseItemLevel
legacyLevelOffset = 0
operations = {} // collection of operations
foreach item.bonuses as bonus
if bonus.itemLevel
add a "set level" operation for era bonus.itemLevel.squishEra (or 0 if undefined)
if bonus.levelOffset
add a "level offset" operation for era bonus.levelOffset.squishEra (or 0 if undefined)
if bonus.levelOffsetSecondary
add a "level offset" operation for the current/max era
if bonus.dropLevelCurve
add a "drop level curve" operation for era bonus.dropLevelCurve.squishEra (or 0 if undefined)
if bonus.level
legacyLevelOffset += bonus.level
if operations.size > 0
foreach itemSquishEras as itemSquishEra
if itemSquishEra.curveId
itemLevel = applyCurve(curveId, itemLevel)
apply the lowest priority "set level" operation for this era
apply the lowest priority "drop level curve" operation for this era using the item's drop level
apply all level offset operations for this era
else
// very old item that only uses older bonus IDs
itemLevel += legacyLevelOffset
To fully process bonus IDs, you'll probably need to use these additional JSON files
equippable-items.json to get the base item level for legacy items
item-curves.json to scale a value using a curve
item-squish-era.json for the list of era and the curves used for scaling
Here are some example items and a log of what my current code is doing
Rune-Branded Armbands, level 112
neck=,id=237963,bonus_id=12291/6652/9881/10255/10879/10396
'Item 237963 DBC base level 246',
'Bonus 12291: Found base item level 684, priority 9999, era 1',
'Bonus 9881: LEGACY increase ilevel by 438',
'Era 1: set item level 684 (prev 246)',
'Era 2: apply era curve 92181 level 684 -> 124'
Ring of Earthen Craftsmanship (160)
finger2=,id=215135,bonus_id=10421/9633/8902/10879/10396/9627/12050/10520/8960/8793/12053,crafted_stats=32/49,crafting_quality=5
'Item 215135 DBC base level 610',
'Bonus 10421: Found base item level 593, priority 100, era 1',
'Bonus 9627: Found item level offset 13, era 2',
'Bonus 12050: Found base item level 678, priority 70, era 1',
'Bonus 12053: Found base item level 707, priority 65, era 1',
'Era 1: set item level 707 (prev 610)',
'Era 2: apply era curve 92181 level 707 -> 147',
'Era 2: apply level offset 13, 147 -> 160'
Weather-Beaten Fishing Hat (5)
head=,id=33820
'Item 33820 DBC base level 5',
'Era 2: apply era curve 92181 level 5 -> 5'
# Sash of Herbicide (126)
waist=,id=248039,bonus_id=13573/13613/13615/6652,drop_level=80
'Item 248039 DBC base level 415',
'Bonus 13573: Found item drop level curve 92772, offset 0, priority 300, era 2',
'Bonus 13613: Found item drop level curve 92772, offset 6, priority 200, era 2',
'Era 2: apply era curve 92181 level 415 -> 73',
'Era 2: apply drop level curve 92772, 120 + 6 -> 126'
Arcanoweave Cord (275)
waist=,id=239664,bonus_id=12214/8960/12494/12066/13622/12667,crafted_stats=40/36,crafting_quality=2
'Item 239664 DBC base level 577',
'Bonus 12214: Found base item level 246, priority 100, era 2',
'Bonus 12494: Found item level offset 3, era 2',
'Bonus 12066: Found base item level 246, priority 70, era 2',
'Bonus 13622: Found base item level 272, priority 65, era 2',
'Era 2: set item level 272 (prev 577)',
'Era 2: apply level offset 3, 272 -> 275'
For folks dealing with the DBCs directly, here are some rawer notes on what I've found in ItemBonus
Value[0] curveId Curve::Id (unused?)
Value[1] itemLevel
Value[2] eraId ItemSquishEra::Id
Directly set the item level. Probably runs the item level through curve but the only examples I've seen use curve that doesn't change the item level
Value[0] scalingId ItemScalingConfig::Id
Value[1] priority
Changes behavior based on ItemScalingConfig::flags. Applies during the eraId phase. Lowest priority in the same phase is applied.
0x0 - Directly set the item level using the ItemScalingConfig making sure to apply the curve and offset from ItemOffsetCurve
0x1 - ?? Unknown - has been seen on timewalking gear. Might indicate skipping the era squish altogether?
0x2 - Scale the item level based on the item's drop level using the ItemOffsetCurve from the ItemScalingConfig
Value[0] scalingId ItemScalingConfig::Id
Value[1] priority
Set the item level based on the item's drop level and the ItemOffsetCurve from the ItemScalingConfig. Lowest priority for the era is used.
Value[0] amount
Value[1] eraId ItemSquishEra::Id
Value[2] craftingQuality?
Value[3] ?
Applies an item level offset after applying any item levels or drop level curves for the era.
Value[0] amount
Value[1] ?priority?
Applies an item level offset without regard for item squish. Used for crafted item augments