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
229 changes: 229 additions & 0 deletions spec/System/TestGemSocketQuality_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
describe("TestGemSocketQuality", function()
before_each(function()
newBuild()
end)

-- 3 blue socket body armour
local function equipBody(sockets, extraLines)
build.itemsTab:CreateDisplayItemFromRaw(
"Rarity: RARE\nTest Robe\nSage's Robe\nQuality: 0\nSockets: " .. sockets .. "\nImplicits: 0\n" .. (extraLines or ""))
build.itemsTab:AddDisplayItem()
end

-- get specific socket group linked to a slot
local function groupForSlot(slotName, index)
local seen = 0
for _, group in ipairs(build.skillsTab.socketGroupList) do
if group.slot == slotName then
seen = seen + 1
if seen == (index or 1) then
return group
end
end
end
end

it("grants +10% quality to a gem in a matching colour socket", function()
equipBody("B-B-B")
build.skillsTab:PasteSocketGroup("Slot: Body Armour\nFireball 20/0 1\n")
runCallback("OnFrame")

local group = groupForSlot("Body Armour")
assert.is_true(group.gemList[1].matchesSocket)
assert.are.equals(10, build.calcsTab.mainOutput.GemQuality)
-- The bonus must reach the calculations, not just the displayed number:
-- activeEffect.quality is the value fed to buildSkillInstanceStats.
assert.are.equals(10, build.calcsTab.mainEnv.player.mainSkill.activeEffect.quality)
end)

it("adds the socket bonus on top of the gem's own quality", function()
equipBody("B-B-B")
build.skillsTab:PasteSocketGroup("Slot: Body Armour\nFireball 20/5 1\n")
runCallback("OnFrame")

assert.are.equals(15, build.calcsTab.mainOutput.GemQuality)
end)

it("grants no bonus to a gem in a mismatched colour socket", function()
equipBody("R-W-R")
build.skillsTab:PasteSocketGroup("Slot: Body Armour\nFireball 20/0 1\nFireball 20/0 1\n")
runCallback("OnFrame")

local group = groupForSlot("Body Armour")
assert.is_false(group.gemList[1].matchesSocket)
assert.is_false(group.gemList[2].matchesSocket)
assert.are.equals(0, build.calcsTab.mainOutput.GemQuality)
assert.are.equals(0, build.calcsTab.mainEnv.player.mainSkill.activeEffect.quality)
end)

it("applies the socket quality bonus to the skill's quality-scaled stats", function()
equipBody("B-B-B")
build.skillsTab:PasteSocketGroup("Slot: Body Armour\nFireball 20/0 1\n")
-- Fireball aoe explosion
build.calcsTab.input.skillPart = 2
runCallback("OnFrame")
assert.are.equals(10, build.calcsTab.mainEnv.player.mainSkill.activeEffect.quality)
local matchedRadius = build.calcsTab.mainOutput.AreaOfEffectRadius

newBuild()
equipBody("R-R-R")
build.skillsTab:PasteSocketGroup("Slot: Body Armour\nFireball 20/0 1\n")
build.calcsTab.input.skillPart = 2
runCallback("OnFrame")
assert.are.equals(0, build.calcsTab.mainEnv.player.mainSkill.activeEffect.quality)
local unmatchedRadius = build.calcsTab.mainOutput.AreaOfEffectRadius

assert.is_true(matchedRadius > unmatchedRadius)
end)

it("grants no bonus to a white/colourless gem", function()
equipBody("W-W-W")
build.skillsTab:PasteSocketGroup("Slot: Body Armour\nPortal 20/0 1\n")
runCallback("OnFrame")

local group = groupForSlot("Body Armour")
assert.is_false(group.gemList[1].matchesSocket)
end)

it("adds quality when an item is equipped and removes it when unequipped", function()
equipBody("B-B-B")
build.skillsTab:PasteSocketGroup("Slot: Body Armour\nFireball 20/0 1\n")
runCallback("OnFrame")
assert.are.equals(10, build.calcsTab.mainOutput.GemQuality)

-- Unequip the body armour
build.itemsTab.slots["Body Armour"]:SetSelItemId(0)
build.buildFlag = true
runCallback("OnFrame")

local group = groupForSlot("Body Armour")
assert.is_false(group.gemList[1].matchesSocket)
assert.are.equals(0, build.calcsTab.mainOutput.GemQuality)
end)

it("always grants the bonus with Dialla's 'always matches' mod", function()
-- Blue gem in a red socket
equipBody("R-R-R", "Gems Socketed always have the Quality bonus from Socket Colour\n")
build.skillsTab:PasteSocketGroup("Slot: Body Armour\nFireball 20/0 1\n")
runCallback("OnFrame")

local item = build.itemsTab.items[build.itemsTab.slots["Body Armour"].selItemId]
assert.is_true(item.sockets.colourAlwaysMatches)

local group = groupForSlot("Body Armour")
assert.is_true(group.gemList[1].matchesSocket)
assert.are.equals(10, build.calcsTab.mainOutput.GemQuality)
assert.are.equals(10, build.calcsTab.mainEnv.player.mainSkill.activeEffect.quality)
end)

it("respects socket order across split groups on one item", function()
-- Sockets are Blue then Red. The first group's gem lines up with the
-- blue socket, the second group's gem continues at the red socket
equipBody("B-R")
build.skillsTab:PasteSocketGroup("Slot: Body Armour\nFireball 20/0 1\n")
build.skillsTab:PasteSocketGroup("Slot: Body Armour\nAbsolution 20/0 1\n")
runCallback("OnFrame")

local blueGroup = groupForSlot("Body Armour", 1)
local redGroup = groupForSlot("Body Armour", 2)
-- Fireball (blue) matches socket 1
assert.is_true(blueGroup.gemList[1].matchesSocket)
-- Absolution (red) only matches if the offset continues at socket 2
assert.is_true(redGroup.gemList[1].matchesSocket)
end)

it("does not grant the bonus when split order breaks the match", function()
-- Both sockets blue: the red gem in the second group should not match
equipBody("R-B")
build.skillsTab:PasteSocketGroup("Slot: Body Armour\nFireball 20/0 1\n")
build.skillsTab:PasteSocketGroup("Slot: Body Armour\nAbsolution 20/0 1\n")
runCallback("OnFrame")

assert.is_false(groupForSlot("Body Armour", 1).gemList[1].matchesSocket)
assert.is_false(groupForSlot("Body Armour", 2).gemList[1].matchesSocket)
end)

it("Optimise Sockets recolours an item to match its assigned gems", function()
equipBody("R-R-R-R")
build.skillsTab:PasteSocketGroup("Slot: Body Armour\nFireball 20/0 1\nAbsolution 20/0 1\n")
runCallback("OnFrame")

build.skillsTab.controls.optimiseSockets.onClick()
runCallback("OnFrame")

local item = build.itemsTab.items[build.itemsTab.slots["Body Armour"].selItemId]
assert.are.equals(2, #item.sockets)
assert.are.equals("B", item.sockets[1].color)
assert.are.equals("R", item.sockets[2].color)

local group = groupForSlot("Body Armour")
assert.is_true(group.gemList[1].matchesSocket)
assert.is_true(group.gemList[2].matchesSocket)
end)

it("Optimise Sockets does not exceed the item's socket limit", function()
equipBody("R")
-- Assign more gems than the base's 6 socket limit
build.skillsTab:PasteSocketGroup("Slot: Body Armour\n" ..
("Fireball 20/0 1\n"):rep(8))
runCallback("OnFrame")

build.skillsTab.controls.optimiseSockets.onClick()
runCallback("OnFrame")

local item = build.itemsTab.items[build.itemsTab.slots["Body Armour"].selItemId]
assert.is_true(#item.sockets <= item.base.socketLimit)
assert.are.equals(6, #item.sockets)
end)
it("Optimise Sockets preserves abyssal sockets and stays within the limit", function()
equipBody("A-R-R")
build.skillsTab:PasteSocketGroup("Slot: Body Armour\nFireball 20/0 1\nAbsolution 20/0 1\n")
runCallback("OnFrame")

build.skillsTab.controls.optimiseSockets.onClick()
runCallback("OnFrame")

local item = build.itemsTab.items[build.itemsTab.slots["Body Armour"].selItemId]
local abyssal = 0
for _, socket in ipairs(item.sockets) do
if socket.color == "A" then
abyssal = abyssal + 1
end
end
assert.are.equals(1, abyssal)
assert.is_true(#item.sockets <= item.base.socketLimit)
end)

-- find controlled destruction increased damage mod
local function supportQualityDamageMod()
local mainSkill = build.calcsTab.mainEnv.player.mainSkill
for _, entry in ipairs(mainSkill.skillModList:Tabulate("INC", mainSkill.skillCfg, "Damage")) do
if entry.mod.source == "Skill:SupportControlledDestruction" then
return entry.mod
end
end
end

it("applies the socket bonus to a support gem's effective quality", function()
equipBody("B-B")
build.skillsTab:PasteSocketGroup("Slot: Body Armour\nFireball 20/0 1\nControlled Destruction 20/0 1\n")
runCallback("OnFrame")

local group = groupForSlot("Body Armour")
assert.is_true(group.gemList[2].matchesSocket)

-- check that the actual 5% damage mod exists
local mod = supportQualityDamageMod()
assert.is_not_nil(mod)
assert.are.equals(5, mod.value)

-- and check that the mod is gone without the socket quality bonus
newBuild()
equipBody("R-R")
build.skillsTab:PasteSocketGroup("Slot: Body Armour\nFireball 20/0 1\nControlled Destruction 20/0 1\n")
runCallback("OnFrame")

assert.is_false(groupForSlot("Body Armour").gemList[2].matchesSocket)
assert.is_nil(supportQualityDamageMod())
end)
end)
25 changes: 19 additions & 6 deletions src/Classes/GemTooltip.lua
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,25 @@ local function addCommonGemInfo(tooltip, build, gemInstance, grantedEffect, addL
grantedEffectLevel.damageEffectiveness * 100), "FONTIN SC")
end
end
if addReq and displayInstance.quality > 0 then
tooltip:AddLine(fontSizeBig, string.format("^x7F7F7FQuality: " .. colorCodes.MAGIC .. "+%d%%^7%s",
gemInstance.quality,
(displayInstance.quality > gemInstance.quality) and
" (" .. colorCodes.MAGIC .. "+" .. (displayInstance.quality - gemInstance.quality) .. "^7)" or ""
), "FONTIN SC")
if addReq then
if gemInstance.quality > 0 then
tooltip:AddLine(fontSizeBig, string.format("^x7F7F7FQuality: +%s%d%%", colorCodes.MAGIC, gemInstance.quality), "FONTIN SC")
end
local function formatQuality(number, suffix)
return colorCodes.MAGIC .. string.format("+%d%% Quality from %s", number, suffix)
end
if (displayInstance.itemQuality or 0) > 0 then
tooltip:AddLine(fontSizeBig, formatQuality(displayInstance.itemQuality, "Item"), "FONTIN SC")
end
if (displayInstance.supportQuality or 0) > 0 then
tooltip:AddLine(fontSizeBig, formatQuality(displayInstance.supportQuality, "Support"), "FONTIN SC")
end
if (displayInstance.globalQuality or 0) > 0 then
tooltip:AddLine(fontSizeBig, formatQuality(displayInstance.globalQuality, "Global Modifiers"), "FONTIN SC")
end
if (displayInstance.socketQuality or 0) > 0 then
tooltip:AddLine(fontSizeBig, formatQuality(displayInstance.socketQuality, "Socket Colour"), "FONTIN SC")
end
end
tooltip:AddSeparator(10)
if addReq then
Expand Down
8 changes: 2 additions & 6 deletions src/Classes/ImportTab.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1472,6 +1472,7 @@ function ImportTabClass:ImportItemsAndSkills(charData, clearItems, clearSkills,
end
self.build.itemsTab:PopulateSlots()
self.build.itemsTab:AddUndoState()
self.build.skillsTab:UpdateSocketGroups()
self.build.skillsTab:AddUndoState()
self.build.characterLevel = charData.level
self.build.configTab:UpdateLevel()
Expand Down Expand Up @@ -1839,12 +1840,7 @@ function ImportTabClass:ImportSocketedItems(item, socketedItems, slotName)
itemSocketGroupList[groupID] = { label = "", enabled = true, gemList = { }, slot = slotName }
end
local socketGroup = itemSocketGroupList[groupID]
if not socketedItem.support and socketGroup.gemList[1] and socketGroup.gemList[1].support and not (item.title and item.title:match("Dialla's Malefaction")) then
-- If the first gemInstance is a support gemInstance, put the first active gemInstance before it
t_insert(socketGroup.gemList, 1, gemInstance)
else
t_insert(socketGroup.gemList, gemInstance)
end
t_insert(socketGroup.gemList, gemInstance)
if socketedItem.builtInSupport then
socketGroup.imbuedSupport = socketedItem.builtInSupport:gsub("Supported by Level 1 ", "")
self.build.skillsTab.controls.imbuedSupport.gemChangeFunc(data.gems[data.gemForBaseName[socketGroup.imbuedSupport:lower().." support"]], nil, nil, true, slotName)
Expand Down
5 changes: 4 additions & 1 deletion src/Classes/Item.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1741,7 +1741,7 @@ function ItemClass:BuildRaw()
end
if self.sockets and #self.sockets > 0 then
local line = "Sockets: "
for i, socket in pairs(self.sockets) do
for i, socket in ipairs(self.sockets) do
line = line .. socket.color
if self.sockets[i+1] then
line = line .. (socket.group == self.sockets[i+1].group and "-" or " ")
Expand Down Expand Up @@ -2380,6 +2380,9 @@ function ItemClass:BuildModList()
end
self.sockets = newSockets
end
if self.sockets and calcLocal(baseList, "SocketAlwaysMatches", "FLAG", 0) then
self.sockets.colourAlwaysMatches = true
end
self.socketedJewelEffectModifier = 1 + calcLocal(baseList, "SocketedJewelEffect", "INC", 0) / 100
if self.base.weapon or self.type == "Ring" then
self.slotModList = { }
Expand Down
56 changes: 30 additions & 26 deletions src/Classes/ItemsTab.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4064,6 +4064,35 @@ local function buildSpecForJewelComparison(itemsTab, compareSlot, replacementIte
return spec
end

function ItemsTabClass:GetSocketDescriptionLine(item)
-- Sockets/links
local group = 0
local line = ""
for i, socket in ipairs(item.sockets) do
if i > 1 then
if socket.group == group then
line = line .. "^7="
else
line = line .. " "
end
group = socket.group
end
local code
if socket.color == "R" then
code = colorCodes.STRENGTH
elseif socket.color == "G" then
code = colorCodes.DEXTERITY
elseif socket.color == "B" then
code = colorCodes.INTELLIGENCE
elseif socket.color == "W" then
code = colorCodes.SCION
elseif socket.color == "A" then
code = "^xB0B0B0"
end
line = line .. code .. socket.color
end
return line
end
function ItemsTabClass:AddItemTooltip(tooltip, item, slot, dbMode, maxWidth)
local fontSizeSmall = main.showFlavourText and 16 or 14
local fontSizeBig = main.showFlavourText and 18 or 16
Expand Down Expand Up @@ -4282,32 +4311,7 @@ function ItemsTabClass:AddItemTooltip(tooltip, item, slot, dbMode, maxWidth)
end

if #item.sockets > 0 then
-- Sockets/links
local group = 0
local line = ""
for i, socket in ipairs(item.sockets) do
if i > 1 then
if socket.group == group then
line = line .. "^7="
else
line = line .. " "
end
group = socket.group
end
local code
if socket.color == "R" then
code = colorCodes.STRENGTH
elseif socket.color == "G" then
code = colorCodes.DEXTERITY
elseif socket.color == "B" then
code = colorCodes.INTELLIGENCE
elseif socket.color == "W" then
code = colorCodes.SCION
elseif socket.color == "A" then
code = "^xB0B0B0"
end
line = line .. code .. socket.color
end
local line = self:GetSocketDescriptionLine(item)
tooltip:AddLine(fontSizeBig, "^x7F7F7FSockets: "..line, "FONTIN SC")
end
tooltip:AddSeparator(10)
Expand Down
Loading
Loading