Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions spec/System/TestItemParse_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,29 @@ describe("TestItemParse", function()

end)

it("loads Darkness Enthroned with two augment sockets", function()
local item = new("Item", data.uniques.belt[6])

assert.are.equals("Darkness Enthroned, Fine Belt", item.name)
assert.are.equals(2, item.itemSocketCount)
assert.are.equals(2, #item.sockets)

item.variant = 1 -- Helmet
item:BuildModList()
local baseType, specificType = item:GetSocketedAugmentTypes()
assert.are.equals("armour", baseType)
assert.are.equals("helmet", specificType)
end)

it("parses Atziri's Splendour soul core socket types", function()
local item = new("Item", data.uniques.body[1])
item.variant = 1 -- Helmet
item:BuildModList()

assert.is_true(item.socketedSoulCoreTypes["helmet"])
assert.is_nil(item.socketedSoulCoreTypes["gloves"])
end)

it("jewel sockets", function()
local item = new("Item", [[
Six Socket Body
Expand Down
61 changes: 61 additions & 0 deletions spec/System/TestItemsTab_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,67 @@ describe("TestItemsTab", function()
assert.is_true(build.itemsTab:IsSocketBoundRune(item, item.runes[1], validRunes))
assert.is_false(build.itemsTab:IsSocketBoundRune(item, item.runes[2], validRunes))
end)

it("uses variant socket types for valid augments", function ()
for _, itemRaw in ipairs({ data.uniques.belt[6], data.uniques.body[1] }) do
local item = new("Item", itemRaw)
item.variant = 1 -- Helmet
item:BuildModList()

local foundHelmetSoulCore = false
for _, rune in ipairs(build.itemsTab:GetValidRunesForItem(item)) do
if rune.name == "Quipolatl's Soul Core of Flow" then
foundHelmetSoulCore = true
break
end
end
assert.is_true(foundHelmetSoulCore)

item.runes[1] = "Quipolatl's Soul Core of Flow"
item:UpdateRunes()

assert.are.equals(2, #item.runeModLines)
assert.are.equals("8% increased Skill Effect Duration", item.runeModLines[1].line)
assert.are.equals("8% increased Cooldown Recovery Rate", item.runeModLines[2].line)
end
end)

it("refreshes valid augments when the item variant changes", function ()
local item = new("Item", data.uniques.body[1])
item.variant = 3 -- Boots
item:BuildModList()
build.itemsTab:SetDisplayItem(item)

build.itemsTab.controls.displayItemVariant:SetSel(1) -- Helmet

local foundMaximumRage = false
for _, rune in ipairs(build.itemsTab.controls.displayItemRune1.list) do
if rune.name == "Tzamoto's Soul Core of Ferocity" then
foundMaximumRage = true
break
end
end
assert.is_true(foundMaximumRage)
end)

it("deduplicates valid augments by socketed item name", function ()
local item = new("Item", data.uniques.body[1])
item.variant = 4 -- Shield
item:BuildModList()

local ticabaCount = 0
local ticabaRune
for _, rune in ipairs(build.itemsTab:GetValidRunesForItem(item)) do
if rune.name == "Soul Core of Ticaba" then
ticabaCount = ticabaCount + 1
ticabaRune = rune
end
end
assert.are.equals(1, ticabaCount)
assert.are.equals(2, #ticabaRune.lines)
assert.are.equals("Hits against you have 20% reduced Critical Damage Bonus", ticabaRune.lines[1])
assert.are.equals("Hits against you have 20% reduced Critical Damage Bonus", ticabaRune.lines[2])
end)
end)

it("does nothing when no matching item is equipped", function ()
Expand Down
55 changes: 45 additions & 10 deletions src/Classes/Item.lua
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,8 @@ function ItemClass:ParseRaw(raw, rarity, highQuality)
self.buffModLines = { }
self.enchantModLines = { }
self.runeModLines = { }
self.socketedAugmentTypeOverride = nil
self.socketedSoulCoreTypes = { }
self.implicitModLines = { }
self.explicitModLines = { }
local implicitLines = 0
Expand Down Expand Up @@ -965,6 +967,8 @@ function ItemClass:ParseRaw(raw, rarity, highQuality)
self.canHaveThreeEnchants = true
self.canHaveFourEnchants = true
end
modLine.socketedAugmentTypeOverride = lineLower:match("^this item gains bonuses from socketed items as though it was a? ?(.+)$")
modLine.socketedSoulCoreType = lineLower:match("^this item gains bonuses from socketed soul cores as though it was also a? ?(.+)$")

local modLines
if modLine.rune then
Expand All @@ -979,6 +983,13 @@ function ItemClass:ParseRaw(raw, rarity, highQuality)
modLines = self.explicitModLines
end
modLine.line = line
if self:CheckModLineVariant(modLine) then
if modLine.socketedAugmentTypeOverride then
self.socketedAugmentTypeOverride = modLine.socketedAugmentTypeOverride
elseif modLine.socketedSoulCoreType then
self.socketedSoulCoreTypes[modLine.socketedSoulCoreType] = true
end
end
if modList then
modLine.modList = modList
modLine.extra = extra
Expand Down Expand Up @@ -1020,7 +1031,7 @@ function ItemClass:ParseRaw(raw, rarity, highQuality)
end
-- this will need more advanced logic for jewel sockets in items to work properly but could just be removed as items like this was only introduced during development.
if self.base then
if self.base.weapon or self.base.armour or self.base.tags.wand or self.base.tags.staff or self.base.tags.sceptre then
if self.base.weapon or self.base.armour or self.base.tags.wand or self.base.tags.staff or self.base.tags.sceptre or self.itemSocketCount > 0 then
local shouldFixRunesOnItem = #self.runes == 0
if not shouldFixRunesOnItem and #self.runeModLines > 0 then
local canRebuildRunes = true
Expand Down Expand Up @@ -1546,7 +1557,7 @@ end
-- Rebuild rune modifiers using the item's runes
function ItemClass:UpdateRunes()
wipeTable(self.runeModLines)
local getModRunesForTypes = function(runeName, baseType, specificType)
local getModRunesForTypes = function(runeName, baseType, specificType, soulCoreTypes)
local rune = data.itemMods.Runes[runeName]
local gatheredRuneMods = { }
if rune then
Expand All @@ -1560,22 +1571,23 @@ function ItemClass:UpdateRunes()
t_insert(gatheredRuneMods, rune[specificType])
-- end
end
for soulCoreType in pairs(soulCoreTypes) do
local soulCoreMod = rune[soulCoreType]
if soulCoreMod and soulCoreMod.type == "SoulCore" then
t_insert(gatheredRuneMods, soulCoreMod)
end
end
end
return gatheredRuneMods
end

local statOrder = {}
local baseType, specificType = self:GetSocketedAugmentTypes()
local soulCoreTypes = self.socketedSoulCoreTypes
for i = 1, self.itemSocketCount do
local name = self.runes[i]
if name and name ~= "None" then
local subType = self.base.subType and self.base.subType:lower()
local itemType = self.base.type:lower()
local baseType = self.base.weapon and "weapon" or self.base.armour and "armour" or (self.base.tags.wand or self.base.tags.staff or self.base.tags.sceptre) and "caster"
local specificType =
(subType == "warstaff" and "quarterstaff") or
(itemType == "shield" and subType == "evasion" and "buckler") or
itemType
local gatheredMods = getModRunesForTypes(name, baseType, specificType)
local gatheredMods = getModRunesForTypes(name, baseType, specificType, soulCoreTypes)
for _, mod in ipairs(gatheredMods) do
for i, modLine in ipairs(mod) do
local order = mod.statOrder[i]
Expand Down Expand Up @@ -1716,6 +1728,22 @@ function ItemClass:GetModLineVariantCount(modLine)
return count
end

function ItemClass:GetSocketedAugmentTypes()
local subType = self.base.subType and self.base.subType:lower()
local itemType = self.base.type:lower()
local baseType = self.base.weapon and "weapon" or self.base.armour and "armour" or (self.base.tags.wand or self.base.tags.staff or self.base.tags.sceptre) and "caster"
local specificType =
(subType == "warstaff" and "quarterstaff") or
(itemType == "shield" and subType == "evasion" and "buckler") or
itemType

if self.socketedAugmentTypeOverride then
return "armour", self.socketedAugmentTypeOverride
end

return baseType, specificType
end

-- Return the name of the slot this item is equipped in
function ItemClass:GetPrimarySlot()
if self.base.weapon or self.base.type == "Wand" or self.base.type == "Sceptre" or self.base.type == "Staff" then
Expand Down Expand Up @@ -2066,6 +2094,11 @@ function ItemClass:BuildModList()
if modLine.line:find("Requires Class") then
self.classRestriction = modLine.line:gsub("{variant:([%d,]+)}", ""):match("Requires Class (.+)")
end
if modLine.socketedAugmentTypeOverride then
self.socketedAugmentTypeOverride = modLine.socketedAugmentTypeOverride
elseif modLine.socketedSoulCoreType then
self.socketedSoulCoreTypes[modLine.socketedSoulCoreType] = true
end
-- handle understood modifier variable properties
if not modLine.extra then
if modLine.range then
Expand Down Expand Up @@ -2098,6 +2131,8 @@ function ItemClass:BuildModList()
end
end
end
self.socketedAugmentTypeOverride = nil
self.socketedSoulCoreTypes = { }
for _, modLine in ipairs(self.enchantModLines) do
processModLine(modLine)
end
Expand Down
54 changes: 41 additions & 13 deletions src/Classes/ItemsTab.lua
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ local function isAnointable(item)
-- and not item.sanctified and not item.corrupted and not item.mirrored
end

local function hasAugmentSockets(item)
return item and item.itemSocketCount and item.itemSocketCount > 0
end

local function canHaveAugmentSockets(item)
return item and ((item.base.socketLimit and item.base.socketLimit > 0) or hasAugmentSockets(item))
end

local function buildModSortList()
local sortList = { { label = "Default", stat = nil } }
local sortTransforms = { }
Expand Down Expand Up @@ -437,6 +445,7 @@ holding Shift will put it in the second.]])
self.controls.displayItemVariant = new("DropDownControl", {"TOPLEFT", self.controls.displayItemSectionVariant,"TOPLEFT"}, {0, 0, 300, 20}, nil, function(index, value)
self.displayItem.variant = index
self.displayItem:BuildAndParseRaw()
self:UpdateRuneControls()
self:UpdateDisplayItemTooltip()
self:UpdateDisplayItemRangeLines()
end)
Expand All @@ -447,6 +456,7 @@ holding Shift will put it in the second.]])
self.controls.displayItemAltVariant = new("DropDownControl", {"TOPLEFT",self.controls.displayItemVariant,"BOTTOMLEFT"}, {0, 4, 300, 20}, nil, function(index, value)
self.displayItem.variantAlt = index
self.displayItem:BuildAndParseRaw()
self:UpdateRuneControls()
self:UpdateDisplayItemTooltip()
self:UpdateDisplayItemRangeLines()
end)
Expand All @@ -457,6 +467,7 @@ holding Shift will put it in the second.]])
self.controls.displayItemAltVariant2 = new("DropDownControl", {"TOPLEFT",self.controls.displayItemAltVariant,"BOTTOMLEFT"}, {0, 4, 300, 20}, nil, function(index, value)
self.displayItem.variantAlt2 = index
self.displayItem:BuildAndParseRaw()
self:UpdateRuneControls()
self:UpdateDisplayItemTooltip()
self:UpdateDisplayItemRangeLines()
end)
Expand All @@ -467,6 +478,7 @@ holding Shift will put it in the second.]])
self.controls.displayItemAltVariant3 = new("DropDownControl", {"TOPLEFT",self.controls.displayItemAltVariant2,"BOTTOMLEFT"}, {0, 4, 300, 20}, nil, function(index, value)
self.displayItem.variantAlt3 = index
self.displayItem:BuildAndParseRaw()
self:UpdateRuneControls()
self:UpdateDisplayItemTooltip()
self:UpdateDisplayItemRangeLines()
end)
Expand All @@ -477,6 +489,7 @@ holding Shift will put it in the second.]])
self.controls.displayItemAltVariant4 = new("DropDownControl", {"TOPLEFT",self.controls.displayItemAltVariant3,"BOTTOMLEFT"}, {0, 4, 300, 20}, nil, function(index, value)
self.displayItem.variantAlt4 = index
self.displayItem:BuildAndParseRaw()
self:UpdateRuneControls()
self:UpdateDisplayItemTooltip()
self:UpdateDisplayItemRangeLines()
end)
Expand All @@ -487,6 +500,7 @@ holding Shift will put it in the second.]])
self.controls.displayItemAltVariant5 = new("DropDownControl", {"TOPLEFT",self.controls.displayItemAltVariant4,"BOTTOMLEFT"}, {0, 4, 300, 20}, nil, function(index, value)
self.displayItem.variantAlt5 = index
self.displayItem:BuildAndParseRaw()
self:UpdateRuneControls()
self:UpdateDisplayItemTooltip()
self:UpdateDisplayItemRangeLines()
end)
Expand All @@ -497,11 +511,11 @@ holding Shift will put it in the second.]])

-- Section: Sockets and Links
self.controls.displayItemSectionSockets = new("Control", {"TOPLEFT",self.controls.displayItemSectionVariant,"BOTTOMLEFT"}, {0, 0, 0, function()
return self.displayItem and (self.displayItem.base.weapon or self.displayItem.base.armour or self.displayItem.base.tags.wand or self.displayItem.base.tags.staff or self.displayItem.base.tags.sceptre) and 28 or 0
return canHaveAugmentSockets(self.displayItem) and 28 or 0
end})
self.controls.displayItemSocketRune = new("LabelControl", {"TOPLEFT",self.controls.displayItemSectionSockets,"TOPLEFT"}, {0, 0, 36, 20}, "^x7F7F7FS")
self.controls.displayItemSocketRune.shown = function()
return self.displayItem.base.weapon or self.displayItem.base.armour or self.displayItem.base.tags.wand or self.displayItem.base.tags.staff or self.displayItem.base.tags.sceptre
return canHaveAugmentSockets(self.displayItem)
end
self.controls.displayItemSocketRuneEdit = new("EditControl", {"LEFT",self.controls.displayItemSocketRune,"RIGHT"}, {2, 0, 50, 20}, nil, nil, "%D", 1, function(buf)
local count = tonumber(buf) or 0
Expand Down Expand Up @@ -668,7 +682,7 @@ holding Shift will put it in the second.]])

-- Section: Rune Selection
self.controls.displayItemSectionRune = new("Control", {"TOPLEFT",self.controls.displayItemSectionClusterJewel,"BOTTOMLEFT"}, {0, 0, 0, function()
if not self.displayItem or self.displayItem.itemSocketCount == 0 or not (self.displayItem.base.weapon or self.displayItem.base.armour or self.displayItem.base.tags.wand or self.displayItem.base.tags.staff or self.displayItem.base.tags.sceptre) then
if not hasAugmentSockets(self.displayItem) then
return 0
end
local h = 6
Expand Down Expand Up @@ -707,7 +721,7 @@ holding Shift will put it in the second.]])
end
end
drop.shown = function()
return self.displayItem and i <= self.displayItem.itemSocketCount and (self.displayItem.base.weapon or self.displayItem.base.armour or self.displayItem.base.tags.wand or self.displayItem.base.tags.staff or self.displayItem.base.tags.sceptre)
return hasAugmentSockets(self.displayItem) and i <= self.displayItem.itemSocketCount
end

self.controls["displayItemRune"..i] = drop
Expand Down Expand Up @@ -1950,6 +1964,7 @@ end)

function ItemsTabClass:GetValidRunesForItem(item)
local runes = { }
local addedRunes = { }
local socketedItemType
if item.baseModList then
if item.baseModList:Flag(nil, "SocketedSoulCoresOnly") then
Expand All @@ -1958,29 +1973,42 @@ function ItemsTabClass:GetValidRunesForItem(item)
socketedItemType = "Rune"
end
end
local baseType, specificType = item:GetSocketedAugmentTypes()
local soulCoreTypes = item.socketedSoulCoreTypes
for _, rune in pairs(runeModLines) do
local subType = item.base.subType and item.base.subType:lower()
local itemType = item.base.type:lower()
local function isRuneValidForSlot(runeSlot)
if runeSlot == "None" then
return true
elseif rune.type == "SoulCore" and soulCoreTypes[runeSlot] then
return true
elseif runeSlot == "quarterstaff" then
return subType == "warstaff"
return specificType == "quarterstaff"
elseif runeSlot == "buckler" then
return itemType == "shield" and subType == "evasion"
return specificType == "buckler"
elseif runeSlot == "weapon" then
return item.base.weapon
return baseType == "weapon"
elseif runeSlot == "armour" then
return item.base.armour
return baseType == "armour"
elseif runeSlot == "caster" then
return item.base.tags.wand or item.base.tags.staff or item.base.tags.sceptre
return baseType == "caster"
else
return itemType == runeSlot and not (subType == "warstaff")
return specificType == runeSlot
end
end
if isRuneValidForSlot(rune.slot) then
if rune.slot == "None" or not socketedItemType or rune.type == socketedItemType then
if rune.slot == "None" then
table.insert(runes, rune)
elseif not socketedItemType or rune.type == socketedItemType then
local addedRune = addedRunes[rune.name]
if not addedRune then
addedRune = { name = rune.name, label = rune.label, lines = { }, req = rune.req, order = rune.order, slot = rune.slot, type = rune.type, group = 0, isSocketBound = rune.isSocketBound }
table.insert(runes, addedRune)
addedRunes[rune.name] = addedRune
end
for _, line in ipairs(rune.lines) do
table.insert(addedRune.lines, line)
end
addedRune.group = #addedRune.lines
end
end
end
Expand Down
Loading
Loading