diff --git a/spec/System/TestItemParse_spec.lua b/spec/System/TestItemParse_spec.lua
index 66bc1cdb394..0bb61b3f6fb 100644
--- a/spec/System/TestItemParse_spec.lua
+++ b/spec/System/TestItemParse_spec.lua
@@ -54,6 +54,50 @@ describe("TestItemParse", function()
data.itemBases[baseName] = nil
end)
+ it("keeps saved rolls on their lines for a talisman saved before its base enchant", function()
+ local baseName = "Test Talisman"
+ data.itemBases[baseName] = {
+ type = "Amulet",
+ subType = "Talisman",
+ tags = { amulet = true, talisman = true },
+ req = { },
+ enchant = "+10 to Strength",
+ }
+ loadBuildFromXML([[
+
+
+
+ -
+Rarity: RARE
+Old Save
+Test Talisman
+Implicits: 1
++(10-20) to Dexterity
+
+
+
+
+]])
+ data.itemBases[baseName] = nil
+
+ assert.are.equals(1, build.itemsTab.items[1].implicitModLines[1].range)
+ end)
+
+ it("does not add a talisman base enchant already saved as an implicit", function()
+ local baseName = "Test Talisman"
+ data.itemBases[baseName] = {
+ type = "Amulet",
+ subType = "Talisman",
+ tags = { amulet = true, talisman = true },
+ req = { },
+ enchant = "+10 to Strength",
+ }
+ local item = new("Item"):Item("Rarity: Normal\n" .. baseName .. "\nImplicits: 1\n+10 to Strength")
+ data.itemBases[baseName] = nil
+
+ assert.are.equals(0, #item.enchantModLines)
+ end)
+
it("Two-Toned Boots", function()
local item = new("Item"):Item(raw("", "Two-Toned Boots"))
assert.are.equals("Two-Toned Boots (Armour/Energy Shield)", item.baseName)
@@ -886,6 +930,26 @@ describe("TestAdvancedItemParse #item", function()
assert.are.equals("Commissioned 150720 coins to commemorate Chitus", seedLine)
end)
+ it("keeps the roll written in a saved value(min-max) line over its ModRange", function()
+ loadBuildFromXML([[
+
+
+
+ -
+Rarity: UNIQUE
+Crown of the Inward Eye
+Prophet Crown
+Implicits: 0
+{range:0.5}14(9-21)% increased maximum Life, Mana and Global Energy Shield
+
+
+
+
+]])
+
+ assert.are.equals(0.416667, build.itemsTab.items[1].explicitModLines[1].range)
+ end)
+
it("preserves independently rolled values on the same modifier line", function()
local item = new("Item"):Item([[
Rarity: Unique
@@ -989,6 +1053,28 @@ describe("TestAdvancedItemParse #item", function()
assert.are.equals(1, #item.explicitModLines)
end)
+ it("keeps saved rolls when a property was saved among the modifier lines", function()
+ loadBuildFromXML([[
+
+
+
+ -
+Rarity: RARE
+Pain Shell
+Vaal Regalia
+Implicits: 0
+Intangibility: 7%
+{range:0}+(10-13) to all Attributes
+
+
+
+
+
+]])
+
+ assert.are.equals(0, build.itemsTab.items[1].explicitModLines[1].range)
+ end)
+
it("preserves cluster jewel enchants from advanced copy", function()
newBuild()
runCallback("onFrame")
diff --git a/src/Classes/Item.lua b/src/Classes/Item.lua
index 1618e93e018..0dcb16adafe 100644
--- a/src/Classes/Item.lua
+++ b/src/Classes/Item.lua
@@ -1181,6 +1181,7 @@ function ItemClass:ParseRaw(raw, rarity, highQuality)
line = advancedCopyLine:gsub("(%-?%d+%.?%d*)%(%-?%d+%.?%d*%-%-?%d+%.?%d*%)", "%1")
elseif bestPrecisionRange <= 1 and bestPrecisionRange >= 0 then
modLine.range = bestPrecisionRange
+ modLine.rangeFromValue = true
end
end
local rangedLine = itemLib.applyRange(line, 1, catalystScalar, modLine.corruptedRange)
@@ -1618,18 +1619,28 @@ function ItemClass:ParseRaw(raw, rarity, highQuality)
end
end
if self.base and self.base.enchant and #self.enchantModLines == 0 then
+ -- Items saved before their base gained this enchant carry it as an implicit (Black Maw Talisman)
+ local activeImplicitLines = { }
+ for _, modLine in ipairs(self.implicitModLines) do
+ if self:CheckModLineVariant(modLine) then
+ activeImplicitLines[modLine.line] = true
+ end
+ end
local enchantIndex = 1
for line in self.base.enchant:gmatch("[^\n]+") do
- local modList, extra = modLib.parseMod(line)
- t_insert(self.enchantModLines, {
- line = line,
- crafted = true,
- implicit = true,
- enchant = true,
- extra = extra,
- modList = modList or { },
- modTags = self.base.enchantModTypes and self.base.enchantModTypes[enchantIndex] or { },
- })
+ if not activeImplicitLines[line] then
+ local modList, extra = modLib.parseMod(line)
+ t_insert(self.enchantModLines, {
+ line = line,
+ crafted = true,
+ implicit = true,
+ enchant = true,
+ addedFromBase = true,
+ extra = extra,
+ modList = modList or { },
+ modTags = self.base.enchantModTypes and self.base.enchantModTypes[enchantIndex] or { },
+ })
+ end
enchantIndex = enchantIndex + 1
end
end
diff --git a/src/Classes/ItemsTab.lua b/src/Classes/ItemsTab.lua
index 0b3852aba58..40ca711bebb 100644
--- a/src/Classes/ItemsTab.lua
+++ b/src/Classes/ItemsTab.lua
@@ -1218,6 +1218,11 @@ function ItemsTabClass:Load(xml, dbFileName)
self.itemSets = { }
self.itemSetOrderList = { }
self.tradeQuery.statSortSelectionList = { }
+ -- Enchants from the base data are only added to an item text without enchants and were not in the saved text,
+ -- so saved ModRange ids skip the whole list
+ local function countSavedModLines(modLines)
+ return modLines[1] and modLines[1].addedFromBase and 0 or #modLines
+ end
for _, node in ipairs(xml) do
if node.elem == "Item" then
local item = new("Item"):Item("")
@@ -1243,10 +1248,21 @@ function ItemsTabClass:Load(xml, dbFileName)
item.hasAltVariant5 = true
item.variantAlt5 = tonumber(node.attrib.variantAlt5)
end
+ local modRangeCount = 0
+ for _, child in ipairs(node) do
+ if type(child) == "table" and child.elem == "ModRange" then
+ modRangeCount = modRangeCount + 1
+ end
+ end
+ local ignoreModRanges
for _, child in ipairs(node) do
if type(child) == "string" then
item:ParseRaw(child)
- elseif child.elem == "ModRange" then
+ -- Save writes at most one ModRange per mod line. More entries than lines means some saved lines are
+ -- no longer mod lines (e.g. Intangibility before 2.67), so the ids no longer match their lines and the
+ -- {range:} tags of the item text are kept instead
+ ignoreModRanges = modRangeCount > countSavedModLines(item.enchantModLines) + #item.scourgeModLines + #item.implicitModLines + #item.explicitModLines + #item.crucibleModLines
+ elseif child.elem == "ModRange" and not ignoreModRanges then
local id = tonumber(child.attrib.id) or 0
local range = tonumber(child.attrib.range) or 1
-- This is garbage, but needed due to change to separate mod line lists
@@ -1254,11 +1270,15 @@ function ItemsTabClass:Load(xml, dbFileName)
-- Maybe it is? Maybe it isn't? Maybe up is down? Maybe good is bad? AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-- Sorry, cluster jewels are making me crazy(-ier)
for _, list in ipairs{item.buffModLines, item.enchantModLines, item.scourgeModLines, item.implicitModLines, item.explicitModLines, item.crucibleModLines} do
- if id <= #list then
- list[id].range = range
+ local savedLineCount = countSavedModLines(list)
+ if id <= savedLineCount then
+ -- Versions that could not parse "value(min-max)" lines saved them as is with a default ModRange
+ if not list[id].rangeFromValue then
+ list[id].range = range
+ end
break
end
- id = id - #list
+ id = id - savedLineCount
end
end
end