Files
PathOfBuilding/src/Data/Uniques/Special/Generated.lua
Никита Троянов 18736464c9 Fix Intuitive Leap, Thread of Hope and Impossible Escape not working (#9135)
* fix #9125

* Update parsing + update unique mod lines

---------

Co-authored-by: LocalIdentity <localidentity2@gmail.com>
2025-11-03 08:03:05 +11:00

944 lines
36 KiB
Lua

---
--- Programmatically generated uniques live here.
--- Some uniques have to be generated because the amount of variable mods makes it infeasible to implement them manually.
--- As a result, they are forward compatible to some extent as changes to the variable mods are picked up automatically.
---
data.uniques.generated = { }
local parseVeiledModName = function(string)
return (string:
gsub("%JunMasterVeiled", ""):
gsub("%Local", ""):
gsub("%Display", ""):
gsub("%Crafted", ""):
gsub("(%d)h", ""):
gsub("%_", ""):
gsub("(%l)(%u)", "%1 %2"):
gsub("(%d)", " %1 "))
end
local veiledModIsActive = function(mod, baseType, specificType1, specificType2)
local baseIndex = isValueInTable(mod.weightKey, baseType)
local typeIndex1 = isValueInTable(mod.weightKey, specificType1)
local typeIndex2 = isValueInTable(mod.weightKey, specificType2)
return (typeIndex1 and mod.weightVal[typeIndex1] > 0) or (typeIndex2 and mod.weightVal[typeIndex2] > 0) or (not typeIndex1 and not typeIndex2 and baseIndex and mod.weightVal[baseIndex] > 0)
end
local getVeiledMods = function (veiledPool, baseType, specificType1, specificType2)
local veiledMods = { }
for veiledModIndex, veiledMod in pairs(data.veiledMods) do
if veiledModIsActive(veiledMod, baseType, specificType1, specificType2) then
local veiledName = parseVeiledModName(veiledModIndex)
veiledName = "("..veiledMod.type..") "..veiledName
local veiled = { veiledName = veiledName, veiledLines = { } }
for line, value in ipairs(veiledMod) do
veiled.veiledLines[line] = value
end
if veiledPool == "base" and (veiledMod.affix == "Chosen" or veiledMod.affix == "of the Order") then
table.insert(veiledMods, veiled)
elseif veiledPool == "catarina" and (veiledMod.affix == "Catarina's" or veiledMod.affix == "Chosen" or veiledMod.affix == "of the Order") then
table.insert(veiledMods, veiled)
elseif veiledPool == "all" then
table.insert(veiledMods, veiled)
end
end
end
table.sort(veiledMods, function (m1, m2) return m1.veiledName < m2.veiledName end )
return veiledMods
end
local paradoxicaMods = getVeiledMods("base", "weapon", "one_hand_weapon")
local paradoxica = {
"Paradoxica",
"Vaal Rapier",
"League: Betrayal",
"Source: Drops from unique{Intervention Leaders} in normal{Safehouses}",
"Has Alt Variant: true",
"Selected Variant: 4",
"Selected Alt Variant: 16"
}
for index, mod in pairs(paradoxicaMods) do
if (mod.veiledName == "(Suffix) Double Damage Chance") then
table.remove(paradoxicaMods, index)
end
end
for index, mod in pairs(paradoxicaMods) do
table.insert(paradoxica, "Variant: "..mod.veiledName)
end
table.insert(paradoxica, "Requires Level 66, 212 Dex")
table.insert(paradoxica, "Implicits: 1")
table.insert(paradoxica, "+25% to Global Critical Strike Multiplier")
for index, mod in pairs(paradoxicaMods) do
for _, value in pairs(mod.veiledLines) do
table.insert(paradoxica, "{variant:"..index.."}"..value.."")
end
end
table.insert(paradoxica, "Attacks with this Weapon deal Double Damage")
table.insert(data.uniques.generated, table.concat(paradoxica, "\n"))
local caneOfKulemakMods = getVeiledMods("catarina", "weapon", "staff", "two_hand_weapon")
local caneOfKulemakMinUnveiledModifierMagnitudes, caneOfKulemakMaxUnveiledModifierMagnitudes = 60, 90
local caneOfKulemak = {
"Cane of Kulemak",
"Serpentine Staff",
"Source: Drops from unique{Catarina, Master of Undeath}",
"Has Alt Variant: true",
"Has Alt Variant Two: true",
"Selected Variant: 1",
"Selected Alt Variant: 20"
}
for index, mod in pairs(caneOfKulemakMods) do
table.insert(caneOfKulemak, "Variant: "..mod.veiledName)
end
table.insert(caneOfKulemak, "Requires Level 68, 85 Str, 85 Int")
table.insert(caneOfKulemak, "Implicits: 1")
table.insert(caneOfKulemak, "+22% Chance to Block Attack Damage while wielding a Staff")
table.insert(caneOfKulemak, "("..caneOfKulemakMinUnveiledModifierMagnitudes.."-"..caneOfKulemakMaxUnveiledModifierMagnitudes..")% increased Unveiled Modifier magnitudes")
for index, mod in pairs(caneOfKulemakMods) do
for _, value in pairs(mod.veiledLines) do
local minValue, maxValue = value:match("%((%d+)%-(%d+)%)")
if minValue then
value = value:gsub("%(%d+%-%d+%)", "%("..tostring(math.floor(minValue*(100 + caneOfKulemakMinUnveiledModifierMagnitudes) / 100)).."%-"..tostring(math.floor(maxValue*(100 + caneOfKulemakMaxUnveiledModifierMagnitudes) / 100)).."%)")
elseif value == "+2 to Level of Socketed Support Gems" then
value = "+3 to Level of Socketed Support Gems"
end
table.insert(caneOfKulemak, "{variant:"..index.."}"..value.."")
end
end
table.insert(data.uniques.generated, table.concat(caneOfKulemak, "\n"))
local replicaParadoxicaMods = getVeiledMods("all", "weapon", "one_hand_weapon")
local replicaParadoxica = {
"Replica Paradoxica",
"Vaal Rapier",
"League: Heist",
"Source: Steal from a unique{Curio Display} during a Grand Heist",
"Has Alt Variant: true",
"Has Alt Variant Two: true",
"Has Alt Variant Three: true",
"Has Alt Variant Four: true",
"Has Alt Variant Five: true",
"Selected Variant: 1",
"Selected Alt Variant: 2",
"Selected Alt Variant Two: 3",
"Selected Alt Variant Three: 25",
"Selected Alt Variant Four: 27",
"Selected Alt Variant Five: 34"
}
for index, mod in pairs(replicaParadoxicaMods) do
table.insert(replicaParadoxica, "Variant: "..mod.veiledName)
end
table.insert(replicaParadoxica, "Requires Level 66, 212 Dex")
table.insert(replicaParadoxica, "Implicits: 1")
table.insert(replicaParadoxica, "+25% to Global Critical Strike Multiplier")
for index, mod in pairs(replicaParadoxicaMods) do
for _, value in pairs(mod.veiledLines) do
table.insert(replicaParadoxica, "{variant:"..index.."}"..value.."")
end
end
table.insert(data.uniques.generated, table.concat(replicaParadoxica, "\n"))
local queensHungerMods = getVeiledMods("base", "body_armour", "int_armour")
local queensHunger = {
"The Queen's Hunger",
"Vaal Regalia",
"League: Betrayal",
"Source: Drops from unique{Catarina, Master of Undeath}",
"Has Alt Variant: true",
"Selected Variant: 1",
"Selected Alt Variant: 24"
}
for index, mod in pairs(queensHungerMods) do
table.insert(queensHunger, "Variant: "..mod.veiledName)
end
table.insert(queensHunger, "Requires Level 68, 194 Int")
table.insert(queensHunger, "Trigger Level 20 Bone Offering, Flesh Offering or Spirit Offering every 5 seconds")
table.insert(queensHunger, "Offering Skills Triggered this way also affect you")
table.insert(queensHunger, "(5-10)% increased Cast Speed")
table.insert(queensHunger, "(100-130)% increased Energy Shield")
table.insert(queensHunger, "(6-10)% increased maximum Life")
for index, mod in pairs(queensHungerMods) do
for _, value in pairs(mod.veiledLines) do
table.insert(queensHunger, "{variant:"..index.."}"..value.."")
end
end
table.insert(data.uniques.generated, table.concat(queensHunger, "\n"))
local megalomaniac = {
"Megalomaniac",
"Medium Cluster Jewel",
"League: Delirium",
"Source: Drops from the Simulacrum Encounter",
"Has Alt Variant: true",
"Has Alt Variant Two: true",
}
local notables = { }
for name in pairs(data.clusterJewels.notableSortOrder) do
table.insert(notables, name)
end
table.sort(notables)
for index, name in ipairs(notables) do
table.insert(megalomaniac, "Variant: "..name)
end
table.insert(megalomaniac, "Adds 4 Passive Skills")
table.insert(megalomaniac, "Added Small Passive Skills grant Nothing")
for index, name in ipairs(notables) do
table.insert(megalomaniac, "{variant:"..index.."}1 Added Passive Skill is "..name)
end
table.insert(data.uniques.generated, table.concat(megalomaniac, "\n"))
local forbiddenShako = {
"Forbidden Shako",
"Great Crown",
"League: Harvest",
"Source: Drops from unique{Oshabi, Avatar of the Grove}",
"Requires Level 68, 59 Str, 59 Int",
"Has Alt Variant: true"
}
local replicaForbiddenShako = {
"Replica Forbidden Shako",
"Great Crown",
"League: Heist",
"Source: Steal from a unique{Curio Display} during a Grand Heist",
"Requires Level 68, 59 Str, 59 Int",
"Has Alt Variant: true"
}
local excludedGems = {
"Block Chance Reduction",
"Empower",
"Enhance",
"Enlighten",
"Item Quantity",
}
local gems = { }
for _, gemData in pairs(data.gems) do
local grantedEffect = gemData.grantedEffect
if grantedEffect.support and not (grantedEffect.plusVersionOf) and not isValueInArray(excludedGems, grantedEffect.name) then
table.insert(gems, grantedEffect.name)
end
end
table.sort(gems)
for index, name in ipairs(gems) do
table.insert(forbiddenShako, "Variant: "..name.. " (Low Level)")
table.insert(forbiddenShako, "Variant: "..name.. " (High Level)")
table.insert(replicaForbiddenShako, "Variant: "..name.. " (Low Level)")
table.insert(replicaForbiddenShako, "Variant: "..name.. " (High Level)")
end
for index, name in ipairs(gems) do
table.insert(forbiddenShako, "{variant:"..(index * 2 - 1).."}Socketed Gems are Supported by Level (1-10) "..name)
table.insert(forbiddenShako, "{variant:"..(index * 2).."}Socketed Gems are Supported by Level (25-35) "..name)
table.insert(replicaForbiddenShako, "{variant:"..(index * 2 - 1).."}Socketed Gems are Supported by Level (1-10) "..name)
table.insert(replicaForbiddenShako, "{variant:"..(index * 2).."}Socketed Gems are Supported by Level (25-35) "..name)
end
table.insert(forbiddenShako, "+(25-30) to all Attributes")
table.insert(replicaForbiddenShako, "+(25-30) to all Attributes")
table.insert(data.uniques.generated, table.concat(forbiddenShako, "\n"))
table.insert(data.uniques.generated, table.concat(replicaForbiddenShako, "\n"))
local enduranceChargeMods = {
[3] = {
["Up to Max."] = "15% chance that if you would gain Endurance Charges, you instead gain up to your maximum number of Endurance Charges",
["Duration"] = "(20-40)% increased Endurance Charge Duration",
["Movement Speed"] = "1% increased Movement Speed per Endurance Charge",
["Armour"] = "6% increased Armour per Endurance Charge",
["Add Fire Damage"] = "(7-9) to (13-14) Fire Damage per Endurance Charge",
["Inc. Damage"] = "5% increased Damage per Endurance Charge",
["On Kill"] = "10% chance to gain an Endurance Charge on Kill",
},
[2] = {
["Block Attacks"] = "1% Chance to Block Attack Damage per Endurance Charge",
["Spell Suppression"] = "1% chance to Suppress Spell Damage per Endurance Charge",
["Chaos Res"] = "+4% to Chaos Resistance per Endurance Charge",
["Fire as Chaos"] = "Gain 1% of Fire Damage as Extra Chaos Damage per Endurance Charge",
["Attack and Cast Speed"] = "1% increased Attack and Cast Speed per Endurance Charge",
["Regen. Life"] = "Regenerate 0.3% of Life per second per Endurance Charge",
["Inc. Critical Strike Chance"] = "6% increased Critical Strike Chance per Endurance Charge",
},
[1] = {
["Gain every second"] = "Gain an Endurance Charge every second if you've been Hit Recently",
["+1 Maximum"] = "+1 to Maximum Endurance Charges",
["Cannot be Stunned"] = "You cannot be Stunned while at maximum Endurance Charges",
["Vaal Pact"] = "You have Vaal Pact while at maximum Endurance Charges",
["Intimidate"] = "Intimidate Enemies for 4 seconds on Hit with Attacks while at maximum Endurance Charges",
},
}
local frenzyChargeMods = {
[3] = {
["Up to Max."] = "15% chance that if you would gain Frenzy Charges, you instead gain up to your maximum number of Frenzy Charges",
["Duration"] = "(20-40)% increased Frenzy Charge Duration",
["Movement Speed"] = "1% increased Movement Speed per Frenzy Charge",
["Evasion"] = "8% increased Evasion Rating per Frenzy Charge",
["Add Cold Damage"] = "(6-8) to (12-13) Cold Damage per Frenzy Charge",
["Inc. Damage"] = "5% increased Damage per Frenzy Charge",
["On Kill"] = "10% chance to gain an Frenzy Charge on Kill",
},
[2] = {
["Block Attacks"] = "1% Chance to Block Attack Damage per Frenzy Charge",
["Spell Suppression"] = "1% chance to Suppress Spell Damage per Frenzy Charge",
["Accuracy Rating"] = "10% increased Accuracy Rating per Frenzy Charge",
["Cold as Chaos"] = "Gain 1% of Cold Damage as Extra Chaos Damage per Frenzy Charge",
["Attack and Cast Speed"] = "1% increased Attack and Cast Speed per Frenzy Charge",
["Regen. Life"] = "Regenerate 0.3% of Life per second per Frenzy Charge",
["Inc. Critical Strike Chance"] = "6% increased Critical Strike Chance per Frenzy Charge",
},
[1] = {
["Gain on Hit"] = "10% chance to gain a Frenzy Charge on Hit",
["+1 Maximum"] = "+1 to Maximum Frenzy Charges",
["Flask Charge on Crit"] = "Gain a Flask Charge when you deal a Critical Strike while at maximum Frenzy Charges",
["Iron Reflexes"] = "You have Iron Reflexes while at maximum Frenzy Charges",
["Onslaught"] = "Gain Onslaught for 4 seconds on Hit while at maximum Frenzy Charges",
},
}
local powerChargeMods = {
[3] = {
["Up to Max."] = "15% chance that if you would gain Power Charges, you instead gain up to your maximum number of Power Charges",
["Duration"] = "(20-40)% increased Power Charge Duration",
["Movement Speed"] = "1% increased Movement Speed per Power Charge",
["Energy Shield"] = "3% increased Energy Shield per Power Charge",
["Add Lightning Damage"] = "(1-2) to (18-20) Lightning Damage per Power Charge",
["Inc. Damage"] = "5% increased Damage per Power Charge",
["On Kill"] = "10% chance to gain an Power Charge on Kill",
},
[2] = {
["Block Attacks"] = "1% Chance to Block Attack Damage per Power Charge",
["Spell Suppression"] = "1% chance to Suppress Spell Damage per Power Charge",
["Phys. Damage Red."] = "1% additional Physical Damage Reduction per Power Charge",
["Lightning as Chaos"] = "Gain 1% of Lightning Damage as Extra Chaos Damage per Power Charge",
["Attack and Cast Speed"] = "1% increased Attack and Cast Speed per Power Charge",
["Regen. Life"] = "Regenerate 0.3% of Life per second per Power Charge",
["Crit Strike Multi"] = "+3% to Critical Strike Multiplier per Power Charge",
},
[1] = {
["Gain on Crit"] = "20% chance to gain a Power Charge on Critical Strike",
["+1 Maximum"] = "+1 to Maximum Power Charges",
["Arcane Surge with Spells"] = "Gain Arcane Surge on Hit with Spells while at maximum Power Charges",
["Mind over Matter"] = "You have Mind over Matter while at maximum Power Charges",
["Additional Curse"] = "You can apply an additional Curse while at maximum Power Charges",
},
}
local precursorsEmblem = {
[[Precursor's Emblem
{variant:1}Topaz Ring
{variant:2}Sapphire Ring
{variant:3}Ruby Ring
{variant:4}Two-Stone Ring (Cold/Lightning)
{variant:5}Two-Stone Ring (Fire/Lightning)
{variant:6}Two-Stone Ring (Fire/Cold)
{variant:7}Prismatic Ring
League: Delve
Source: Vendor Recipe
Variant: Topaz Ring
Variant: Sapphire Ring
Variant: Ruby Ring
Variant: Two-Stone Ring (Cold/Lightning)
Variant: Two-Stone Ring (Fire/Lightning)
Variant: Two-Stone Ring (Fire/Cold)
Variant: Prismatic Ring]]
}
for _, type in ipairs({ { prefix = "Endurance - ", mods = enduranceChargeMods }, { prefix = "Frenzy - ", mods = frenzyChargeMods }, { prefix = "Power - ", mods = powerChargeMods } }) do
for tier, mods in ipairs(type.mods) do
for desc in pairsSortByKey(mods) do
table.insert(precursorsEmblem, "Variant: " .. type.prefix .. desc)
end
end
end
table.insert(precursorsEmblem, [[Selected Variant: 1
Has Alt Variant: true
Has Alt Variant Two: true
Has Alt Variant Three: true
LevelReq: 49
Implicits: 7
{tags:jewellery_resistance}{variant:1}+(20-30)% to Lightning Resistance
{tags:jewellery_resistance}{variant:2}+(20-30)% to Cold Resistance
{tags:jewellery_resistance}{variant:3}+(20-30)% to Fire Resistance
{tags:jewellery_resistance}{variant:4}+(12-16)% to Cold and Lightning Resistances
{tags:jewellery_resistance}{variant:5}+(12-16)% to Fire and Lightning Resistances
{tags:jewellery_resistance}{variant:6}+(12-16)% to Fire and Cold Resistances
{tags:jewellery_resistance}{variant:7}+(8-10)% to all Elemental Resistances
{tags:jewellery_attribute}{variant:1}+20 to Intelligence
{tags:jewellery_attribute}{variant:2}+20 to Dexterity
{tags:jewellery_attribute}{variant:3}+20 to Strength
{tags:jewellery_attribute}{variant:4}+20 to Strength and Intelligence
{tags:jewellery_attribute}{variant:5}+20 to Dexterity and Intelligence
{tags:jewellery_attribute}{variant:6}+20 to Strength and Dexterity
{tags:jewellery_attribute}{variant:7}+20 to all Attributes
{tags:jewellery_defense}5% increased maximum Energy Shield
{tags:life}5% increased maximum Life]])
local index = 8
for _, type in ipairs({ enduranceChargeMods, frenzyChargeMods, powerChargeMods }) do
for tier, mods in ipairs(type) do
for desc, mod in pairsSortByKey(mods) do
if mod:match("[%+%-]?[%d%.]*%d+%%") then
mod = mod:gsub("([%d%.]*%d+)", function(num) return "(" .. num .. "-" .. tonumber(num) * tier .. ")" end)
elseif mod:match("%(%-?[%d%.]+%-%-?[%d%.]+%)%%") then
mod = mod:gsub("(%(%-?[%d%.]+%-)(%-?[%d%.]+)%)", function(preceding, higher) return preceding .. tonumber(higher) * tier .. ")" end)
elseif mod:match("%(%d+%-%d+%) to %(%d+%-%d+%)") then
mod = mod:gsub("(%(%d+%-)(%d+)(%) to %(%d+%-)(%d+)%)", function(preceding, higher1, middle, higher2) return preceding .. higher1 * tier .. middle .. higher2 * tier .. ")" end)
end
table.insert(precursorsEmblem, "{variant:" .. index .. "}{range:0}" .. mod)
index = index + 1
end
end
end
table.insert(data.uniques.generated, table.concat(precursorsEmblem, "\n"))
local balanceOfTerrorMods = {
["Vulnerability: Double Damage"] = "(6-10)% chance to deal Double Damage if you've cast Vulnerability in the past 10 seconds",
["Vulnerability: Unaffected by Bleeding"] = "You are Unaffected by Bleeding if you've cast Vulnerability in the past 10 seconds",
["Enfeeble: Critical Strike Multiplier"] = "+(30-40)% to Critical Strike Multiplier if you've cast Enfeeble in the past 10 seconds",
["Enfeeble: Take no Extra Crit Damage"] = "Take no Extra Damage from Critical Strikes if you've cast Enfeeble in the past 10 seconds",
["Despair: Immune to Curses"] = "Immune to Curses if you've cast Despair in the past 10 seconds",
["Despair: Inflict Withered"] = "Inflict Withered for 2 seconds on Hit if you've cast Despair in the past 10 seconds",
["Punishment: Immune to Reflected Damage"] = "Immune to Reflected Damage if you've cast Punishment in the past 10 seconds",
["Punishment: Intimidate"] = "Intimidate Enemies on Hit if you've cast Punishment in the past 10 seconds",
["Frostbite: Cold Exposure"] = "Cold Exposure on Hit if you've cast Frostbite in the past 10 seconds",
["Frostbite: Unaffected by Freeze"] = "You are Unaffected by Freeze if you've cast Frostbite in the past 10 seconds",
["Flammability: Fire Exposure"] = "Inflict Fire Exposure on Hit if you've cast Flammability in the past 10 seconds",
["Flammability: Unaffected by Ignite"] = "You are Unaffected by Ignite if you've cast Flammability in the past 10 seconds",
["Conductivity: Lightning Exposure"] = "Inflict Lightning Exposure on Hit if you've cast Conductivity in the past 10 seconds",
["Conductivity: Unaffected by Shock"] = "You are Unaffected by Shock if you've cast Conductivity in the past 10 seconds",
["Elemental Weakness: Immune to Exposure"] = "Immune to Exposure if you've cast Elemental Weakness in the past 10 seconds",
["Elemental Weakness: Physical Damage as a Random Element"] = "Gain (30-40)% of Physical Damage as a Random Element if you've cast Elemental Weakness in the past 10 seconds",
["Temporal Chains: Cooldown Recovery Rate"] = "(20-25)% increased Cooldown Recovery Rate if you've cast Temporal Chains in the past 10 seconds",
["Temporal Chains: Action Speed"] = "Action Speed cannot be Slowed below Base Value if you've cast Temporal Chains in the past 10 seconds",
}
local balanceOfTerror = {
"The Balance of Terror",
"Cobalt Jewel",
"League: Sanctum",
"Source: Drops from unique{Lycia, Herald of the Scourge} in normal{The Beyond}",
"Has Alt Variant: true",
"Has Alt Variant Two: true",
"Selected Alt Variant Two: 1",
"Limited to: 1",
"LevelReq: 56",
}
-- adding a blank variant for 3 mod jewels
table.insert(balanceOfTerror, "Variant: None")
for name in pairsSortByKey(balanceOfTerrorMods) do
table.insert(balanceOfTerror, "Variant: "..name)
end
table.insert(balanceOfTerror, "+(10-15)% to all Elemental Resistances")
local index = 2
for name, line in pairsSortByKey(balanceOfTerrorMods) do
table.insert(balanceOfTerror, "{variant:"..index.."}"..balanceOfTerrorMods[name])
index = index + 1
end
table.insert(data.uniques.generated, table.concat(balanceOfTerror, "\n"))
--[[ 3 scenarios exist for legacy mods
- Mod changed, but kept the same mod Id
-- Has legacyMod
- Mod removed, or changed with a new mod Id
-- Has only a version when it changed
- Mod changed/removed, but isn't legacy
-- Has empty table to exclude it from the list
4th scenario: Mod was changed (not legacy), but the mod ID (aka Variant name) no longer reflects the mod
-- Has 'rename' field to customize the name
]]
local watchersEyeLegacyMods = {
["ClarityManaAddedAsEnergyShield"] = {
["version"] = "3.12.0",
["legacyMod"] = function(currentMod) return (currentMod:gsub("%(.*%)", "(12-18)")) end,
},
["ClarityReducedManaCost"] = {
["version"] = "3.8.0",
},
["ClarityManaRecoveryRate"] = {
["version"] = "3.12.0",
["legacyMod"] = function(currentMod) return (currentMod:gsub("%(.*%)", "(20-30)")) end,
},
["DisciplineEnergyShieldRecoveryRate"] = {
["version"] = "3.12.0",
["legacyMod"] = function(currentMod) return (currentMod:gsub("%(.*%)", "(20-30)")) end,
},
["MalevolenceDamageOverTimeMultiplier"] = {
["version"] = "3.8.0",
["legacyMod"] = function(currentMod) return (currentMod:gsub("%(.*%)", "(36-44)")) end,
},
["MalevolenceLifeAndEnergyShieldRecoveryRate"] = {
["version"] = "3.12.0",
["legacyMod"] = function(currentMod) return (currentMod:gsub("%(.*%)", "(15-20)")) end,
},
["PrecisionIncreasedCriticalStrikeMultiplier"] = {
["version"] = "3.12.0",
["legacyMod"] = function(currentMod) return (currentMod:gsub("%(.*%)", "(30-50)")) end,
},
["VitalityDamageLifeLeech"] = {
["version"] = "3.12.0",
["legacyMod"] = function(currentMod) return (currentMod:gsub("%(.*%)", "(1-1.5)")) end,
},
["VitalityFlatLifeRegen"] = {
["version"] = "3.12.0",
},
["VitalityLifeRecoveryRate"] = {
["version"] = "3.12.0",
["legacyMod"] = function(currentMod) return (currentMod:gsub("%(.*%)", "(20-30)")) end,
},
["WrathLightningDamageManaLeech"] = {
["version"] = "3.8.0",
},
["GraceChanceToDodge"] = {
["rename"] = "Grace: Chance to Suppress Spells",
},
["HasteChanceToDodgeSpells"] = {
["rename"] = "Haste: Chance to Suppress Spells",
},
["PurityOfFireTakePhysicalAsFire"] = {
["version"] = "3.25.0",
},
["PurityOfIceTakePhysicalAsIce"] = {
["version"] = "3.25.0",
},
["PurityOfLightningTakePhysicalAsLightning"] = {
["version"] = "3.25.0",
},
["PurityOfElementsTakePhysicalAsFire_"] = {
["version"] = "3.25.0",
},
["PurityOfElementsTakePhysicalAsCold"] = {
["version"] = "3.25.0",
},
["PurityOfElementsTakePhysicalAsLightning"] = {
["version"] = "3.25.0",
},
["PurityOfFireReducedReflectedFireDamage"] = { },
["PurityOfIceReducedReflectedColdDamage"] = { },
["PurityOfLightningReducedReflectedLightningDamage"] = { },
["MalevolenceSkillEffectDuration"] = { },
["ZealotryMaximumEnergyShieldPerSecondToMaximumEnergyShieldLeechRate"] = { },
["MalevolenceColdDamageOverTimeMultiplier"] = { },
["MalevolenceChaosNonAilmentDamageOverTimeMultiplier"] = { },
}
local watchersEye = {
[[
Watcher's Eye
Prismatic Jewel
Source: Drops from unique{The Elder} or unique{The Elder} (Uber)
Has Alt Variant: true
Has Alt Variant Two: true
Selected Variant: 5
Selected Alt Variant: 30
Selected Alt Variant Two: 1
]]
}
local sublimeVision = {
[[
Sublime Vision
Prismatic Jewel
Shaper Item
Source: Drops from unique{The Elder} (Uber Uber) or unique{The Shaper} (Uber)
Limited to: 1
]]
}
local voranasMarch = {
[[
Vorana's March
Runic Sabatons
League: Expedition
Source: Drops from unique{Olroth, Origin of the Fall} in normal{Expedition Logbook}
Has Alt Variant: true
Has Alt Variant Two: true
Has Alt Variant Three: true
Selected Variant: 24
Selected Alt Variant: 10
Selected Alt Variant Two: 11
Selected Alt Variant Three: 13
]]
}
local boundByDestiny = {
[[
Bound by Destiny
Prismatic Jewel
Source: Drops from unique{Incarnation of Neglect} or unique{Incarnation of Fear} or unique{Incarnation of Dread}
Limited to: 1
Has Alt Variant: true
Has Alt Variant Two: true
Selected Variant: 1
Selected Alt Variant: 19
Selected Alt Variant Two: 37
]]
}
local abbreviateModId = function(string)
return (string:
gsub("Increased", "Inc"):
gsub("Reduced", "Red."):
gsub("Critical", "Crit"):
gsub("Physical", "Phys"):
gsub("Elemental", "Ele"):
gsub("Multiplier", "Mult"):
gsub("EnergyShield", "ES"))
end
-- adding a blank variant to account for changes made in 3.20.1
table.insert(voranasMarch, "Variant: None")
table.insert(watchersEye, "Variant: None")
for _, mod in ipairs(data.uniqueMods["Watcher's Eye"]) do
if not (mod.Id:match("^SublimeVision") or mod.Id:match("^SummonArbalist")) then
local variantName = abbreviateModId(mod.Id):gsub("^[Purity Of ]*%u%l+", "%1:"):gsub("New", ""):gsub("[%u%d]", " %1"):gsub("_", ""):gsub("E S", "ES")
if watchersEyeLegacyMods[mod.Id] then
if watchersEyeLegacyMods[mod.Id].version then
table.insert(watchersEye, "Variant:" .. variantName .. " (Pre " .. watchersEyeLegacyMods[mod.Id].version .. ")")
end
if watchersEyeLegacyMods[mod.Id].legacyMod then
table.insert(watchersEye, "Variant:" .. variantName)
end
if watchersEyeLegacyMods[mod.Id].rename then
table.insert(watchersEye, "Variant: " .. watchersEyeLegacyMods[mod.Id].rename)
end
else
table.insert(watchersEye, "Variant:" .. variantName)
end
elseif not mod.Id:match("^SummonArbalist") then
local variantName = mod.Id:gsub("SublimeVision", ""):gsub("[%u%d]", " %1")
table.insert(sublimeVision, "Variant:" .. variantName)
else
local variantName = abbreviateModId(mod.Id):gsub("SummonArbalist", ""):gsub("[%u%d]", " %1"):gsub("_", ""):gsub("Percent To ", ""):gsub("Chance To ", ""):gsub("Targets To ", ""):gsub("[fF]or 4 ?[Ss]econds On Hit", ""):gsub(" Percent", ""):gsub("Number Of ", "")
table.insert(voranasMarch, "Variant:" .. variantName)
end
end
local unsortedMods = LoadModule("Data/Uniques/Special/BoundByDestiny")
local sortedMods = { }
local boundByDestinyMods = { }
for id, mod in pairs(unsortedMods) do
table.insert(sortedMods, { mod.type, id } )
end
table.sort(sortedMods, function (m1, m2)
if m1[1] == m2[1] then
return m1[2] < m2[2]
end
return m1[1] < m2[1]
end )
for _, modId in ipairs(sortedMods) do
table.insert(boundByDestinyMods, {
Id = modId[2],
mod = unsortedMods[modId[2]],
})
end
for _, mod in ipairs(boundByDestinyMods) do
local variantName = mod.mod.type:gsub("(%d+)(%a+)", "%2 %1:")..abbreviateModId(mod.Id):gsub(mod.mod.type .. ".*", ""):gsub("New", ""):gsub("[%u%d]", " %1"):gsub("_", ""):gsub("E S", "ES"):gsub("Velocity", "Speed"):gsub("Permyriad", "")
table.insert(boundByDestiny, "Variant: " .. variantName)
end
for i, mod in ipairs(boundByDestinyMods) do
for j, _ in ipairs(mod.mod) do
table.insert(boundByDestiny, "{variant:" .. i .. "}" .. mod.mod[j])
end
end
table.insert(watchersEye,
[[Limited to: 1
(4-6)% increased maximum Energy Shield
(4-6)% increased maximum Life
(4-6)% increased maximum Mana]])
table.insert(voranasMarch,
[[Requires Level 69, 46 Str, 46 Dex, 46 Int
Has no Sockets
Triggers Level 20 Summon Arbalists when Equipped
25% increased Movement Speed]])
local indexWatchersEye = 2
local indexSublimeVision = 1
local indexVoranasMarch = 2
for _, mod in ipairs(data.uniqueMods["Watcher's Eye"]) do
if not (mod.Id:match("^SublimeVision") or mod.Id:match("^SummonArbalist")) then
if watchersEyeLegacyMods[mod.Id] then
if watchersEyeLegacyMods[mod.Id].legacyMod then
for i, _ in ipairs(mod.mod) do
table.insert(watchersEye, "{variant:" .. indexWatchersEye .. "}" .. watchersEyeLegacyMods[mod.Id].legacyMod(mod.mod[i]))
end
indexWatchersEye = indexWatchersEye + 1
end
if watchersEyeLegacyMods[mod.Id].version or watchersEyeLegacyMods[mod.Id].rename then
for i, _ in ipairs(mod.mod) do
table.insert(watchersEye, "{variant:" .. indexWatchersEye .. "}" .. mod.mod[i])
end
indexWatchersEye = indexWatchersEye + 1
end
else
for i, _ in ipairs(mod.mod) do
table.insert(watchersEye, "{variant:" .. indexWatchersEye .. "}" .. mod.mod[i])
end
indexWatchersEye = indexWatchersEye + 1
end
elseif not mod.Id:match("^SummonArbalist") then
for i, _ in ipairs(mod.mod) do
table.insert(sublimeVision, "{variant:" .. indexSublimeVision .. "}" .. mod.mod[i])
end
indexSublimeVision = indexSublimeVision + 1
else
for i, _ in ipairs(mod.mod) do
table.insert(voranasMarch, "{variant:" .. indexVoranasMarch .. "}" .. mod.mod[i])
end
indexVoranasMarch = indexVoranasMarch + 1
end
end
table.insert(data.uniques.generated, table.concat(watchersEye, "\n"))
table.insert(data.uniques.generated, table.concat(sublimeVision, "\n"))
table.insert(data.uniques.generated, table.concat(voranasMarch, "\n"))
table.insert(data.uniques.generated, table.concat(boundByDestiny, "\n"))
function buildTreeDependentUniques(tree)
buildForbidden(tree.classNotables)
buildKeystoneItems(tree.keystoneMap)
end
function buildForbidden(classNotables)
local forbidden = { }
for _, name in pairs({"Flame", "Flesh"}) do
forbidden[name] = { }
table.insert(forbidden[name], "Rarity: UNIQUE")
table.insert(forbidden[name], "Forbidden " .. name)
table.insert(forbidden[name], (name == "Flame" and "Crimson" or "Cobalt") .. " Jewel")
local index = 1
for className, notableTable in pairs(classNotables) do
if className ~= "alternate_ascendancies" then --Remove Affliction Ascendancy's
for _, notableName in ipairs(notableTable) do
table.insert(forbidden[name], "Variant: (" .. className .. ") " .. notableName)
index = index + 1
end
end
end
if name == "Flame" then
table.insert(forbidden[name], "Source: Drops from unique{The Searing Exarch}")
else
table.insert(forbidden[name], "Source: Drops from unique{The Eater of Worlds}")
end
table.insert(forbidden[name], "Limited to: 1")
table.insert(forbidden[name], "Item Level: 83")
index = 1
for className, notableTable in pairs(classNotables) do
if className ~= "alternate_ascendancies" then --Remove Affliction Ascendancy's
for _, notableName in ipairs(notableTable) do
table.insert(forbidden[name], "{variant:" .. index .. "}" .. "Requires Class " .. className)
table.insert(forbidden[name], "{variant:" .. index .. "}" .. "Allocates ".. notableName .. " if you have the matching modifier on Forbidden " .. (name == "Flame" and "Flesh" or "Flame"))
index = index + 1
end
end
end
table.insert(forbidden[name], "Corrupted")
end
table.insert(data.uniques.generated, table.concat(forbidden["Flame"], "\n"))
table.insert(data.uniques.generated, table.concat(forbidden["Flesh"], "\n"))
end
function buildKeystoneItems(keystoneMap)
local skinOfTheLords = {
"Skin of the Lords",
"Simple Robe",
"League: Breach",
"Source: Upgraded from unique{Skin of the Loyal} using currency{Blessing of Chayula}",
}
local excludedPassiveKeystones = {
"Chaos Inoculation", -- to prevent infinite loop
"Necromantic Aegis", -- to prevent infinite loop
}
-- Keystones added by jewels don't have a position set in the tree data
local isKeystoneNative = function(node) return node.isKeystone and not node.isBlighted and node.x ~= nil end
local skinOfTheLordsKeystones = {}
local seen = {}
for _, node in pairs(keystoneMap) do
if isKeystoneNative(node) and not isValueInArray(excludedPassiveKeystones, node.name) and not seen[node] then
table.insert(skinOfTheLordsKeystones, node.name)
seen[node] = true
end
end
table.sort(skinOfTheLordsKeystones)
for _, name in ipairs(skinOfTheLordsKeystones) do
table.insert(skinOfTheLords, "Variant: "..name)
end
table.insert(skinOfTheLords, "Implicits: 0")
table.insert(skinOfTheLords, "Sockets cannot be modified")
table.insert(skinOfTheLords, "+2 to Level of Socketed Gems")
table.insert(skinOfTheLords, "100% increased Global Defences")
table.insert(skinOfTheLords, "You can only Socket Corrupted Gems in this item")
for index, name in ipairs(skinOfTheLordsKeystones) do
table.insert(skinOfTheLords, "{variant:"..index.."}"..name)
end
table.insert(skinOfTheLords, "Corrupted")
table.insert(data.uniques.generated, table.concat(skinOfTheLords, "\n"))
local impossibleEscapeKeystones = {}
seen = {}
for _, node in pairs(keystoneMap) do
if isKeystoneNative(node) and not seen[node] then
table.insert(impossibleEscapeKeystones, node.name)
seen[node] = true
end
end
table.sort(impossibleEscapeKeystones)
local impossibleEscape = {
"Impossible Escape",
"Viridian Jewel",
"League: Sentinel",
"Source: Drops from unique{The Maven} (Uber)",
"Limited to: 1",
"Radius: Small"
}
for _, name in ipairs(impossibleEscapeKeystones) do
table.insert(impossibleEscape, "Variant: "..name)
end
table.insert(impossibleEscape, "Variant: Everything (QoL Test Variant)")
local variantCount = #impossibleEscapeKeystones + 1
for index, name in ipairs(impossibleEscapeKeystones) do
table.insert(impossibleEscape, "{variant:"..index..","..variantCount.."}Passives Skills in radius of "..name.." can be allocated without being connected to your tree")
end
table.insert(impossibleEscape, "Corrupted")
table.insert(data.uniques.generated, table.concat(impossibleEscape, "\n"))
end
-- That Which Was Taken
local thatWhichWasTaken = {
[[
Item Class: Jewels
Rarity: Unique
That Which Was Taken
Crimson Jewel
League: Affliction
Has Alt Variant: true
Has Alt Variant Two: true
Has Alt Variant Three: true
Selected Variant: 82
Selected Alt Variant: 104
Selected Alt Variant Two: 106
Selected Alt Variant Three: 125
Variant: None
]]
}
local unsortedCharmsMods = LoadModule("Data/ModJewelCharm")
local sortedCharmsMods = { }
for modId, mod in pairs(unsortedCharmsMods) do
if not modId:match("1$") then
table.insert(sortedCharmsMods, modId)
end
end
table.sort(sortedCharmsMods)
for _, modId in ipairs(sortedCharmsMods) do
local variantName = abbreviateModId(modId):gsub("AnimalCharm", ""):gsub("LIfe", "Life"):gsub("OnHIt", "OnHit"):gsub("2$", ""):gsub("New", ""):gsub("[%u]", " %1"):gsub("[%d]+", " %1"):gsub("_", ""):gsub("E S", "ES")
table.insert(thatWhichWasTaken, "Variant:"..variantName)
end
table.insert(thatWhichWasTaken,
[[Limited to: 1
Requirements:
Level: 48
Item Level: 86
]]
)
local indexCharmMod = 2
for _, modId in ipairs(sortedCharmsMods) do
local mod = unsortedCharmsMods[modId]
for i, _ in ipairs(mod) do
table.insert(thatWhichWasTaken, "{variant:" .. indexCharmMod .. "}" .. mod[i])
end
indexCharmMod = indexCharmMod + 1
end
table.insert(data.uniques.generated, table.concat(thatWhichWasTaken, "\n"))
local replicaDragonfangsFlightMods = {}
LoadModule("Modules/CalcTools")
for _, gem in pairs(data.gems) do
if not string.match(gem.grantedEffectId, "Alt[XY]$") and calcLib.gemIsType(gem, "active skill", false) and calcLib.gemIsType(gem, "non-vaal", false) then
replicaDragonfangsFlightMods[gem.name] = "+3 to Level of all "..gem.name.." Gems"
end
end
local replicaDragonfangsFlight = {
[[Replica Dragonfang's Flight
Onyx Amulet
Selected Variant: 2
Has Alt Variant: true
Selected Alt Variant: 3
LevelReq: 56
]]
}
table.insert(replicaDragonfangsFlight,
[[Variant: Pre 3.23.0
Variant: Current
]]
)
for name, _ in pairs(replicaDragonfangsFlightMods) do
table.insert(replicaDragonfangsFlight, "Variant: "..name)
end
table.insert(replicaDragonfangsFlight,
[[Implicits: 1
{tags:jewellery_attribute}+(10-16) to all Attributes
{tags:jewellery_resistance}{variant:1}+(10-15)% to all Elemental Resistances
{tags:jewellery_resistance}{variant:2}+(5-10)% to all Elemental Resistances
]]
)
local index = 3
for _, line in pairs(replicaDragonfangsFlightMods) do
table.insert(replicaDragonfangsFlight, "{variant:"..index.."}"..line)
index = index + 1
end
table.insert(replicaDragonfangsFlight,
[[
{variant:1}(10-15)% increased Reservation Efficiency of Skills
{variant:2}(5-10)% increased Reservation Efficiency of Skills
{variant:1}Items and Gems have (10-15)% reduced Attribute Requirements
{variant:2}Items and Gems have (5-10)% reduced Attribute Requirements
]]
)
table.insert(data.uniques.generated, table.concat(replicaDragonfangsFlight, "\n"))