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

teardown(function()
-- newBuild() takes care of resetting everything in setup()
end)

it("parses Non-Unique Time-Lost Jewel radius modifier", function()
build.configTab.input.customMods = "\z
Non-Unique Time-Lost Jewels have 40% increased radius\n\z
"
build.configTab:BuildModList()
runCallback("OnFrame")

assert.are.equals(40, build.calcsTab.mainEnv.modDB:Sum("INC", nil, "NonUniqueTimeLostJewelRadius"))
end)

it("resolveTimeLostRadiusIndex returns upgraded tier at 40% and falls back otherwise", function()
-- Each base tier (Small..Very Large) maps to a +40% counterpart whose outer
-- radius is base * 1.4 (Small 1000 -> 1400).
assert.are.equals(1400, data.jewelRadius[data.resolveTimeLostRadiusIndex(1, 40)].outer)
assert.are.equals(1610, data.jewelRadius[data.resolveTimeLostRadiusIndex(2, 40)].outer)
assert.are.equals(1820, data.jewelRadius[data.resolveTimeLostRadiusIndex(3, 40)].outer)
assert.are.equals(2100, data.jewelRadius[data.resolveTimeLostRadiusIndex(4, 40)].outer)

-- No upgrade tier exists below 40%, so the base index is returned unchanged.
assert.are.equals(1, data.resolveTimeLostRadiusIndex(1, 0))
assert.are.equals(1, data.resolveTimeLostRadiusIndex(1, 39))
assert.are.equals(1, data.resolveTimeLostRadiusIndex(1, nil))
end)
end)
11 changes: 10 additions & 1 deletion src/Classes/ItemsTab.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3965,7 +3965,16 @@ function ItemsTabClass:AddItemTooltip(tooltip, item, slot, dbMode, maxWidth)
tooltip:AddLine(fontSizeBig, "^x7F7F7FRequires Class "..(self.build.spec.curClassName == item.classRestriction and colorCodes.POSITIVE or colorCodes.NEGATIVE)..item.classRestriction, "FONTIN SC")
end
if item.jewelRadiusLabel then
tooltip:AddLine(fontSizeBig, "^x7F7F7FRadius: ^7"..item.jewelRadiusLabel, "FONTIN SC")
local radiusLine = "^x7F7F7FRadius: ^7"..item.jewelRadiusLabel
if item.base and item.base.subType == "Radius"
and item.rarity ~= "UNIQUE" and item.rarity ~= "RELIC"
and self.build.calcsTab and self.build.calcsTab.mainEnv and self.build.calcsTab.mainEnv.modDB then
local upgradePct = self.build.calcsTab.mainEnv.modDB:Sum("INC", nil, "NonUniqueTimeLostJewelRadius")
if upgradePct > 0 then
radiusLine = radiusLine .. " ^x7F7F7F(+"..upgradePct.."% from tree)"
end
end
tooltip:AddLine(fontSizeBig, radiusLine, "FONTIN SC")
end
if item.jewelRadiusData and slot and item.jewelRadiusData[slot.nodeId] then
local radiusData = item.jewelRadiusData[slot.nodeId]
Expand Down
14 changes: 14 additions & 0 deletions src/Classes/PassiveSpec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1471,6 +1471,18 @@ function PassiveSpecClass:BuildNodePathsToRootNodes(roots)
::continueBuildPath::
end
end

-- Returns the radius index a jewel uses in this spec, accounting for Time-Lost
-- radius upgrades from allocated passives (e.g. Baryanic Leylines)
function PassiveSpecClass:GetJewelRadiusIndex(item)
local radiusIndex = item.jewelRadiusIndex
if radiusIndex and item.base and item.base.subType == "Radius"
and item.rarity ~= "UNIQUE" and item.rarity ~= "RELIC" then
return data.resolveTimeLostRadiusIndex(radiusIndex, self.timeLostJewelRadiusIncrease)
end
return radiusIndex
end

-- Rebuilds dependencies and paths for all nodes
function PassiveSpecClass:BuildAllDependsAndPaths()
-- This table will keep track of which nodes have been visited during each path-finding attempt
Expand All @@ -1493,6 +1505,7 @@ function PassiveSpecClass:BuildAllDependsAndPaths()
end
end
wipeTable(intuitiveLeapLikeNodes)
self.timeLostJewelRadiusIncrease = 0
for id, node in pairs(self.allocNodes) do
if node.ascendancyName then -- avoid processing potentially replaceable nodes
self.tree:ProcessStats(node)
Expand All @@ -1501,6 +1514,7 @@ function PassiveSpecClass:BuildAllDependsAndPaths()
t_insert(intuitiveLeapLikeNodes, radius)
end
end
self.timeLostJewelRadiusIncrease = self.timeLostJewelRadiusIncrease + node.modList:Sum("INC", nil, "NonUniqueTimeLostJewelRadius")
processed[id] = true
end
end
Expand Down
56 changes: 49 additions & 7 deletions src/Classes/PassiveTreeView.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1146,8 +1146,10 @@ function PassiveTreeViewClass:Draw(build, viewPort, inputEvents)
end
else
-- Jewel in socket is not Thread of Hope or similar
-- Use the increased radii instead of the base ones if the socketed jewel is upgraded
local isIncreased = jewel and spec:GetJewelRadiusIndex(jewel) ~= jewel.jewelRadiusIndex
for index, data in ipairs(build.data.jewelRadius) do
if hoverNode.nodesInRadius[index][node.id] then
if (not data.increased) == (not isIncreased) and hoverNode.nodesInRadius[index][node.id] then
-- Draw normal jewel radii
if data.inner == 0 then
SetDrawColor(data.col)
Expand Down Expand Up @@ -1269,8 +1271,9 @@ function PassiveTreeViewClass:Draw(build, viewPort, inputEvents)
end

-- Draw ring overlays for jewel sockets
local function drawJewelRadius(jewel, scrX, scrY, tint)
local radData = build.data.jewelRadius[jewel.jewelRadiusIndex]
local function drawJewelRadius(jewel, scrX, scrY, tint, jewelSpec)
-- Use the spec the ring belongs to, so compared trees can differ (e.g. Baryanic Leylines)
local radData = build.data.jewelRadius[jewelSpec:GetJewelRadiusIndex(jewel)]
local outerSize = radData.outer * data.gameConstants["PassiveTreeJewelDistanceMultiplier"] * scale
local innerSize = radData.inner * data.gameConstants["PassiveTreeJewelDistanceMultiplier"] * scale * 1.06
SetDrawColor(tint[1], tint[2], tint[3], tint[4])
Expand Down Expand Up @@ -1307,6 +1310,7 @@ function PassiveTreeViewClass:Draw(build, viewPort, inputEvents)
self:DrawImageRotated(self.jewelShadedInnerRingFlipped, scrX, scrY, innerSize * 2, innerSize * 2, 0.7)
end
end

SetDrawLayer(nil, 25)
for nodeId in pairs(tree.sockets) do
local node = spec.nodes[nodeId]
Expand All @@ -1318,6 +1322,9 @@ function PassiveTreeViewClass:Draw(build, viewPort, inputEvents)
if node == hoverNode then
local effectiveJewel = jewel or cJewel
local isThreadOfHope = effectiveJewel and effectiveJewel.jewelRadiusLabel == "Variable"
-- Preview the increased radii instead of the base ones if the socketed jewel is upgraded
local jewelSpec = jewel and spec or self.compareSpec
local isIncreased = effectiveJewel and jewelSpec and jewelSpec:GetJewelRadiusIndex(effectiveJewel) ~= effectiveJewel.jewelRadiusIndex
for _, radData in ipairs(build.data.jewelRadius) do
local outerSize = radData.outer * data.gameConstants["PassiveTreeJewelDistanceMultiplier"] * scale
local innerSize = radData.inner * data.gameConstants["PassiveTreeJewelDistanceMultiplier"] * scale
Expand All @@ -1330,7 +1337,7 @@ function PassiveTreeViewClass:Draw(build, viewPort, inputEvents)
end
else
-- Standard jewel: draw the full-disc radii (inner == 0)
if innerSize == 0 then
if innerSize == 0 and (not radData.increased) == (not isIncreased) then
SetDrawColor(radData.col)
DrawImage(self.ring, scrX - outerSize, scrY - outerSize, outerSize * 2, outerSize * 2)
end
Expand All @@ -1341,12 +1348,16 @@ function PassiveTreeViewClass:Draw(build, viewPort, inputEvents)
local pHasRadius = jewel and jewel.jewelRadiusIndex
local cHasRadius = cJewel and cJewel.jewelRadiusIndex
local sameJewel = compareJewelsEqual(jewel, cJewel)
if sameJewel and pHasRadius and cHasRadius then
-- Identical jewels can have different radii when only one spec has a Time-Lost radius upgrade
sameJewel = spec:GetJewelRadiusIndex(jewel) == self.compareSpec:GetJewelRadiusIndex(cJewel)
end
if pHasRadius then
local tint = (not self.compareSpec or sameJewel) and JEWEL_RADIUS_TINT_NEUTRAL or JEWEL_RADIUS_TINT_PRIMARY_ONLY
drawJewelRadius(jewel, scrX, scrY, tint)
drawJewelRadius(jewel, scrX, scrY, tint, spec)
end
if cHasRadius and not sameJewel then
drawJewelRadius(cJewel, scrX, scrY, JEWEL_RADIUS_TINT_COMPARE_ONLY)
drawJewelRadius(cJewel, scrX, scrY, JEWEL_RADIUS_TINT_COMPARE_ONLY, self.compareSpec)
end
end
end
Expand Down Expand Up @@ -1887,11 +1898,42 @@ function PassiveTreeViewClass:AddNodeTooltip(tooltip, node, build, incSmallPassi
-- we only want to run the timeLost function on a node that can could be in a jewel socket radius of up to Large
-- essentially trying to avoid calling ProcessStats for a Normal/Notable node that can't possibly be affected
-- loops potentially every socket (24) until itemsTab is loaded or a jewel socket is hovered, then it will only loop the allocated sockets
-- Radius indexes to probe: Very Large (4) plus any Time-Lost upgrade tiers (e.g. Baryanic Leylines'
-- "Very Large +40%" at 16) that the current build actually has access to.
local radiusProbeIndexes = { 4 }
local timeLostRadiusUpgrade = build.calcsTab and build.calcsTab.mainEnv and build.calcsTab.mainEnv.modDB
and build.calcsTab.mainEnv.modDB:Sum("INC", nil, "NonUniqueTimeLostJewelRadius") or 0
if timeLostRadiusUpgrade > 0 and data.nonUniqueTimeLostJewelRadiusUpgrades then
for pct, map in pairs(data.nonUniqueTimeLostJewelRadiusUpgrades) do
if pct <= timeLostRadiusUpgrade then
for _, upgradedIndex in pairs(map) do
local seen = false
for _, existing in ipairs(radiusProbeIndexes) do
if existing == upgradedIndex then
seen = true
break
end
end
if not seen then
t_insert(radiusProbeIndexes, upgradedIndex)
end
end
end
end
end
local function isNodeInARadius(node)
local isInRadius = false
for id, socket in pairs(build.itemsTab.sockets) do
if build.itemsTab.activeSocketList and socket.inactive == false or socket.inactive == nil then
isInRadius = isInRadius or (build.spec.nodes[id] and build.spec.nodes[id].nodesInRadius and build.spec.nodes[id].nodesInRadius[4][node.id] ~= nil)
local socketNode = build.spec.nodes[id]
if socketNode and socketNode.nodesInRadius then
for _, radiusIndex in ipairs(radiusProbeIndexes) do
if socketNode.nodesInRadius[radiusIndex] and socketNode.nodesInRadius[radiusIndex][node.id] ~= nil then
isInRadius = true
break
end
end
end
if isInRadius then break end
end
end
Expand Down
2 changes: 1 addition & 1 deletion src/Data/ModCache.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8828,7 +8828,7 @@ c["Non-Keystone Passive Skills in Medium Radius of allocated Keystone Passive Sk
c["Non-Minion Skills have 50% less Reservation Efficiency"]={{[1]={[1]={neg=true,skillType=6,type="SkillType"},flags=0,keywordFlags=0,name="ReservationEfficiency",type="MORE",value=-50}},nil}
c["Non-Unique Life Flasks apply their Effects constantly"]={nil,"Non-Unique Life Flasks apply their Effects constantly "}
c["Non-Unique Life Flasks apply their Effects constantly Recovery from Life Flasks cannot be Instant"]={nil,"Non-Unique Life Flasks apply their Effects constantly Recovery from Life Flasks cannot be Instant "}
c["Non-Unique Time-Lost Jewels have 40% increased radius"]={nil,"Non-Unique Time-Lost Jewels have 40% increased radius "}
c["Non-Unique Time-Lost Jewels have 40% increased radius"]={{[1]={flags=0,keywordFlags=0,name="NonUniqueTimeLostJewelRadius",type="INC",value=40}},nil}
c["Non-instant Recovery from Mana Flasks also applies to Life"]={nil,"Non-instant Recovery from Mana Flasks also applies to Life "}
c["Nova Spells have 20% less Area of Effect"]={{[1]={[1]={skillType=85,type="SkillType"},flags=0,keywordFlags=0,name="AreaOfEffect",type="MORE",value=-20}},nil}
c["Oasis"]={{[1]={flags=0,keywordFlags=0,name="Keystone",type="LIST",value="Oasis"},[2]={flags=0,keywordFlags=0,name="Condition:HaveOasis",type="FLAG",value=true}},nil}
Expand Down
13 changes: 10 additions & 3 deletions src/Modules/CalcSetup.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1291,23 +1291,30 @@ function calcs.initEnv(build, mode, override, specEnv)
end
end
if item and not (node and node.sinister) and ( item.jewelRadiusIndex or (override and override.extraJewelFuncs and #override.extraJewelFuncs > 0) ) then
-- Non-unique Time-Lost Jewels use an upgraded radius tier when the build
-- has e.g. Baryanic Leylines allocated.
local effectiveRadiusIndex = item.jewelRadiusIndex
if effectiveRadiusIndex and item.base and item.base.subType == "Radius"
and item.rarity ~= "UNIQUE" and item.rarity ~= "RELIC" then
effectiveRadiusIndex = data.resolveTimeLostRadiusIndex(effectiveRadiusIndex, nodesModsList:Sum("INC", nil, "NonUniqueTimeLostJewelRadius"))
end
-- Jewel has a radius, add it to the list
local funcList = (item.jewelData and item.jewelData.funcList) or { { type = "Self", func = defaultRadiusJewelFunc } }
for _, func in ipairs(funcList) do
t_insert(env.radiusJewelList, {
nodes = node.nodesInRadius and node.nodesInRadius[item.jewelRadiusIndex] or { },
nodes = node.nodesInRadius and node.nodesInRadius[effectiveRadiusIndex] or { },
func = func.func,
type = func.type,
item = item,
nodeId = slot.nodeId,
attributes = node.attributesInRadius and node.attributesInRadius[item.jewelRadiusIndex] or { },
attributes = node.attributesInRadius and node.attributesInRadius[effectiveRadiusIndex] or { },
data = { },
-- store this to compare with cache later
jewelHash = getHashFromString(item.modSource..item.raw)
})
if func.type ~= "Self" and node.nodesInRadius then
-- Add nearby unallocated nodes to the extra node list
for nodeId, node in pairs(node.nodesInRadius[item.jewelRadiusIndex]) do
for nodeId, node in pairs(node.nodesInRadius[effectiveRadiusIndex]) do
if not env.allocNodes[nodeId] then
env.extraRadiusNodeList[nodeId] = env.spec.nodes[nodeId]
end
Expand Down
34 changes: 34 additions & 0 deletions src/Modules/Data.lua
Original file line number Diff line number Diff line change
Expand Up @@ -680,9 +680,43 @@ data.jewelRadii = {
{ inner = 1400, outer = 1700, col = "^xFFCC00", label = "Variable" },
{ inner = 1650, outer = 1950, col = "^xFF6600", label = "Variable" },
{ inner = 1800, outer = 2100, col = "^x0099FF", label = "Variable" },

-- Baryanic Leylines (Disciple of Varashta): non-unique Time-Lost radius +40%
-- `increased` keeps these out of the radius previews unless the jewel is upgraded
{ inner = 0, outer = 1400, col = "^xBB6600", label = "Increased Small", increased = true },
{ inner = 0, outer = 1610, col = "^x66FFCC", label = "Increased Medium", increased = true },
{ inner = 0, outer = 1820, col = "^x2222CC", label = "Increased Large", increased = true },
{ inner = 0, outer = 2100, col = "^xC100FF", label = "Increased Very Large", increased = true },
}
}

-- Maps a base Time-Lost Jewel radius index to the upgraded index granted by
-- "Non-Unique Time-Lost Jewels have X% increased radius" effects. Keyed by
-- percentage so additional tiers can be added later.
data.nonUniqueTimeLostJewelRadiusUpgrades = {
[40] = { [1] = 13, [2] = 14, [3] = 15, [4] = 16 },
}

-- Returns the radius index that should be used for a non-unique Time-Lost Jewel
-- given the base index and the total "% increased radius" value in effect.
-- Picks the largest supported tier at or below upgradePct; returns baseIndex
-- unchanged when no tier applies.
function data.resolveTimeLostRadiusIndex(baseIndex, upgradePct)
if not baseIndex or not upgradePct or upgradePct <= 0 then
return baseIndex
end
local bestPct
for pct, map in pairs(data.nonUniqueTimeLostJewelRadiusUpgrades) do
if pct <= upgradePct and map[baseIndex] and (not bestPct or pct > bestPct) then
bestPct = pct
end
end
if bestPct then
return data.nonUniqueTimeLostJewelRadiusUpgrades[bestPct][baseIndex]
end
return baseIndex
end

data.jewelRadius = data.setJewelRadiiGlobally(latestTreeVersion)

-- Stat descriptions
Expand Down
9 changes: 9 additions & 0 deletions src/Modules/ItemSlotHelper.lua
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ function M.DrawViewer(itemsTab, nodeId, x, y, w, h)

local viewer = itemsTab.socketViewer
viewer.zoom = 17
-- Zoom out if needed so the socketed jewel's radius ring (including Time-Lost radius
-- upgrades such as Baryanic Leylines) fits inside the preview: the ring's diameter
-- must fit min(w,h), with ~5% margin so the ring stroke isn't clipped
local _, jewel = itemsTab:GetSocketAndJewelForNodeID(nodeId)
if jewel and jewel.jewelRadiusIndex then
local radData = data.jewelRadius[itemsTab.build.spec:GetJewelRadiusIndex(jewel)]
local ringSize = radData.outer * data.gameConstants["PassiveTreeJewelDistanceMultiplier"]
viewer.zoom = math.min(viewer.zoom, itemsTab.build.spec.tree.size / (2 * ringSize) / 1.05)
end

local viewPortSize = math.min(w, h)
local scale = itemsTab.build.spec.tree.size / (viewPortSize * viewer.zoom)
Expand Down
1 change: 1 addition & 0 deletions src/Modules/ModParser.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5617,6 +5617,7 @@ local specialModList = {
["upgrades radius to medium"] = { mod("JewelData", "LIST", { key = "timeLostJewelRadiusOverride", value = 2 })},
["upgrades radius to large"] = { mod("JewelData", "LIST", { key = "timeLostJewelRadiusOverride", value = 3 })},
["upgrades radius to very large"] = { mod("JewelData", "LIST", { key = "timeLostJewelRadiusOverride", value = 4 })},
["non%-unique time%-lost jewels have (%d+)%% increased radius"] = function(num) return { mod("NonUniqueTimeLostJewelRadius", "INC", num) } end,
["primordial"] = { mod("Multiplier:PrimordialItem", "BASE", 1) },
["spectres have a base duration of (%d+) seconds"] = { mod("SkillData", "LIST", { key = "duration", value = 6 }, { type = "SkillName", skillName = "Raise Spectre", includeTransfigured = true }) },
["flasks applied to you have (%d+)%% increased effect"] = function(num) return { mod("FlaskEffect", "INC", num, { type = "ActorCondition", actor = "player"}) } end,
Expand Down