From 4ec4ecf8d7ce5754b21dfcf8a61630d7963e3809 Mon Sep 17 00:00:00 2001 From: EtherealCarnivore <42915554+EtherealCarnivore@users.noreply.github.com> Date: Mon, 2 Mar 2026 22:02:36 +0200 Subject: [PATCH] Add Essence of Desolation with ring slot support 3.28 essence with slot-specific ring mods (left ring: Unleash seals, right ring: Shockwave cooldown). Adds mod definitions, parser patterns, and calc wiring for most of its mods across all item types. Extends FindModifierSubstring to check GGPK modTags for defence modifier detection on the body armour mod. --- src/Classes/Item.lua | 8 ++++++++ src/Classes/ItemsTab.lua | 31 ++++++++++++++++++++++------- src/Data/Essence.lua | 1 + src/Data/ModItem.lua | 12 ++++++++++++ src/Modules/CalcPerform.lua | 11 +++++++++++ src/Modules/CalcSetup.lua | 11 +++++++++++ src/Modules/ConfigOptions.lua | 6 ++++++ src/Modules/Data.lua | 15 ++++++++++++++ src/Modules/ModParser.lua | 37 +++++++++++++++++++++++++++++++++++ 9 files changed, 125 insertions(+), 7 deletions(-) diff --git a/src/Classes/Item.lua b/src/Classes/Item.lua index e9f7cbc75c..c9f8c0d6e2 100644 --- a/src/Classes/Item.lua +++ b/src/Classes/Item.lua @@ -277,6 +277,14 @@ function ItemClass:FindModifierSubstring(substring, itemSlotName) end end end + -- Also check GGPK-exported mod tags (e.g. "defences" tag catches mods that don't mention defence in text) + if v.modTags then + for _, tag in ipairs(v.modTags) do + if tag:lower():find(substring) then + return true + end + end + end end end return false diff --git a/src/Classes/ItemsTab.lua b/src/Classes/ItemsTab.lua index 9a8162b911..0224d2f489 100644 --- a/src/Classes/ItemsTab.lua +++ b/src/Classes/ItemsTab.lua @@ -2670,13 +2670,30 @@ function ItemsTabClass:AddCustomModifierToDisplayItem() elseif sourceId == "ESSENCE" then for _, essence in pairs(self.build.data.essences) do local modId = essence.mods[self.displayItem.type] - local mod = self.displayItem.affixes[modId] - t_insert(modList, { - label = essence.name .. " " .. "^8[" .. table.concat(mod, "/") .. "]" .. " (" .. mod.type .. ")", - mod = mod, - type = "custom", - essence = essence, - }) + if modId then + local mod = self.displayItem.affixes[modId] + t_insert(modList, { + label = essence.name .. " " .. "^8[" .. table.concat(mod, "/") .. "]" .. " (" .. mod.type .. ")", + mod = mod, + type = "custom", + essence = essence, + }) + end + -- Some essences (Desolation) have different mods per ring slot + if self.displayItem.type == "Ring" then + for _, slotKey in ipairs({"Ring 1", "Ring 2"}) do + local slotModId = essence.mods[slotKey] + if slotModId then + local mod = self.displayItem.affixes[slotModId] + t_insert(modList, { + label = essence.name .. " (" .. slotKey .. ") " .. "^8[" .. table.concat(mod, "/") .. "]" .. " (" .. mod.type .. ")", + mod = mod, + type = "custom", + essence = essence, + }) + end + end + end end table.sort(modList, function(a, b) if a.essence.type ~= b.essence.type then diff --git a/src/Data/Essence.lua b/src/Data/Essence.lua index 733cf5d6dc..5a64ade346 100644 --- a/src/Data/Essence.lua +++ b/src/Data/Essence.lua @@ -106,4 +106,5 @@ return { ["Metadata/Items/Currency/CurrencyEssenceInsanity1"] = { name = "Essence of Insanity", type = 21, tier = 8, mods = { ["Amulet"] = "ChanceToRecoverManaOnSkillUseEssence1", ["Belt"] = "MovementVelocityDuringFlaskEffectEssence1", ["Body Armour"] = "OnslaughtWhenHitNewEssence1", ["Boots"] = "ManaRegenerationWhileShockedEssence1", ["Bow"] = "SpiritMinionEssence1", ["Claw"] = "SpiritMinionEssence1", ["Dagger"] = "SpiritMinionEssence1", ["Gloves"] = "SocketedGemsHaveMoreAttackAndCastSpeedEssenceNew1", ["Helmet"] = "SocketedGemsAddPercentageOfPhysicalAsLightningEssence1", ["One Handed Axe"] = "SpiritMinionEssence1", ["One Handed Mace"] = "SpiritMinionEssence1", ["One Handed Sword"] = "SpiritMinionEssence1", ["Quiver"] = "AdditionalPierceEssence7", ["Ring"] = "ReflectDamageTakenEssence1", ["Sceptre"] = "SpiritMinionEssence1", ["Shield"] = "PowerChargeOnBlockEssence1", ["Staff"] = "SpiritMinionEssence1", ["Thrusting One Handed Sword"] = "SpiritMinionEssence1", ["Two Handed Axe"] = "SpiritMinionEssence1", ["Two Handed Mace"] = "SpiritMinionEssence1", ["Two Handed Sword"] = "SpiritMinionEssence1", ["Wand"] = "SpiritMinionEssence1", }, }, ["Metadata/Items/Currency/CurrencyEssenceHorror1"] = { name = "Essence of Horror", type = 22, tier = 8, mods = { ["Amulet"] = "CrushOnHitChanceEssence1", ["Belt"] = "AlchemistsGeniusOnFlaskEssence1_", ["Body Armour"] = "ReducedDamageFromCriticalStrikesPerEnduranceChargeEssence1", ["Boots"] = "ElementalDamageTakenWhileStationaryEssence1", ["Bow"] = "PowerFrenzyOrEnduranceChargeOnKillEssence1", ["Claw"] = "PowerFrenzyOrEnduranceChargeOnKillEssence1", ["Dagger"] = "PowerFrenzyOrEnduranceChargeOnKillEssence1", ["Gloves"] = "SocketedSkillsCriticalChanceEssence1", ["Helmet"] = "SocketedGemsDealMoreElementalDamageEssence1", ["One Handed Axe"] = "PowerFrenzyOrEnduranceChargeOnKillEssence1", ["One Handed Mace"] = "PowerFrenzyOrEnduranceChargeOnKillEssence1", ["One Handed Sword"] = "PowerFrenzyOrEnduranceChargeOnKillEssence1", ["Quiver"] = "AddedColdDamagePerFrenzyChargeEssenceQuiver1", ["Ring"] = "AddedColdDamagePerFrenzyChargeEssence1", ["Sceptre"] = "PowerFrenzyOrEnduranceChargeOnKillEssence1", ["Shield"] = "NearbyEnemiesChilledOnBlockEssence1", ["Staff"] = "PowerFrenzyOrEnduranceChargeOnKillEssence1", ["Thrusting One Handed Sword"] = "PowerFrenzyOrEnduranceChargeOnKillEssence1", ["Two Handed Axe"] = "PowerFrenzyOrEnduranceChargeOnKillEssence1", ["Two Handed Mace"] = "PowerFrenzyOrEnduranceChargeOnKillEssence1", ["Two Handed Sword"] = "PowerFrenzyOrEnduranceChargeOnKillEssence1", ["Wand"] = "PowerFrenzyOrEnduranceChargeOnKillEssence1", }, }, ["Metadata/Items/Currency/CurrencyEssenceDelirium1"] = { name = "Essence of Delirium", type = 23, tier = 8, mods = { ["Amulet"] = "SpellBlockAmuletEssence1", ["Belt"] = "ChaosResistanceWhileUsingFlaskEssence1", ["Body Armour"] = "ChaosDamageOverTimeTakenEssence1", ["Boots"] = "CannotBePoisonedEssence1", ["Bow"] = "DecayOnHitEssence1", ["Claw"] = "DecayOnHitEssence1", ["Dagger"] = "DecayOnHitEssence1", ["Gloves"] = "SupportDamageOverTimeEssence1", ["Helmet"] = "SocketedAuraGemLevelsEssence1", ["One Handed Axe"] = "DecayOnHitEssence1", ["One Handed Mace"] = "DecayOnHitEssence1", ["One Handed Sword"] = "DecayOnHitEssence1", ["Quiver"] = "MarkEffectEssence1", ["Ring"] = "GlobalDamageOverTimeMultiplierRingEssence1", ["Sceptre"] = "DecayOnHitEssence1", ["Shield"] = "SpellBlockOnLowLifeEssence1", ["Staff"] = "DecayOnHitEssence1", ["Thrusting One Handed Sword"] = "DecayOnHitEssence1", ["Two Handed Axe"] = "DecayOnHitEssence1", ["Two Handed Mace"] = "DecayOnHitEssence1", ["Two Handed Sword"] = "DecayOnHitEssence1", ["Wand"] = "DecayOnHitEssence1", }, }, + ["Metadata/Items/Currency/CurrencyEssenceDesolation1"] = { name = "Essence of Desolation", type = 24, tier = 8, mods = { ["Amulet"] = "AilmentDurationEssenceDesolation1", ["Belt"] = "MagicFlaskEffectEssenceDesolation1", ["Body Armour"] = "GlobalDefencesEssenceDesolation1", ["Boots"] = "MovementSpeedPerEnemyEssenceDesolation1", ["Gloves"] = "AttackCastSpeedPerEnemyEssenceDesolation1", ["Helmet"] = "SocketedGemLevelEssenceDesolation1", ["Quiver"] = "ProjectileChainCloseRangeEssenceDesolation1", ["Ring 1"] = "UnleashSealsEssenceDesolation1", ["Ring 2"] = "ShockwaveCooldownEssenceDesolation1", ["Shield"] = "ArmourAppliesToEleDamageEssenceDesolation1", ["Claw"] = "DoubleDamagePerAilmentEssenceDesolation1", ["Dagger"] = "DoubleDamagePerAilmentEssenceDesolation1", ["One Handed Axe"] = "DoubleDamagePerAilmentEssenceDesolation1", ["One Handed Mace"] = "DoubleDamagePerAilmentEssenceDesolation1", ["One Handed Sword"] = "DoubleDamagePerAilmentEssenceDesolation1", ["Thrusting One Handed Sword"] = "DoubleDamagePerAilmentEssenceDesolation1", ["Wand"] = "DoubleDamagePerAilmentEssenceDesolation1", ["Sceptre"] = "DoubleDamagePerAilmentEssenceDesolation1", ["Bow"] = "DoubleDamagePerAilmentEssenceDesolationTwoHand1", ["Staff"] = "DoubleDamagePerAilmentEssenceDesolationTwoHand1", ["Two Handed Axe"] = "DoubleDamagePerAilmentEssenceDesolationTwoHand1", ["Two Handed Mace"] = "DoubleDamagePerAilmentEssenceDesolationTwoHand1", ["Two Handed Sword"] = "DoubleDamagePerAilmentEssenceDesolationTwoHand1", }, }, } \ No newline at end of file diff --git a/src/Data/ModItem.lua b/src/Data/ModItem.lua index ed03273684..bcb76415c4 100644 --- a/src/Data/ModItem.lua +++ b/src/Data/ModItem.lua @@ -1934,6 +1934,18 @@ return { ["MovementSpeedOnBurningChilledShockedGroundEssence1"] = { type = "Suffix", affix = "of the Essence", "12% increased Movement speed while on Burning, Chilled or Shocked ground", statOrder = { 9245 }, level = 63, group = "MovementSpeedOnBurningChilledShockedGround", weightKey = { "default", }, weightVal = { 0 }, modTags = { "speed" }, }, ["ManaRegenerationWhileShockedEssence1"] = { type = "Suffix", affix = "of the Essence", "70% increased Mana Regeneration Rate while Shocked", statOrder = { 2419 }, level = 63, group = "ManaRegenerationWhileShocked", weightKey = { "default", }, weightVal = { 0 }, modTags = { "resource", "mana" }, }, ["ManaGainedOnBlockEssence1"] = { type = "Suffix", affix = "of the Essence", "Recover 5% of your maximum Mana when you Block", statOrder = { 8028 }, level = 63, group = "ManaGainedOnBlock", weightKey = { "default", }, weightVal = { 0 }, modTags = { "block", "resource", "mana" }, }, + ["DoubleDamagePerAilmentEssenceDesolation1"] = { type = "Suffix", affix = "of the Essence", "1% chance to deal Double Damage against Enemies for each type of Ailment you have inflicted on them", statOrder = { 9900 }, level = 63, group = "DoubleDamagePerAilmentEssence", weightKey = { "default", }, weightVal = { 0 }, modTags = { "damage" }, }, + ["DoubleDamagePerAilmentEssenceDesolationTwoHand1"] = { type = "Suffix", affix = "of the Essence", "2% chance to deal Double Damage against Enemies for each type of Ailment you have inflicted on them", statOrder = { 9900 }, level = 63, group = "DoubleDamagePerAilmentEssence", weightKey = { "default", }, weightVal = { 0 }, modTags = { "damage" }, }, + ["MovementSpeedPerEnemyEssenceDesolation1"] = { type = "Suffix", affix = "of the Essence", "5% increased Movement Speed for each nearby Enemy, up to a maximum of 50%", statOrder = { 9901 }, level = 63, group = "MovementSpeedPerEnemyEssence", weightKey = { "default", }, weightVal = { 0 }, modTags = { "speed" }, }, + ["GlobalDefencesEssenceDesolation1"] = { type = "Suffix", affix = "of the Essence", "(70-90)% increased Global Defences if there are no Defence Modifiers on other Equipped Items", statOrder = { 9902 }, level = 63, group = "GlobalDefencesEssence", weightKey = { "default", }, weightVal = { 0 }, modTags = { "defences" }, }, + ["SocketedGemLevelEssenceDesolation1"] = { type = "Suffix", affix = "of the Essence", "+6 to Level of Socketed Gems while there is a single Gem Socketed in this Item", statOrder = { 9903 }, level = 63, group = "SocketedGemLevelEssence", weightKey = { "default", }, weightVal = { 0 }, modTags = { "gem" }, }, + ["ArmourAppliesToEleDamageEssenceDesolation1"] = { type = "Suffix", affix = "of the Essence", "(2-4)% of Armour applies to Fire, Cold and Lightning Damage taken from Hits if you have Blocked Recently", statOrder = { 9904 }, level = 63, group = "ArmourAppliesToEleDamageEssence", weightKey = { "default", }, weightVal = { 0 }, modTags = { "armour", "defences", "elemental" }, }, + ["ProjectileChainCloseRangeEssenceDesolation1"] = { type = "Suffix", affix = "of the Essence", "Projectiles can Chain from any number of additional targets in Close Range", statOrder = { 9905 }, level = 63, group = "ProjectileChainCloseRangeEssence", weightKey = { "default", }, weightVal = { 0 }, modTags = { "attack" }, }, + ["AilmentDurationEssenceDesolation1"] = { type = "Suffix", affix = "of the Essence", "(40-60)% increased Duration of Ailments of types you haven't inflicted Recently", statOrder = { 9906 }, level = 63, group = "AilmentDurationEssence", weightKey = { "default", }, weightVal = { 0 }, modTags = { "ailment" }, }, + ["UnleashSealsEssenceDesolation1"] = { type = "Suffix", affix = "of the Essence", "Left ring slot: Skills supported by Unleash have +1 to maximum number of Seals", statOrder = { 9907 }, level = 63, group = "UnleashSealsEssence", weightKey = { "default", }, weightVal = { 0 }, modTags = { "caster" }, }, + ["ShockwaveCooldownEssenceDesolation1"] = { type = "Suffix", affix = "of the Essence", "Right ring slot: Shockwave has +1 to Cooldown Uses", statOrder = { 9908 }, level = 63, group = "ShockwaveCooldownEssence", weightKey = { "default", }, weightVal = { 0 }, modTags = { "attack" }, }, + ["AttackCastSpeedPerEnemyEssenceDesolation1"] = { type = "Suffix", affix = "of the Essence", "5% increased Attack and Cast Speed for each nearby Enemy, up to a maximum of 30%", statOrder = { 9910 }, level = 63, group = "AttackCastSpeedPerEnemyEssence", weightKey = { "default", }, weightVal = { 0 }, modTags = { "speed", "attack", "caster" }, }, + ["MagicFlaskEffectEssenceDesolation1"] = { type = "Suffix", affix = "of the Essence", "Equipped Magic Flasks have 30% increased effect on you if no Flasks are Adjacent to them", statOrder = { 9909 }, level = 63, group = "MagicFlaskEffectEssence", weightKey = { "default", }, weightVal = { 0 }, modTags = { "flask" }, }, ["BleedDuration1"] = { type = "Suffix", affix = "of Agony", "(8-12)% increased Bleeding Duration", statOrder = { 4893 }, level = 30, group = "BleedDuration", weightKey = { "bow", "sword", "axe", "mace", "default", }, weightVal = { 0, 0, 0, 0, 0 }, modTags = { "bleed", "physical", "attack", "ailment" }, }, ["BleedDuration2"] = { type = "Suffix", affix = "of Torment", "(13-18)% increased Bleeding Duration", statOrder = { 4893 }, level = 60, group = "BleedDuration", weightKey = { "bow", "sword", "axe", "mace", "default", }, weightVal = { 0, 0, 0, 0, 0 }, modTags = { "bleed", "physical", "attack", "ailment" }, }, ["ChanceToIgnite1"] = { type = "Suffix", affix = "of Ignition", "(18-24)% chance to Ignite", statOrder = { 1937 }, level = 15, group = "ChanceToIgnite", weightKey = { "sceptre", "wand", "default", }, weightVal = { 1000, 1000, 0 }, modTags = { "elemental", "fire", "ailment" }, }, diff --git a/src/Modules/CalcPerform.lua b/src/Modules/CalcPerform.lua index 05b2d161b5..fdf47d2f62 100644 --- a/src/Modules/CalcPerform.lua +++ b/src/Modules/CalcPerform.lua @@ -1434,6 +1434,7 @@ function calcs.perform(env, skipEHP) local effectInc = modDB:Sum("INC", {actor = "player"}, "FlaskEffect") local effectIncMagic = modDB:Sum("INC", {actor = "player"}, "MagicUtilityFlaskEffect") + local effectIncMagicNoAdjacent = modDB:Sum("INC", {actor = "player"}, "MagicFlaskEffect") local effectIncNonPlayer = modDB:Sum("INC", nil, "FlaskEffect") local effectIncMagicNonPlayer = modDB:Sum("INC", nil, "MagicUtilityFlaskEffect") local flasksApplyToMinion = env.minion and modDB:Flag(env.player.mainSkill.skillCfg, "FlasksApplyToMinion") @@ -1523,6 +1524,16 @@ function calcs.perform(env, skipEHP) flaskEffectInc = flaskEffectInc + effectIncMagic flaskEffectIncNonPlayer = flaskEffectIncNonPlayer + effectIncMagicNonPlayer end + -- Essence of Desolation belt mod: bonus for magic flasks with no flask in an adjacent slot (1-5) + if item.rarity == "MAGIC" and effectIncMagicNoAdjacent ~= 0 then + local flaskSlotNum = env.flaskSlotMap and env.flaskSlotMap[item] + if flaskSlotNum then + local hasAdjacent = (env.flaskSlotOccupied[flaskSlotNum - 1] or env.flaskSlotOccupied[flaskSlotNum + 1]) + if not hasAdjacent then + flaskEffectInc = flaskEffectInc + effectIncMagicNoAdjacent + end + end + end local effectMod = 1 + (flaskEffectInc) / 100 local effectModNonPlayer = 1 + (flaskEffectIncNonPlayer) / 100 diff --git a/src/Modules/CalcSetup.lua b/src/Modules/CalcSetup.lua index e33109c645..422c111dfe 100644 --- a/src/Modules/CalcSetup.lua +++ b/src/Modules/CalcSetup.lua @@ -862,12 +862,20 @@ function calcs.initEnv(build, mode, override, specEnv) end end + -- Track which flask slot (1-5) each flask is in, for adjacency checks + env.flaskSlotMap = { } + env.flaskSlotOccupied = { } for _, slot in pairs(build.itemsTab.orderedSlots) do local slotName = slot.slotName local item = items[slotName] if item and item.type == "Flask" then if slot.active then env.flasks[item] = true + local flaskNum = tonumber(slotName:match("Flask (%d+)")) + if flaskNum then + env.flaskSlotMap[item] = flaskNum + env.flaskSlotOccupied[flaskNum] = true + end end if item.base.subType == "Life" then local highestLifeRecovery = env.itemModDB.multipliers["LifeFlaskRecovery"] or 0 @@ -1162,6 +1170,9 @@ function calcs.initEnv(build, mode, override, specEnv) end end env.itemModDB.multipliers["SocketedGemsIn"..slotName] = (env.itemModDB.multipliers["SocketedGemsIn"..slotName] or 0) + math.min(slotGemSocketsCount, socketedGems) + if socketedGems == 1 then + env.itemModDB.conditions["SingleGemSocketedIn"..slotName] = true -- for Essence of Desolation helmet mod + end env.itemModDB.multipliers.EmptyRedSocketsInAnySlot = (env.itemModDB.multipliers.EmptyRedSocketsInAnySlot or 0) + slotEmptySocketsCount.R env.itemModDB.multipliers.EmptyGreenSocketsInAnySlot = (env.itemModDB.multipliers.EmptyGreenSocketsInAnySlot or 0) + slotEmptySocketsCount.G env.itemModDB.multipliers.EmptyBlueSocketsInAnySlot = (env.itemModDB.multipliers.EmptyBlueSocketsInAnySlot or 0) + slotEmptySocketsCount.B diff --git a/src/Modules/ConfigOptions.lua b/src/Modules/ConfigOptions.lua index feac95a2b0..f6123b6314 100644 --- a/src/Modules/ConfigOptions.lua +++ b/src/Modules/ConfigOptions.lua @@ -1237,6 +1237,12 @@ Huge sets the radius to 11. { var = "multiplierPoisonAppliedRecently", type = "count", label = "# of Poisons applied Recently:", ifMult = "PoisonAppliedRecently", apply = function(val, modList, enemyModList) modList:NewMod("Multiplier:PoisonAppliedRecently", "BASE", val, "Config", { type = "Condition", var = "Combat" }) end }, + { var = "conditionCausedBleedingRecently", type = "check", label = "Have you caused ^xE05030Bleeding ^7Recently?", ifCond = "CausedBleedingRecently", apply = function(val, modList, enemyModList) + modList:NewMod("Condition:CausedBleedingRecently", "FLAG", true, "Config", { type = "Condition", var = "Combat" }) + end }, + { var = "conditionPoisonedEnemyRecently", type = "check", label = "Have you ^x60A060Poisoned ^7an enemy Recently?", ifCond = "PoisonedEnemyRecently", apply = function(val, modList, enemyModList) + modList:NewMod("Condition:PoisonedEnemyRecently", "FLAG", true, "Config", { type = "Condition", var = "Combat" }) + end }, { var = "multiplierLifeSpentRecently", type = "count", label = "# of ^xE05030Life ^7spent Recently:", ifMult = "LifeSpentRecently", apply = function(val, modList, enemyModList) modList:NewMod("Multiplier:LifeSpentRecently", "BASE", val, "Config", { type = "Condition", var = "Combat" }) end }, diff --git a/src/Modules/Data.lua b/src/Modules/Data.lua index be484e0c32..49eaa97e4a 100644 --- a/src/Modules/Data.lua +++ b/src/Modules/Data.lua @@ -646,6 +646,21 @@ data.itemTagSpecial = { "Cannot Evade", }, }, + -- Text patterns for ItemCondition "no Defence Modifiers"; also backed by modTags in FindModifierSubstring + ["defence"] = (function() + local defencePatterns = { + "[Aa]rmour", + "[Ee]vasion", + "[Ee]nergy [Ss]hield", + "[Ww]ard", + } + local slots = { "weapon 1", "weapon 2", "helmet", "body armour", "gloves", "boots", "amulet", "ring 1", "ring 2", "belt", "shield" } + local t = {} + for _, slot in ipairs(slots) do + t[slot] = defencePatterns + end + return t + end)(), } data.itemTagSpecialExclusionPattern = { ["life"] = { diff --git a/src/Modules/ModParser.lua b/src/Modules/ModParser.lua index fd91130b3e..dc81d3d9bd 100644 --- a/src/Modules/ModParser.lua +++ b/src/Modules/ModParser.lua @@ -5344,6 +5344,43 @@ local specialModList = { ["(%d+)%% more frozen legion and general's cry cooldown recovery rate"] = function(num) return { mod("CooldownRecovery", "MORE", num, { type = "SkillName", skillNameList = { "Frozen Legion", "General's Cry" }, includeTransfigured = true }) } end, ["flamethrower, seismic and lightning spire trap have (%d+)%% increased cooldown recovery rate"] = function(num) return { mod("CooldownRecovery", "INC", num, { type = "SkillName", skillNameList = { "Flamethrower Trap", "Seismic Trap", "Lightning Spire Trap" }, includeTransfigured = true }) } end, ["flamethrower, seismic and lightning spire trap have %-(%d+) cooldown uses?"] = function(num) return { mod("AdditionalCooldownUses", "BASE", -num, { type = "SkillName", skillNameList = { "Flamethrower Trap", "Seismic Trap", "Lightning Spire Trap" }, includeTransfigured = true }) } end, + ["shockwave has %+(%d+) to cooldown uses?"] = function(num) return { mod("AdditionalCooldownUses", "BASE", num, { type = "SkillName", skillName = "Shockwave", includeTransfigured = true }) } end, + -- Stacks per ailment type on enemy; one entry per ailment so they each contribute independently + ["(%d+)%% chance to deal double damage against enemies for each type of ailment you have inflicted on them"] = function(num) return { + mod("DoubleDamageChance", "BASE", num, { type = "ActorCondition", actor = "enemy", var = "Frozen" }), + mod("DoubleDamageChance", "BASE", num, { type = "ActorCondition", actor = "enemy", var = "Chilled" }), + mod("DoubleDamageChance", "BASE", num, { type = "ActorCondition", actor = "enemy", var = "Ignited" }), + mod("DoubleDamageChance", "BASE", num, { type = "ActorCondition", actor = "enemy", var = "Shocked" }), + mod("DoubleDamageChance", "BASE", num, { type = "ActorCondition", actor = "enemy", var = "Scorched" }), + mod("DoubleDamageChance", "BASE", num, { type = "ActorCondition", actor = "enemy", var = "Brittle" }), + mod("DoubleDamageChance", "BASE", num, { type = "ActorCondition", actor = "enemy", var = "Sapped" }), + mod("DoubleDamageChance", "BASE", num, { type = "ActorCondition", actor = "enemy", var = "Bleeding" }), + mod("DoubleDamageChance", "BASE", num, { type = "ActorCondition", actor = "enemy", var = "Poisoned" }), + } end, + ["(%d+)%% increased movement speed for each nearby enemy, up to a maximum of (%d+)%%"] = function(num, _, limit) return { mod("MovementSpeed", "INC", num, { type = "Multiplier", var = "NearbyEnemies", limit = tonumber(limit), limitTotal = true }) } end, + ["(%d+)%% increased attack and cast speed for each nearby enemy, up to a maximum of (%d+)%%"] = function(num, _, limit) return { mod("Speed", "INC", num, { type = "Multiplier", var = "NearbyEnemies", limit = tonumber(limit), limitTotal = true }) } end, + -- {SlotName} gets replaced with the actual slot (e.g. "Helmet") by Item.lua when the mod is applied + ["%+(%d+) to level of socketed gems while there is a single gem socketed in this item"] = function(num) return { + mod("GemProperty", "LIST", { keyword = "all", key = "level", value = num }, { type = "SocketedIn", slotName = "{SlotName}" }, { type = "Condition", var = "SingleGemSocketedIn{SlotName}" }), + } end, + ["(%d+)%% of armour applies to fire, cold and lightning damage taken from hits if you have blocked recently"] = function(num) return { + mod("ArmourAppliesToFireDamageTaken", "BASE", num, { type = "Condition", var = "BlockedRecently" }), + mod("ArmourAppliesToColdDamageTaken", "BASE", num, { type = "Condition", var = "BlockedRecently" }), + mod("ArmourAppliesToLightningDamageTaken", "BASE", num, { type = "Condition", var = "BlockedRecently" }), + } end, + -- "any number" modeled as +99; displays oddly in tooltip but calcs are correct + ["projectiles can chain from any number of additional targets in close range"] = { mod("ChainCountMax", "BASE", 99, nil, ModFlag.Projectile, { type = "Condition", var = "AtCloseRange" }) }, + -- Per-ailment duration; freeze/chill share a condition since they're both cold-based + ["(%d+)%% increased duration of ailments of types you haven't inflicted recently"] = function(num) return { + mod("EnemyFreezeDuration", "INC", num, { type = "Condition", var = "FrozenEnemyRecently", neg = true }), + mod("EnemyChillDuration", "INC", num, { type = "Condition", var = "FrozenEnemyRecently", neg = true }), + mod("EnemyIgniteDuration", "INC", num, { type = "Condition", var = "IgnitedEnemyRecently", neg = true }), + mod("EnemyShockDuration", "INC", num, { type = "Condition", var = "ShockedEnemyRecently", neg = true }), + mod("EnemyBleedDuration", "INC", num, { type = "Condition", var = "CausedBleedingRecently", neg = true }), + mod("EnemyPoisonDuration", "INC", num, { type = "Condition", var = "PoisonedEnemyRecently", neg = true }), + } end, + -- Adjacency checked in CalcPerform using flask slot positions (Flask 1-5) + ["equipped magic flasks have (%d+)%% increased effect on you if no flasks are adjacent to them"] = function(num) return { mod("MagicFlaskEffect", "INC", num, { type = "Condition", var = "NoAdjacentFlasks" }) } end, ["flameblast starts with (%d+) additional stages"] = function(num) return { mod("Multiplier:FlameblastMinimumStage", "BASE", num, 0, 0, { type = "GlobalEffect", effectType = "Buff", unscalable = true }) } end, ["incinerate starts with (%d+) additional stages"] = function(num) return { mod("Multiplier:IncinerateMinimumStage", "BASE", num, 0, 0, { type = "GlobalEffect", effectType = "Buff", unscalable = true }) } end, ["%+([%d%.]+) seconds to flameblast and incinerate cooldown"] = function(num) return {