From b157ce79b1c400aa480eabd8f885703c8ee7ecb0 Mon Sep 17 00:00:00 2001 From: majochem <77203255+majochem@users.noreply.github.com> Date: Mon, 8 Jun 2026 11:29:25 +0200 Subject: [PATCH 1/7] Add support for variations of ailment magnitude mods There were different wording variations for mods that affect ailment magnitude on players or enemies. Some of which were not recognized yet. Example: - "magnitude of ignite you inflict" was recognized - "magnitude of ignite on ... enemies" was not recognized --- src/Data/ModCache.lua | 4 ++-- src/Modules/ModParser.lua | 11 ++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Data/ModCache.lua b/src/Data/ModCache.lua index b846e2a142..0aa4d8497e 100644 --- a/src/Data/ModCache.lua +++ b/src/Data/ModCache.lua @@ -2636,7 +2636,7 @@ c["35% increased Elemental Ailment Threshold"]={{[1]={flags=0,keywordFlags=0,nam c["35% increased Flask Life Recovery rate"]={{[1]={flags=0,keywordFlags=0,name="FlaskLifeRecoveryRate",type="INC",value=35}},nil} c["35% increased Freeze Buildup"]={{[1]={flags=0,keywordFlags=0,name="EnemyFreezeBuildup",type="INC",value=35}},nil} c["35% increased Life and Mana Recovery from Flasks"]={{[1]={flags=0,keywordFlags=0,name="FlaskLifeRecovery",type="INC",value=35},[2]={flags=0,keywordFlags=0,name="FlaskManaRecovery",type="INC",value=35}},nil} -c["35% increased Magnitude of Ignite against Poisoned enemies"]={{}," Magnitude of Ignite "} +c["35% increased Magnitude of Ignite against Poisoned enemies"]={{[1]={[1]={actor="enemy",type="ActorCondition",var="Poisoned"},flags=0,keywordFlags=8388608,name="AilmentMagnitude",type="INC",value=35}},nil} c["35% increased Mana Regeneration Rate"]={{[1]={flags=0,keywordFlags=0,name="ManaRegen",type="INC",value=35}},nil} c["35% increased Mana Regeneration Rate while stationary"]={{[1]={[1]={type="Condition",var="Stationary"},flags=0,keywordFlags=0,name="ManaRegen",type="INC",value=35}},nil} c["35% increased Physical Damage"]={{[1]={flags=0,keywordFlags=0,name="PhysicalDamage",type="INC",value=35}},nil} @@ -3101,7 +3101,7 @@ c["50% reduced Evasion Rating"]={{[1]={flags=0,keywordFlags=0,name="Evasion",typ c["50% reduced Freeze Duration on you"]={{[1]={flags=0,keywordFlags=0,name="SelfFreezeDuration",type="INC",value=-50}},nil} c["50% reduced Ignite Duration on Enemies"]={{[1]={flags=0,keywordFlags=0,name="EnemyIgniteDuration",type="INC",value=-50}},nil} c["50% reduced Ignite Duration on you"]={{[1]={flags=0,keywordFlags=0,name="SelfIgniteDuration",type="INC",value=-50}},nil} -c["50% reduced Magnitude of Bleeding on You"]={{}," Magnitude of Bleeding on You "} +c["50% reduced Magnitude of Bleeding on You"]={{[1]={flags=0,keywordFlags=0,name="SelfBleedEffect",type="INC",value=-50}},nil} c["50% reduced Magnitude of Ignite on you"]={{[1]={flags=0,keywordFlags=0,name="SelfIgniteEffect",type="INC",value=-50}},nil} c["50% reduced Poison Duration on you"]={{[1]={flags=0,keywordFlags=0,name="SelfPoisonDuration",type="INC",value=-50}},nil} c["50% reduced Presence Area of Effect"]={{[1]={flags=0,keywordFlags=0,name="PresenceArea",type="INC",value=-50}},nil} diff --git a/src/Modules/ModParser.lua b/src/Modules/ModParser.lua index 4dc7443fdf..67f39bb24f 100644 --- a/src/Modules/ModParser.lua +++ b/src/Modules/ModParser.lua @@ -777,15 +777,23 @@ local modNameList = { ["chance to ignite"] = "EnemyIgniteChance", ["to freeze, shock and ignite"] = { "EnemyFreezeChance", "EnemyShockChance", "EnemyIgniteChance" }, ["flammability magnitude"] = "EnemyIgniteChance", + ["magnitude of shock"] = "EnemyShockMagnitude", ["magnitude of shock you inflict"] = "EnemyShockMagnitude", + ["magnitude of chill"] = "EnemyChillMagnitude", ["magnitude of chill you inflict"] = "EnemyChillMagnitude", ["magnitude of jagged ground you create"] = "EnemyJaggedGroundMagnitude", + ["magnitude of bleeding"] = { "AilmentMagnitude", keywordFlags = KeywordFlag.Bleed }, ["magnitude of bleeding you inflict"] = { "AilmentMagnitude", keywordFlags = KeywordFlag.Bleed }, - ["magnitude of ignite you inflict"] = { "AilmentMagnitude", keywordFlags = KeywordFlag.Ignite }, + ["magnitude of ignite"] = { "AilmentMagnitude", keywordFlags = KeywordFlag.Ignite }, + ["magnitude of ignite [you inflict]"] = { "AilmentMagnitude", keywordFlags = KeywordFlag.Ignite }, ["ignite magnitude"] = { "AilmentMagnitude", keywordFlags = KeywordFlag.Ignite }, ["bleed magnitude"] = { "AilmentMagnitude", keywordFlags = KeywordFlag.Bleed }, + ["magnitude of poison"] = { "AilmentMagnitude", keywordFlags = KeywordFlag.Poison }, + ["magnitude of poisons"] = { "AilmentMagnitude", keywordFlags = KeywordFlag.Poison }, ["magnitude of poison you inflict"] = { "AilmentMagnitude", keywordFlags = KeywordFlag.Poison }, + ["magnitude of poisons you inflict"] = { "AilmentMagnitude", keywordFlags = KeywordFlag.Poison }, ["effect of poison you inflict"] = { "AilmentEffect", keywordFlags = KeywordFlag.Poison }, + ["effect of poisons you inflict"] = { "AilmentEffect", keywordFlags = KeywordFlag.Poison }, ["magnitude of ailments"] = "AilmentMagnitude", ["magnitude of ailments you inflict"] = "AilmentMagnitude", ["magnitude of damaging ailments you inflict"] = { "AilmentMagnitude", keywordFlags = bor(KeywordFlag.Poison, KeywordFlag.Bleed, KeywordFlag.Ignite) }, @@ -850,6 +858,7 @@ local modNameList = { ["bleed duration on you"] = "SelfBleedDuration", ["bleeding duration on you"] = "SelfBleedDuration", ["duration of bleeding on you"] = "SelfBleedDuration", + ["magnitude of bleeding on you"] = "SelfBleedEffect", ["to blind enemies on hit"] = { "BlindChance" }, ["curse magnitudes"] = { "CurseEffect" }, ["aura magnitudes"] = { "AuraEffect" }, From a7ba5fdc69a3488526a75b75b43941c26afadcd9 Mon Sep 17 00:00:00 2001 From: majochem <77203255+majochem@users.noreply.github.com> Date: Mon, 8 Jun 2026 11:47:05 +0200 Subject: [PATCH 2/7] Add `SumNegativeValues()` to `ModStore` Equivalent to `SumPositiveValues()`, but for negative values instead. In this case usefuly to calculate whether the enemy is "slowed" --- src/Classes/ModStore.lua | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/Classes/ModStore.lua b/src/Classes/ModStore.lua index 6e084a3609..b1e496349e 100644 --- a/src/Classes/ModStore.lua +++ b/src/Classes/ModStore.lua @@ -176,6 +176,23 @@ function ModStoreClass:SumPositiveValues(modType, cfg, modName, ...) return total end +--- Returns the value of all negative modifiers to a mod added together, ignoring any negative modifiers. +--- Works by creating a table using Tabulate and then filtering for negative values. +--- +--- @param modType string # the mod type for which we want to create the table, e.g. "INC" or "MORE" +--- @param cfg table | nil # passed configuration, may be nil +--- @param modName string # the name of the mod for which we want to create the table, e.g. "FlaskRecoveryRate", "ActionSpeed", ... +function ModStoreClass:SumNegativeValues(modType, cfg, modName, ...) + local total = 0 + local modTable = self:Tabulate(modType, cfg, modName) + for i = 1, #modTable do + if modTable[i].value < 0 then + total = total + modTable[i].value + end + end + return total +end + function ModStoreClass:More(cfg, ...) local flags, keywordFlags = 0, 0 local source From b6d7ff338ea82cd602530af11ee376fdaedc76d0 Mon Sep 17 00:00:00 2001 From: majochem <77203255+majochem@users.noreply.github.com> Date: Mon, 8 Jun 2026 12:46:00 +0200 Subject: [PATCH 3/7] Add parsing for "against slowed enemies" --- src/Data/ModCache.lua | 2 +- src/Modules/ModParser.lua | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Data/ModCache.lua b/src/Data/ModCache.lua index 0aa4d8497e..39e88f8e7b 100644 --- a/src/Data/ModCache.lua +++ b/src/Data/ModCache.lua @@ -2757,7 +2757,7 @@ c["40% increased Damage with Bow Skills"]={{[1]={flags=0,keywordFlags=1024,name= c["40% increased Damage with Hits against Ignited Enemies"]={{[1]={[1]={actor="enemy",type="ActorCondition",var="Ignited"},flags=0,keywordFlags=262144,name="Damage",type="INC",value=40}},nil} c["40% increased Damage with Two Handed Weapons"]={{[1]={flags=34359738372,keywordFlags=0,name="Damage",type="INC",value=40}},nil} c["40% increased Damage with Warcries"]={{[1]={flags=0,keywordFlags=4,name="Damage",type="INC",value=40}},nil} -c["40% increased Duration of Poisons you inflict against Slowed Enemies"]={{[1]={flags=0,keywordFlags=0,name="EnemyPoisonDuration",type="INC",value=40}}," against Slowed Enemies "} +c["40% increased Duration of Poisons you inflict against Slowed Enemies"]={{[1]={[1]={actor="enemy",type="ActorCondition",var="Slowed"},flags=0,keywordFlags=0,name="EnemyPoisonDuration",type="INC",value=40}},nil} c["40% increased Electrocute Buildup"]={{[1]={flags=0,keywordFlags=0,name="EnemyElectrocuteBuildup",type="INC",value=40}},nil} c["40% increased Elemental Ailment Application if you have Shapeshifted to an Animal form Recently"]={{}," Elemental Ailment Application "} c["40% increased Elemental Ailment Threshold"]={{[1]={flags=0,keywordFlags=0,name="AilmentThreshold",type="INC",value=40}},nil} diff --git a/src/Modules/ModParser.lua b/src/Modules/ModParser.lua index 67f39bb24f..3203ae7869 100644 --- a/src/Modules/ModParser.lua +++ b/src/Modules/ModParser.lua @@ -2138,6 +2138,7 @@ local modTagList = { ["by s?l?a?i?n? ?shocked enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Shocked" } }, ["against enemies further than (%d+)m"] = function(num) return { tag = { type = "MultiplierThreshold", var = "enemyDistance", threshold = num * 10 } } end, ["against enemies within (%d+) ?me?t?r?e?s?"] = function(num) return { tag = { type = "MultiplierThreshold", var = "enemyDistance", threshold = num * 10, upper = true } } end, + ["against slowed enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Slowed" } }, -- Enemy multipliers ["per freeze, shock [ao][nr]d? ignite on enemy"] = { tag = { type = "Multiplier", var = "FreezeShockIgniteOnEnemy" } }, ["per (%d+)%% chill magnitude on enemy"] = function(num) return { tag = { type = "Multiplier", var = "ChillEffect", actor = "enemy", div = num } } end, From dbec6568d59371ec5a59763583c7f493ea5419ca Mon Sep 17 00:00:00 2001 From: majochem <77203255+majochem@users.noreply.github.com> Date: Mon, 8 Jun 2026 12:46:37 +0200 Subject: [PATCH 4/7] Add processing for "Condition:Slowed" NOTE: I am not 100% sure that this is the perfect place for this processing to go, but it needed to be somewhere after all the other debuffs like chill, maim, curses, etc. have been processed --- src/Modules/CalcPerform.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Modules/CalcPerform.lua b/src/Modules/CalcPerform.lua index 8dcf95a33d..30c16a7c69 100644 --- a/src/Modules/CalcPerform.lua +++ b/src/Modules/CalcPerform.lua @@ -760,6 +760,16 @@ local function doActorMisc(env, actor) -- Process enemy modifiers applyEnemyModifiers(actor) + + -- Check enemy "Slowed" condition if not already in effect + if actor ~= env.player and actor ~= env.minion and (not modDB:Flag(nil, "UnaffectedBySlows")) then + local slowEffect = modDB:SumNegativeValues("INC", nil, "ActionSpeed") or 0 + slowEffect = slowEffect + modDB:SumNegativeValues("INC", nil, "MovementSpeed") or 0 + slowEffect = slowEffect + modDB:SumNegativeValues("INC", nil, "TemporalChainsActionSpeed") or 0 + if slowEffect ~= 0 then + modDB:NewMod("Condition:Slowed", "FLAG", true) + end + end end -- Process charges @@ -2392,6 +2402,8 @@ function calcs.perform(env, skipEHP) if activeSkill.skillModList:Flag(nil, "CanParry") then modDB:NewMod("CanParry", "FLAG", true) end + -- Handle `slowed` condition + -- todo remove --Handle combustion if enemyDB:Flag(nil, "Condition:Ignited") and (activeSkill.skillTypes[SkillType.Damage] or activeSkill.skillTypes[SkillType.Attack]) and not appliedCombustion then for _, support in ipairs(activeSkill.supportList) do From 3734e313f45861879d26a824a3ea3157f285a4c8 Mon Sep 17 00:00:00 2001 From: majochem <77203255+majochem@users.noreply.github.com> Date: Mon, 8 Jun 2026 14:34:19 +0200 Subject: [PATCH 5/7] Add generic parsing for "hindered" & "maimed" Added to `flagTypes` to enable parsing of nearby enemies are ... type of mods for those conditions --- src/Modules/CalcPerform.lua | 5 ++--- src/Modules/ModParser.lua | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Modules/CalcPerform.lua b/src/Modules/CalcPerform.lua index 30c16a7c69..a03ebe8e3d 100644 --- a/src/Modules/CalcPerform.lua +++ b/src/Modules/CalcPerform.lua @@ -761,7 +761,8 @@ local function doActorMisc(env, actor) -- Process enemy modifiers applyEnemyModifiers(actor) - -- Check enemy "Slowed" condition if not already in effect + -- Check enemy "Slowed" conditions + -- NOTE: check is done based on negative speed modifiers as the list of possible slowing effects is very large if actor ~= env.player and actor ~= env.minion and (not modDB:Flag(nil, "UnaffectedBySlows")) then local slowEffect = modDB:SumNegativeValues("INC", nil, "ActionSpeed") or 0 slowEffect = slowEffect + modDB:SumNegativeValues("INC", nil, "MovementSpeed") or 0 @@ -2402,8 +2403,6 @@ function calcs.perform(env, skipEHP) if activeSkill.skillModList:Flag(nil, "CanParry") then modDB:NewMod("CanParry", "FLAG", true) end - -- Handle `slowed` condition - -- todo remove --Handle combustion if enemyDB:Flag(nil, "Condition:Ignited") and (activeSkill.skillTypes[SkillType.Damage] or activeSkill.skillTypes[SkillType.Attack]) and not appliedCombustion then for _, support in ipairs(activeSkill.supportList) do diff --git a/src/Modules/ModParser.lua b/src/Modules/ModParser.lua index 3203ae7869..d71b2fef8b 100644 --- a/src/Modules/ModParser.lua +++ b/src/Modules/ModParser.lua @@ -6314,7 +6314,9 @@ local flagTypes = { ["no life regeneration"] = "NoLifeRegen", ["no inherent mana regeneration"] = "Condition:NoInherentManaRegen", ["hexproof"] = { name = "CurseEffectOnSelf", value = -100, type = "MORE" }, + ["maimed"] = "Condition:Maimed", ["hindered,? with (%d+)%% reduced movement speed"] = "Condition:Hindered", + ["hindered"] = "Condition:Hindered", ["unnerved"] = "Condition:Unnerved", ["malediction"] = "HasMalediction", } From e8ab6d3f2e1f96a63d81d1db8fe4d1c5cb842323 Mon Sep 17 00:00:00 2001 From: majochem <77203255+majochem@users.noreply.github.com> Date: Mon, 8 Jun 2026 15:15:47 +0200 Subject: [PATCH 6/7] Add base movement speed penalty for "Hindered" This was only done for "Maimed" before --- src/Modules/CalcSetup.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Modules/CalcSetup.lua b/src/Modules/CalcSetup.lua index 02c55fe4dc..2206533fc0 100644 --- a/src/Modules/CalcSetup.lua +++ b/src/Modules/CalcSetup.lua @@ -61,6 +61,7 @@ function calcs.initModDB(env, modDB) modDB:NewMod("ScorchStacksMax", "BASE", 1, "Base") modDB:NewMod("MovementSpeed", "INC", -30, "Base", { type = "Condition", var = "Maimed" }) modDB:NewMod("Evasion", "INC", -15, "Base", { type = "Condition", var = "Maimed" }) + modDB:NewMod("MovementSpeed", "INC", -30, "Base", { type = "Condition", var = "Hindered" }) modDB:NewMod("AilmentThreshold", "BASE", 50, "Base", { type = "PercentStat", stat = "Life", percent = 1 }) modDB:NewMod("PoiseThreshold", "BASE", 50, "Base", { type = "PercentStat", stat = "Life", percent = 1 }) modDB:NewMod("DamageTaken", "INC", 10, "Base", { type = "Condition", var = "Intimidated"}) From e5b67d488542cd56e5f5bd7dcceaecb9ffa376b8 Mon Sep 17 00:00:00 2001 From: majochem <77203255+majochem@users.noreply.github.com> Date: Mon, 8 Jun 2026 15:19:07 +0200 Subject: [PATCH 7/7] Add automated test for "Condition:Slowed" check --- spec/System/TestDebuffs_spec.lua | 71 ++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 spec/System/TestDebuffs_spec.lua diff --git a/spec/System/TestDebuffs_spec.lua b/spec/System/TestDebuffs_spec.lua new file mode 100644 index 0000000000..887f06b2f2 --- /dev/null +++ b/spec/System/TestDebuffs_spec.lua @@ -0,0 +1,71 @@ +describe("TestAilments", function() + before_each(function() + newBuild() + end) + + teardown(function() + -- newBuild() takes care of resetting everything in setup() + end) + + it("correctly applies effects dependent on 'Condition:Slowed'", function() + build.skillsTab:PasteSocketGroup("Chaos Bolt 1/0 1\n") + runCallback("OnFrame") + + local defaultDmg = build.calcsTab.mainOutput.TotalDPS + assert.True(defaultDmg > 0, "build should deal damage") + + build.configTab.input.customMods = "100% increased damage against slowed enemies" + build.configTab:BuildModList() + runCallback("OnFrame") + + -- no effect yet + local nonSlowedDmg = build.calcsTab.mainOutput.TotalDPS + assert.are.equals(nonSlowedDmg, defaultDmg, "damage should be unchanged until enemy is slowed") + + -- action speed + build.configTab.input.customMods = [[ + 100% increased damage against slowed enemies + Nearby enemies have 10% reduced action speed + ]] + + build.configTab:BuildModList() + runCallback("OnFrame") + local actionSlowedDmg = build.calcsTab.mainOutput.TotalDPS + assert.True(actionSlowedDmg > nonSlowedDmg, "damage should be higher vs. reduced action speed") + + -- movement speed + build.configTab.input.customMods = [[ + 100% increased damage against slowed enemies + Nearby enemies have 10% reduced movement speed + ]] + + build.configTab:BuildModList() + runCallback("OnFrame") + local movementSlowedDmg = build.calcsTab.mainOutput.TotalDPS + assert.True(movementSlowedDmg > nonSlowedDmg, "damage should be higher vs. reduced movement speed") + + -- specific slowing debuffs checks + -- NOTE: there might be more conditions that should be checked here, feel free to add more + for _, debuff in ipairs({"chilled", "maimed", "hindered"}) do + build.configTab.input.customMods = [[ + 100% increased damage against slowed enemies + nearby enemies are ]] .. debuff .. [[ + ]] + + build.configTab:BuildModList() + runCallback("OnFrame") + local debuffSlowedDmg = build.calcsTab.mainOutput.TotalDPS + assert.True(debuffSlowedDmg > nonSlowedDmg, "damage should be higher vs. " .. debuff .. " enemies") + end + + -- temporal chains curse + build.configTab.input.customMods = [[ + 100% increased damage against slowed enemies + ]] + build.skillsTab:PasteSocketGroup("Temporal Chains 20/0 1\n") + build.configTab:BuildModList() + runCallback("OnFrame") + local temporalChainsSlowedDmg = build.calcsTab.mainOutput.TotalDPS + assert.True(temporalChainsSlowedDmg > nonSlowedDmg, "damage should be higher with Temporal Chains curse") + end) +end)