From 6948b5ed44d4cd3cd6c50a88d62a1ca885eddb9f Mon Sep 17 00:00:00 2001 From: Nostrademous Date: Tue, 1 Apr 2025 16:07:32 -0400 Subject: [PATCH 1/3] add support for Unfurled Finger and Mystic Attunement --- src/Classes/ImportTab.lua | 2 +- src/Classes/ItemsTab.lua | 7 ++++- src/Classes/SkillListControl.lua | 1 + src/Classes/SkillsTab.lua | 1 + src/Classes/TradeQuery.lua | 2 +- src/Classes/TradeQueryGenerator.lua | 2 +- src/Modules/CalcPerform.lua | 48 +++++++++++++++++++++++++++-- src/Modules/CalcSetup.lua | 22 +++++++------ src/Modules/Data.lua | 3 +- src/Modules/ModParser.lua | 4 +++ 10 files changed, 74 insertions(+), 18 deletions(-) diff --git a/src/Classes/ImportTab.lua b/src/Classes/ImportTab.lua index f848e5c9fd..8a75fae298 100644 --- a/src/Classes/ImportTab.lua +++ b/src/Classes/ImportTab.lua @@ -777,7 +777,7 @@ function ImportTabClass:ImportItemsAndSkills(json) end local rarityMap = { [0] = "NORMAL", "MAGIC", "RARE", "UNIQUE", [9] = "RELIC", [10] = "RELIC" } -local slotMap = { ["Weapon"] = "Weapon 1", ["Offhand"] = "Weapon 2", ["Weapon2"] = "Weapon 1 Swap", ["Offhand2"] = "Weapon 2 Swap", ["Helm"] = "Helmet", ["BodyArmour"] = "Body Armour", ["Gloves"] = "Gloves", ["Boots"] = "Boots", ["Amulet"] = "Amulet", ["Ring"] = "Ring 1", ["Ring2"] = "Ring 2", ["Belt"] = "Belt" } +local slotMap = { ["Weapon"] = "Weapon 1", ["Offhand"] = "Weapon 2", ["Weapon2"] = "Weapon 1 Swap", ["Offhand2"] = "Weapon 2 Swap", ["Helm"] = "Helmet", ["BodyArmour"] = "Body Armour", ["Gloves"] = "Gloves", ["Boots"] = "Boots", ["Amulet"] = "Amulet", ["Ring"] = "Ring 1", ["Ring2"] = "Ring 2", ["Ring3"] = "Ring 3", ["Belt"] = "Belt" } function ImportTabClass:ImportItem(itemData, slotName) if not slotName then diff --git a/src/Classes/ItemsTab.lua b/src/Classes/ItemsTab.lua index 4a74023b85..7b2269aef3 100644 --- a/src/Classes/ItemsTab.lua +++ b/src/Classes/ItemsTab.lua @@ -27,7 +27,7 @@ local socketDropList = { { label = colorCodes.SCION.."S", color = "W" } } -local baseSlots = { "Weapon 1", "Weapon 2", "Helmet", "Body Armour", "Gloves", "Boots", "Amulet", "Ring 1", "Ring 2", "Belt", "Charm 1", "Charm 2", "Charm 3", "Flask 1", "Flask 2" } +local baseSlots = { "Weapon 1", "Weapon 2", "Helmet", "Body Armour", "Gloves", "Boots", "Amulet", "Ring 1", "Ring 2", "Ring 3","Belt", "Charm 1", "Charm 2", "Charm 3", "Flask 1", "Flask 2" } local catalystQualityFormat = { "^x7F7F7FQuality (Life Modifiers): "..colorCodes.MAGIC.."+%d%% (augmented)", @@ -120,6 +120,11 @@ local ItemsTabClass = newClass("ItemsTab", "UndoHandler", "ControlHost", "Contro swapSlot.shown = function() return self.activeItemSet.useSecondWeaponSet end + elseif slotName == "Ring 3" then + slot.shown = function() + return self.build.spec and self.build.spec.allocNodes[self.build.latestTree.ascendancyMap["gem studded"].skill] + --return self.build.spec and self.build.spec.allocNodes[self.build.latestTree.ascendancyMap["unfurled finger"].skill] + end end end diff --git a/src/Classes/SkillListControl.lua b/src/Classes/SkillListControl.lua index 02a95c3ffe..891f4374b4 100644 --- a/src/Classes/SkillListControl.lua +++ b/src/Classes/SkillListControl.lua @@ -22,6 +22,7 @@ local slot_map = { ["Amulet"] = { icon = NewImageHandle(), path = "Assets/icon_amulet.png" }, ["Ring 1"] = { icon = NewImageHandle(), path = "Assets/icon_ring_left.png" }, ["Ring 2"] = { icon = NewImageHandle(), path = "Assets/icon_ring_right.png" }, + ["Ring 3"] = { icon = NewImageHandle(), path = "Assets/icon_ring_right.png" }, ["Belt"] = { icon = NewImageHandle(), path = "Assets/icon_belt.png" }, } diff --git a/src/Classes/SkillsTab.lua b/src/Classes/SkillsTab.lua index 5ddbd0a2e9..c82494836a 100644 --- a/src/Classes/SkillsTab.lua +++ b/src/Classes/SkillsTab.lua @@ -23,6 +23,7 @@ local groupSlotDropList = { { label = "Amulet", slotName = "Amulet" }, { label = "Ring 1", slotName = "Ring 1" }, { label = "Ring 2", slotName = "Ring 2" }, + { label = "Ring 3", slotName = "Ring 3" }, { label = "Belt", slotName = "Belt" }, } diff --git a/src/Classes/TradeQuery.lua b/src/Classes/TradeQuery.lua index 81db94815a..39325f3331 100644 --- a/src/Classes/TradeQuery.lua +++ b/src/Classes/TradeQuery.lua @@ -16,7 +16,7 @@ local m_min = math.min local m_ceil = math.ceil local s_format = string.format -local baseSlots = { "Weapon 1", "Weapon 2", "Helmet", "Body Armour", "Gloves", "Boots", "Amulet", "Ring 1", "Ring 2", "Belt", "Charm 1", "Charm 2", "Charm 3", "Flask 1", "Flask 2" } +local baseSlots = { "Weapon 1", "Weapon 2", "Helmet", "Body Armour", "Gloves", "Boots", "Amulet", "Ring 1", "Ring 2", "Ring 3", "Belt", "Charm 1", "Charm 2", "Charm 3", "Flask 1", "Flask 2" } local TradeQueryClass = newClass("TradeQuery", function(self, itemsTab) self.itemsTab = itemsTab diff --git a/src/Classes/TradeQueryGenerator.lua b/src/Classes/TradeQueryGenerator.lua index a9f63e1e55..71a5cefeaf 100644 --- a/src/Classes/TradeQueryGenerator.lua +++ b/src/Classes/TradeQueryGenerator.lua @@ -646,7 +646,7 @@ function TradeQueryGeneratorClass:StartQuery(slot, options) elseif slot.slotName == "Amulet" then itemCategoryQueryStr = "accessory.amulet" itemCategory = "Amulet" - elseif slot.slotName == "Ring 1" or slot.slotName == "Ring 2" then + elseif slot.slotName == "Ring 1" or slot.slotName == "Ring 2" or slot.slotName == "Ring 3" then itemCategoryQueryStr = "accessory.ring" itemCategory = "Ring" elseif slot.slotName == "Belt" then diff --git a/src/Modules/CalcPerform.lua b/src/Modules/CalcPerform.lua index 72322eb879..4c38e384b3 100644 --- a/src/Modules/CalcPerform.lua +++ b/src/Modules/CalcPerform.lua @@ -348,7 +348,7 @@ local function determineCursePriority(curseName, activeSkill) elseif source ~= "" then sourcePriority = data.cursePriority["CurseFromEquipment"] end - if source ~= "" and slotPriority == data.cursePriority["Ring 2"] then + if source ~= "" and (slotPriority == data.cursePriority["Ring 2"] or slotPriority == data.cursePriority["Ring 3"]) then -- Implicit and explicit curses from rings have equal priority; only curses from socketed skill gems care about which ring slot they're equipped in slotPriority = data.cursePriority["Ring 1"] end @@ -1066,7 +1066,7 @@ function calcs.perform(env, skipEHP) end local modCopy = copyTable(mod) - modCopy.source = "Many Sources:".. colorCodes.UNIQUE .. "Ingenuity " .. colorCodes.SOURCE .. tostring(ringsEffectMod * 100) .. "% Ring 1 Bonus Effect" + modCopy.source = "Many Sources:".. colorCodes.SOURCE .. tostring(ringsEffectMod * 100) .. "% Ring 1 Bonus Effect" modDB:ScaleAddMod(modCopy, ringsEffectMod) ::skip_mod:: @@ -1088,9 +1088,51 @@ function calcs.perform(env, skipEHP) end local modCopy = copyTable(mod) - modCopy.source = "Many Sources:".. colorCodes.UNIQUE .. "Ingenuity " .. colorCodes.SOURCE .. tostring(ringsEffectMod * 100) .. "% Ring 2 Bonus Effect" + modCopy.source = "Many Sources:".. colorCodes.SOURCE .. tostring(ringsEffectMod * 100) .. "% Ring 2 Bonus Effect" modDB:ScaleAddMod(modCopy, ringsEffectMod) + ::skip_mod:: + end + end + if env.player.itemList["Ring 3"] then + local slotName = "Ring 3" + + if env.player.itemList["Ring 3"].name:match("Kalandra's Touch") and env.player.itemList["Ring 2"] and not env.player.itemList["Ring 2"].name:match("Kalandra's Touch") then + slotName = "Ring 2" + end + + for _, mod in ipairs(env.player.itemList[slotName].modList or env.player.itemList[slotName].slotModList[1]) do + -- Filter out SocketedIn type mods + for _, tag in ipairs(mod) do + if tag.type == "SocketedIn" then + goto skip_mod + end + end + + local modCopy = copyTable(mod) + modCopy.source = "Many Sources:".. colorCodes.SOURCE .. tostring(ringsEffectMod * 100) .. "% Ring 3 Bonus Effect" + modDB:ScaleAddMod(modCopy, ringsEffectMod) + + ::skip_mod:: + end + end + end + + local amuletsEffectMod = modDB:Sum("INC", nil, "EffectOfBonusesFromAmulets") / 100 + if amuletsEffectMod > 0 then + if env.player.itemList["Amulet"] then + for _, mod in ipairs(env.player.itemList["Amulet"].modList or env.player.itemList["Amulet"].slotModList[1]) do + -- Filter out SocketedIn type mods + for _, tag in ipairs(mod) do + if tag.type == "SocketedIn" then + goto skip_mod + end + end + + local modCopy = copyTable(mod) + modCopy.source = "Many Sources:".. colorCodes.SOURCE .. tostring(amuletsEffectMod * 100) .. "% Amulet Bonus Effect" + modDB:ScaleAddMod(modCopy, amuletsEffectMod) + ::skip_mod:: end end diff --git a/src/Modules/CalcSetup.lua b/src/Modules/CalcSetup.lua index 8cdc448583..bcbedb64a1 100644 --- a/src/Modules/CalcSetup.lua +++ b/src/Modules/CalcSetup.lua @@ -846,17 +846,19 @@ function calcs.initEnv(build, mode, override, specEnv) local slotName = slot.slotName if items[slotName] then local srcList = items[slotName].modList or items[slotName].slotModList[slot.slotNum] - for _, mod in ipairs(srcList) do - -- checks if it disables another slot - for _, tag in ipairs(mod) do - if tag.type == "DisablesItem" then - -- e.g. Tincture in Flask 5 while using a Micro-Distillery Belt - if tag.excludeItemType and items[tag.slotName] and items[tag.slotName].type == tag.excludeItemType then + if srcList then + for _, mod in ipairs(srcList) do + -- checks if it disables another slot + for _, tag in ipairs(mod) do + if tag.type == "DisablesItem" then + -- e.g. Tincture in Flask 5 while using a Micro-Distillery Belt + if tag.excludeItemType and items[tag.slotName] and items[tag.slotName].type == tag.excludeItemType then + break + end + itemDisablers[slotName] = tag.slotName + itemDisabled[tag.slotName] = slotName break end - itemDisablers[slotName] = tag.slotName - itemDisabled[tag.slotName] = slotName - break end end end @@ -1066,7 +1068,7 @@ function calcs.initEnv(build, mode, override, specEnv) else env.itemModDB.multipliers["NonCorruptedItem"] = (env.itemModDB.multipliers["NonCorruptedItem"] or 0) + 1 end - local otherRing = items[(slotName == "Ring 1" and "Ring 2") or (slotName == "Ring 2" and "Ring 1")] + local otherRing = items[(slotName == "Ring 1" and "Ring 2") or (slotName == "Ring 2" and "Ring 1") or (slotName == "Ring 3" and "Ring 2")] if otherRing and not otherRing.name:match("Kalandra's Touch") then for _, mod in ipairs(otherRing.modList or otherRing.slotModList[slot.slotNum] or {}) do -- Filter out SocketedIn type mods diff --git a/src/Modules/Data.lua b/src/Modules/Data.lua index 6b3647a6cb..5abf014e0e 100644 --- a/src/Modules/Data.lua +++ b/src/Modules/Data.lua @@ -263,7 +263,8 @@ data.cursePriority = { ["Boots"] = 7000, ["Ring 1"] = 8000, ["Ring 2"] = 9000, - ["CurseFromEquipment"] = 10000, + ["Ring 3"] = 10000, + ["CurseFromEquipment"] = 11000, ["CurseFromAura"] = 20000, } diff --git a/src/Modules/ModParser.lua b/src/Modules/ModParser.lua index 5615cbac82..d05aa32666 100644 --- a/src/Modules/ModParser.lua +++ b/src/Modules/ModParser.lua @@ -4189,6 +4189,10 @@ local specialModList = { ["projectiles deal (%d+)%% increased damage with hits and ailments for each enemy pierced"] = function(num) return { mod("Damage", "INC", num, nil, 0, bor(KeywordFlag.Hit, KeywordFlag.Ailment), { type = "PerStat", stat = "PiercedCount" }, { type = "SkillType", skillType = SkillType.Projectile }) } end, ["(%d+)%% increased bonuses gained from equipped quiver"] = function(num) return {mod("EffectOfBonusesFromQuiver", "INC", num)} end, ["(%d+)%% increased bonuses gained from equipped rings"] = function(num) return {mod("EffectOfBonusesFromRings", "INC", num)} end, + ["(%d+)%% increased bonuses gained from equipped rings and amulets"] = function(num) return { + mod("EffectOfBonusesFromRings", "INC", num), + mod("EffectOfBonusesFromAmulets", "INC", num), + } end, ["(%d+)%% chance for spell skills to fire 2 additional projectiles"] = function(num) return { mod("TwoAdditionalProjectilesChance", "BASE", num, nil , ModFlag.Spell) } end, -- Strike Skills ["non%-vaal strike skills target (%d+) additional nearby enem[yi]e?s?"] = function(num) return { mod("AdditionalStrikeTarget", "BASE", num, { type = "SkillType", skillType = SkillType.MeleeSingleTarget}, { type = "SkillType", skillType = SkillType.Vaal, neg = true}) } end, From c77b80149b6187f69ef2e43adaefa665b772f301 Mon Sep 17 00:00:00 2001 From: Nostrademous Date: Tue, 1 Apr 2025 22:30:08 -0400 Subject: [PATCH 2/3] fixed compare stats for Ring3 only showing up when Ring3 exists --- src/Classes/Item.lua | 3 +++ src/Classes/ItemsTab.lua | 7 ++++--- src/Modules/CalcSetup.lua | 20 +++++++++----------- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/Classes/Item.lua b/src/Classes/Item.lua index 0bdc088852..f76e9c0801 100644 --- a/src/Classes/Item.lua +++ b/src/Classes/Item.lua @@ -1628,6 +1628,9 @@ function ItemClass:BuildModList() for i = 1, 2 do self.slotModList[i] = self:BuildModListForSlotNum(baseList, i) end + if self.type == "Ring" then + self.slotModList[3] = self:BuildModListForSlotNum(baseList, 3) + end else self.modList = self:BuildModListForSlotNum(baseList) end diff --git a/src/Classes/ItemsTab.lua b/src/Classes/ItemsTab.lua index 7b2269aef3..63b779c1c0 100644 --- a/src/Classes/ItemsTab.lua +++ b/src/Classes/ItemsTab.lua @@ -122,8 +122,9 @@ local ItemsTabClass = newClass("ItemsTab", "UndoHandler", "ControlHost", "Contro end elseif slotName == "Ring 3" then slot.shown = function() - return self.build.spec and self.build.spec.allocNodes[self.build.latestTree.ascendancyMap["gem studded"].skill] - --return self.build.spec and self.build.spec.allocNodes[self.build.latestTree.ascendancyMap["unfurled finger"].skill] + -- local nodeCheck = self.build.latestTree.ascendancyMap["gem studded"] + local nodeCheck = self.build.latestTree.ascendancyMap["unfurled finger"] + return self.build.spec and nodeCheck and self.build.spec.allocNodes[nodeCheck.skill] end end end @@ -3039,7 +3040,7 @@ function ItemsTabClass:AddItemTooltip(tooltip, item, slot, dbMode) -- Build sorted list of slots to compare with local compareSlots = { } for slotName, slot in pairs(self.slots) do - if self:IsItemValidForSlot(item, slotName) and not slot.inactive and (not slot.weaponSet or slot.weaponSet == (self.activeItemSet.useSecondWeaponSet and 2 or 1)) then + if self:IsItemValidForSlot(item, slotName) and not slot.inactive and (not slot.weaponSet or slot.weaponSet == (self.activeItemSet.useSecondWeaponSet and 2 or 1)) and slot.shown() then t_insert(compareSlots, slot) end end diff --git a/src/Modules/CalcSetup.lua b/src/Modules/CalcSetup.lua index bcbedb64a1..b9ce059a68 100644 --- a/src/Modules/CalcSetup.lua +++ b/src/Modules/CalcSetup.lua @@ -846,19 +846,17 @@ function calcs.initEnv(build, mode, override, specEnv) local slotName = slot.slotName if items[slotName] then local srcList = items[slotName].modList or items[slotName].slotModList[slot.slotNum] - if srcList then - for _, mod in ipairs(srcList) do - -- checks if it disables another slot - for _, tag in ipairs(mod) do - if tag.type == "DisablesItem" then - -- e.g. Tincture in Flask 5 while using a Micro-Distillery Belt - if tag.excludeItemType and items[tag.slotName] and items[tag.slotName].type == tag.excludeItemType then - break - end - itemDisablers[slotName] = tag.slotName - itemDisabled[tag.slotName] = slotName + for _, mod in ipairs(srcList) do + -- checks if it disables another slot + for _, tag in ipairs(mod) do + if tag.type == "DisablesItem" then + -- e.g. Tincture in Flask 5 while using a Micro-Distillery Belt + if tag.excludeItemType and items[tag.slotName] and items[tag.slotName].type == tag.excludeItemType then break end + itemDisablers[slotName] = tag.slotName + itemDisabled[tag.slotName] = slotName + break end end end From 418e87131e55f3e405d1752183af1ff6c194dfc3 Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Sun, 6 Apr 2025 13:59:15 +1000 Subject: [PATCH 3/3] Use flag instead of hard coding --- src/Classes/ItemsTab.lua | 4 +--- src/Data/ModCache.lua | 1 + src/Modules/ModParser.lua | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Classes/ItemsTab.lua b/src/Classes/ItemsTab.lua index a42041ac0d..e534f880b3 100644 --- a/src/Classes/ItemsTab.lua +++ b/src/Classes/ItemsTab.lua @@ -122,9 +122,7 @@ local ItemsTabClass = newClass("ItemsTab", "UndoHandler", "ControlHost", "Contro end elseif slotName == "Ring 3" then slot.shown = function() - -- local nodeCheck = self.build.latestTree.ascendancyMap["gem studded"] - local nodeCheck = self.build.latestTree.ascendancyMap["unfurled finger"] - return self.build.spec and nodeCheck and self.build.spec.allocNodes[nodeCheck.skill] + return self.build.calcsTab.mainEnv.modDB:Flag(nil, "AdditionalRingSlot") end end end diff --git a/src/Data/ModCache.lua b/src/Data/ModCache.lua index 4fabb6a8f4..e9d8a67b82 100755 --- a/src/Data/ModCache.lua +++ b/src/Data/ModCache.lua @@ -85,6 +85,7 @@ c["+0% to Lightning Resistance"]={{[1]={flags=0,keywordFlags=0,name="LightningRe c["+0.15% to Thorns Critical Hit Chance"]={{[1]={flags=0,keywordFlags=0,name="CritChance",type="BASE",value=0.15}}," Thorns "} c["+0.15% to Thorns Critical Hit Chance +25% to Thorns Critical Hit Chance"]={{[1]={flags=0,keywordFlags=0,name="CritChance",type="BASE",value=0.15}}," Thorns +25% to Thorns Critical Hit Chance "} c["+1 Charm Slot"]={{[1]={flags=0,keywordFlags=0,name="CharmLimit",type="BASE",value=1}},nil} +c["+1 Ring Slot"]={{[1]={flags=0,keywordFlags=0,name="AdditionalRingSlot",type="FLAG",value=true}},nil} c["+1 metre to Dodge Roll distance"]={{}," metre to Dodge Roll distance "} c["+1 metre to Dodge Roll distance 50% increased Evasion Rating if you've Dodge Rolled Recently"]={{[1]={[1]={type="Condition",var="DodgeRolledRecently"},flags=0,keywordFlags=0,name="Evasion",type="BASE",value=1}}," metre to Dodge Roll distance 50% increased "} c["+1 to Evasion Rating per 1 Armour on Equipped Gloves"]={{[1]={[1]={div=1,stat="ArmourOnGloves",type="PerStat"},flags=0,keywordFlags=0,name="Evasion",type="BASE",value=1}},nil} diff --git a/src/Modules/ModParser.lua b/src/Modules/ModParser.lua index 9f9b56f683..7a74b48b67 100644 --- a/src/Modules/ModParser.lua +++ b/src/Modules/ModParser.lua @@ -2777,6 +2777,7 @@ local specialModList = { ["non%-channelling spells cost an additional (%d+)%% of maximum energy shield"] = function(num) return { mod("ESCostBase", "BASE", 1, nil, 0, KeywordFlag.Spell, { type = "PercentStat", percent = num, stat = "EnergyShield" }, { type = "SkillType", skillType = SkillType.Channel, neg = true } )} end, ["non%-channelling spells consume power charges to deal (%d+)%% more damage"] = function(num) return { mod("Damage", "MORE", num, nil, 0,KeywordFlag.Spell, { type = "SkillType", skillType = SkillType.Channel, neg = true }, { type = "MultiplierThreshold", var = "RemovablePowerCharge", threshold = 1 })} end, ["no inherent mana regeneration"] = { flag("Condition:NoInherentManaRegen") }, + ["%+1 ring slot"] = { flag("AdditionalRingSlot") }, ["regenerate (%D+) equal to (%d+)%% of maximum (%D+) per second"] = function(_, resource1, num, resource2) return { mod( combineToUpper(resource1) .. "Regen", "BASE", 1, { type = "PercentStat", stat = combineToUpper(resource2), percent = num } )} end, -- Mercenary -- +2 Weapon Set Passive Skill Points