diff --git a/spec/System/TestBaryanicLeylines_spec.lua b/spec/System/TestBaryanicLeylines_spec.lua new file mode 100644 index 000000000..58a66017a --- /dev/null +++ b/spec/System/TestBaryanicLeylines_spec.lua @@ -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) diff --git a/src/Classes/PassiveTreeView.lua b/src/Classes/PassiveTreeView.lua index e33df5675..b59cf2424 100644 --- a/src/Classes/PassiveTreeView.lua +++ b/src/Classes/PassiveTreeView.lua @@ -1013,8 +1013,14 @@ function PassiveTreeViewClass:Draw(build, viewPort, inputEvents) end elseif node.alloc then if jewel and jewel.jewelRadiusIndex then - -- Draw only the selected jewel radius - local radData = build.data.jewelRadius[jewel.jewelRadiusIndex] + -- Draw only the selected jewel radius (factoring in Time-Lost upgrades such as Baryanic Leylines) + local effectiveRadiusIndex = jewel.jewelRadiusIndex + if jewel.base and jewel.base.subType == "Radius" + and jewel.rarity ~= "UNIQUE" and jewel.rarity ~= "RELIC" + and build.calcsTab and build.calcsTab.mainEnv and build.calcsTab.mainEnv.modDB then + effectiveRadiusIndex = data.resolveTimeLostRadiusIndex(effectiveRadiusIndex, build.calcsTab.mainEnv.modDB:Sum("INC", nil, "NonUniqueTimeLostJewelRadius")) + end + local radData = build.data.jewelRadius[effectiveRadiusIndex] local outerSize = radData.outer * data.gameConstants["PassiveTreeJewelDistanceMultiplier"] * scale local innerSize = radData.inner * data.gameConstants["PassiveTreeJewelDistanceMultiplier"] * scale * 1.06 SetDrawColor(1,1,1,0.7) @@ -1505,11 +1511,38 @@ 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 - local function isNodeInARadius(node) + -- Radius indexes to probe: Very Large (4) plus any Time-Lost upgrade tiers (e.g. Baryanic Leylines' + -- "Very Large +40%" at 16) so nodes only reachable via an increased radius still show jewel mods. + local radiusProbeIndexes = { 4 } + if data.nonUniqueTimeLostJewelRadiusUpgrades then + for _, map in pairs(data.nonUniqueTimeLostJewelRadiusUpgrades) do + 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 + 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 diff --git a/src/Data/ModCache.lua b/src/Data/ModCache.lua index 2bd89bc8a..be0945710 100644 --- a/src/Data/ModCache.lua +++ b/src/Data/ModCache.lua @@ -5665,7 +5665,7 @@ c["Non-Channelling Spells have 3% increased Critical Hit Chance per 100 maximum c["Non-Channelling Spells have 5% increased Critical Hit Chance per 100 maximum Life"]={{[1]={[1]={neg=true,skillType=48,type="SkillType"},[2]={div=100,stat="Life",type="PerStat"},flags=2,keywordFlags=0,name="CritChance",type="INC",value=5}},nil} c["Non-Keystone Passive Skills in Medium Radius of allocated Keystone Passive Skills can be allocated without being connected to your tree"]={{[1]={flags=0,keywordFlags=0,name="AllocateFromNodeRadius",type="LIST",value={from="Keystone",radiusIndex=2,to={[1]="Notable",[2]="Normal"}}}},nil} 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 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["Offering Skills have 15% increased Buff effect"]={{[1]={[1]={skillType=154,type="SkillType"},flags=0,keywordFlags=0,name="BuffEffect",type="INC",value=15}},nil} c["Offering Skills have 20% increased Area of Effect"]={{[1]={[1]={skillType=154,type="SkillType"},flags=0,keywordFlags=0,name="AreaOfEffect",type="INC",value=20}},nil} c["Offering Skills have 20% increased Duration"]={{[1]={[1]={skillType=154,type="SkillType"},flags=0,keywordFlags=0,name="Duration",type="INC",value=20}},nil} diff --git a/src/Modules/CalcSetup.lua b/src/Modules/CalcSetup.lua index 58f354d66..3dee18b60 100644 --- a/src/Modules/CalcSetup.lua +++ b/src/Modules/CalcSetup.lua @@ -868,6 +868,13 @@ function calcs.initEnv(build, mode, override, specEnv) end end if item 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 = function(node, out, data) -- Default function just tallies all stats in radius @@ -880,19 +887,19 @@ function calcs.initEnv(build, mode, override, specEnv) for _, func in ipairs(funcList) do local node = env.spec.nodes[slot.nodeId] 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 diff --git a/src/Modules/Data.lua b/src/Modules/Data.lua index a92579ce6..24e403327 100644 --- a/src/Modules/Data.lua +++ b/src/Modules/Data.lua @@ -605,9 +605,42 @@ 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% + { inner = 0, outer = 1400, col = "^xBB6600", label = "Small" }, + { inner = 0, outer = 1610, col = "^x66FFCC", label = "Medium" }, + { inner = 0, outer = 1820, col = "^x2222CC", label = "Large" }, + { inner = 0, outer = 2100, col = "^xC100FF", label = "Very Large" }, } } +-- 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 diff --git a/src/Modules/ModParser.lua b/src/Modules/ModParser.lua index 3feb3402a..994cac583 100644 --- a/src/Modules/ModParser.lua +++ b/src/Modules/ModParser.lua @@ -5350,6 +5350,7 @@ local specialModList = { ["only affects passives in massive ring"] = { mod("JewelData", "LIST", { key = "radiusIndex", value = 12 }) }, ["upgrades radius to medium"] = { mod("JewelData", "LIST", { key = "timeLostJewelRadiusOverride", value = 2 })}, ["upgrades radius to large"] = { mod("JewelData", "LIST", { key = "timeLostJewelRadiusOverride", value = 3 })}, + ["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,