From c22bc5921061473958ec87297a40a86de0c234ba Mon Sep 17 00:00:00 2001 From: majochem <77203255+majochem@users.noreply.github.com> Date: Wed, 4 Mar 2026 11:11:10 +0100 Subject: [PATCH 01/10] Rename `doublePoisonChance` to `additionalPoisonChance` Renamed for clarity as it is not actually a "doubling" effect --- src/Data/ModCache.lua | 4 ++-- src/Modules/CalcOffence.lua | 6 +++--- src/Modules/ModParser.lua | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Data/ModCache.lua b/src/Data/ModCache.lua index db0c8df48f..9967fcd0a3 100755 --- a/src/Data/ModCache.lua +++ b/src/Data/ModCache.lua @@ -5034,7 +5034,7 @@ c["325% increased Energy Shield"]={{[1]={flags=0,keywordFlags=0,name="EnergyShie c["33% chance to Blind nearby Enemies when gaining Her Blessing"]={{}," to Blind nearby Enemies when gaining Her Blessing "} c["33% chance to Blind nearby Enemies when gaining Her Blessing 100% chance to Avoid being Ignited, Chilled or Frozen with Her Blessing"]={{[1]={flags=0,keywordFlags=0,name="AvoidIgnite",type="BASE",value=33}}," to Blind nearby Enemies when gaining Her Blessing 100% chance , Chilled or Frozen with Her Blessing "} c["33% chance to gain a Frenzy Charge on Kill"]={nil,"a Frenzy Charge "} -c["33% chance to inflict an additional Poison on the same Target when you inflict Poison"]={{[1]={flags=0,keywordFlags=0,name="DoublePoisonChance",type="BASE",value=33}},nil} +c["33% chance to inflict an additional Poison on the same Target when you inflict Poison"]={{[1]={flags=0,keywordFlags=0,name="AdditionalPoisonChance",type="BASE",value=33}},nil} c["33% increased Attack Damage against Bleeding Enemies"]={{[1]={[1]={actor="enemy",type="ActorCondition",var="Bleeding"},flags=1,keywordFlags=0,name="Damage",type="INC",value=33}},nil} c["33% increased Attack Speed while Ignited"]={{[1]={[1]={type="Condition",var="Ignited"},flags=1,keywordFlags=0,name="Speed",type="INC",value=33}},nil} c["33% increased Cast Speed"]={{[1]={flags=16,keywordFlags=0,name="Speed",type="INC",value=33}},nil} @@ -5240,7 +5240,7 @@ c["40% chance to Suppress Spell Damage while your Off Hand is empty"]={{[1]={[1] c["40% chance to cause Bleeding on Melee Hit"]={{[1]={flags=260,keywordFlags=0,name="BleedChance",type="BASE",value=40}},nil} c["40% chance to deal Double Damage while Focused"]={{[1]={[1]={type="Condition",var="Focused"},flags=0,keywordFlags=0,name="DoubleDamageChance",type="BASE",value=40}},nil} c["40% chance to gain a Frenzy Charge for each enemy you hit with a Critical Strike"]={nil,"a Frenzy Charge for each enemy you hit with a Critical Strike "} -c["40% chance to inflict an additional Poison on the same Target when you inflict Poison"]={{[1]={flags=0,keywordFlags=0,name="DoublePoisonChance",type="BASE",value=40}},nil} +c["40% chance to inflict an additional Poison on the same Target when you inflict Poison"]={{[1]={flags=0,keywordFlags=0,name="AdditionalPoisonChance",type="BASE",value=40}},nil} c["40% chance when you Kill a Scorched Enemy to Burn Each surrounding Enemy for 4 seconds, dealing 8% of the Killed Enemy's Life as Fire Damage per second"]={{[1]={flags=0,keywordFlags=0,name="Life",type="BASE",value=40}}," when you Kill a Scorched Enemy to Burn Each surrounding Enemy , dealing 8% of the Killed Enemy's as Fire Damage per second "} c["40% faster start of Energy Shield Recharge"]={{[1]={flags=0,keywordFlags=0,name="EnergyShieldRechargeFaster",type="INC",value=40}},nil} c["40% faster start of Energy Shield Recharge while affected by Discipline"]={{[1]={[1]={type="Condition",var="AffectedByDiscipline"},flags=0,keywordFlags=0,name="EnergyShieldRechargeFaster",type="INC",value=40}},nil} diff --git a/src/Modules/CalcOffence.lua b/src/Modules/CalcOffence.lua index c89a4200fd..4af11359c4 100644 --- a/src/Modules/CalcOffence.lua +++ b/src/Modules/CalcOffence.lua @@ -4363,9 +4363,9 @@ function calcs.offence(env, actor, activeSkill) globalOutput.PoisonDuration = durationBase * durationMod / rateMod * debuffDurationMult -- The chance any given hit applies poison local poisonChance = output.PoisonChanceOnHit / 100 * (1 - output.CritChance / 100) + output.PoisonChanceOnCrit / 100 * output.CritChance / 100 - local doublePoisonChance = 1 + m_min(skillModList:Sum("BASE", cfg, "DoublePoisonChance")/ 100, 1) + local additionalPoisonChance = 1 + m_min(skillModList:Sum("BASE", cfg, "AdditionalPoisonChance")/ 100, 1) -- The average number of poisons that will be active on the enemy at once - local PoisonStacks = output.HitChance / 100 * poisonChance * doublePoisonChance * skillData.dpsMultiplier * (skillData.stackMultiplier or 1) * quantityMultiplier + local PoisonStacks = output.HitChance / 100 * poisonChance * additionalPoisonChance * skillData.dpsMultiplier * (skillData.stackMultiplier or 1) * quantityMultiplier if (globalOutput.HitSpeed or globalOutput.Speed) > 0 then --assume skills with no cast, attack, or cooldown time are single cast PoisonStacks = PoisonStacks * globalOutput.PoisonDuration * (globalOutput.HitSpeed or globalOutput.Speed) @@ -4379,7 +4379,7 @@ function calcs.offence(env, actor, activeSkill) base = { "%.2fs ^8(poison duration)", globalOutput.PoisonDuration }, { "%.2f ^8(poison chance)", poisonChance }, { "%.2f ^8(hit chance)", output.HitChance / 100 }, - { "%.2f ^8(double poison chance)", doublePoisonChance }, + { "%.2f ^8(additional poison chance)", additionalPoisonChance }, { "%.2f ^8(hits per second)", globalOutput.HitSpeed or globalOutput.Speed }, { "%g ^8(dps multiplier for this skill)", skillData.dpsMultiplier or 1 }, { "%g ^8(stack multiplier for this skill)", skillData.stackMultiplier or 1 }, diff --git a/src/Modules/ModParser.lua b/src/Modules/ModParser.lua index fd91130b3e..99400fd417 100644 --- a/src/Modules/ModParser.lua +++ b/src/Modules/ModParser.lua @@ -3686,7 +3686,7 @@ local specialModList = { mod("Damage", "INC", num, nil, 0, KeywordFlag.Poison, { type = "Condition", var = "SinglePoison" }, { type = "SkillName", skillNameList = { "Sunder", "Ground Slam" }, includeTransfigured = true }) } end, ["poisons on you expire (%d+)%% slower"] = function(num) return { mod("SelfPoisonDebuffExpirationRate", "BASE", -num) } end, - ["(%d+)%% chance to inflict an additional poison on the same target when you inflict poison"] = function(num) return { mod("DoublePoisonChance", "BASE", num) } end, + ["(%d+)%% chance to inflict an additional poison on the same target when you inflict poison"] = function(num) return { mod("AdditionalPoisonChance", "BASE", num) } end, -- Suppression ["y?o?u?r? ?chance to suppress spell damage is lucky"] = { flag("SpellSuppressionChanceIsLucky") }, ["y?o?u?r? ?chance to suppress spell damage is unlucky"] = { flag("SpellSuppressionChanceIsUnlucky") }, From 5426f880535b76434054f6c4bdb055f38f61e53f Mon Sep 17 00:00:00 2001 From: majochem <77203255+majochem@users.noreply.github.com> Date: Wed, 4 Mar 2026 15:34:12 +0100 Subject: [PATCH 02/10] Add additional poisons and poison stack limits - parsing and logic for application of more than 1 additional poison per hit via "inflict X additional poisons on the same target ..." - parsing and logic for poison stack limit via "cannot poison enemies with at least X poisons on them" - adds parsing and logic for additional `CannotMultiplie` flag that will apply to Viper Strike of the Mamba as of 3.28 --- src/Modules/CalcOffence.lua | 29 +++++++++++++++++++++++++---- src/Modules/ModParser.lua | 3 +++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/Modules/CalcOffence.lua b/src/Modules/CalcOffence.lua index 4af11359c4..44d5b48647 100644 --- a/src/Modules/CalcOffence.lua +++ b/src/Modules/CalcOffence.lua @@ -4363,12 +4363,33 @@ function calcs.offence(env, actor, activeSkill) globalOutput.PoisonDuration = durationBase * durationMod / rateMod * debuffDurationMult -- The chance any given hit applies poison local poisonChance = output.PoisonChanceOnHit / 100 * (1 - output.CritChance / 100) + output.PoisonChanceOnCrit / 100 * output.CritChance / 100 - local additionalPoisonChance = 1 + m_min(skillModList:Sum("BASE", cfg, "AdditionalPoisonChance")/ 100, 1) - -- The average number of poisons that will be active on the enemy at once - local PoisonStacks = output.HitChance / 100 * poisonChance * additionalPoisonChance * skillData.dpsMultiplier * (skillData.stackMultiplier or 1) * quantityMultiplier + + -- Handling of "inflict x additional poisons" + local additionalPoisonStacks = 1 + if not skillModList:Flag(nil, "CannotMultiplePoison") then + additionalPoisonStacks = 1 + m_min(skillModList:Sum("BASE", cfg, "AdditionalPoisonChance")/ 100, 1) + (skillModList:Sum("BASE", cfg, "AdditionalPoisonStacks")) + end + + -- Calculate average number of poisons that will be active on the enemy at once + local poisonStackLimit = skillModList:Sum("BASE", cfg, "PoisonStackLimit") + local PoisonStacks = output.HitChance / 100 * poisonChance * additionalPoisonStacks * skillData.dpsMultiplier * (skillData.stackMultiplier or 1) * quantityMultiplier if (globalOutput.HitSpeed or globalOutput.Speed) > 0 then --assume skills with no cast, attack, or cooldown time are single cast PoisonStacks = PoisonStacks * globalOutput.PoisonDuration * (globalOutput.HitSpeed or globalOutput.Speed) + + -- If stack limit exists, avg. poison stack is more complicated + if poisonStackLimit > 0 and additionalPoisonStacks > 1 and PoisonStacks > poisonStackLimit then + -- Calc number of avg. poisons applied per hit (without hitrate multipliers) + local singleHitPoisonChance = output.HitChance / 100 * poisonChance + local singleHitPoisonStacks = singleHitPoisonChance * additionalPoisonStacks + + -- Calc how many hits will poison before limit is reached and theoretical max poison stacks, which is different from `poisonStackLimit` due to "additional" poison mechanics + local numPoisoningHits = m_ceil(poisonStackLimit / singleHitPoisonStacks) + local maxPoisonStacks = numPoisoningHits * singleHitPoisonStacks + + -- Only use `maxPoisonStacks` if original value exceeds it + PoisonStacks = m_min(PoisonStacks, maxPoisonStacks) + end end if PoisonStacks < 1 and (env.configInput.multiplierPoisonOnEnemy or 0) <= 1 then skillModList:NewMod("Condition:SinglePoison", "FLAG", true, "poison") @@ -4379,7 +4400,7 @@ function calcs.offence(env, actor, activeSkill) base = { "%.2fs ^8(poison duration)", globalOutput.PoisonDuration }, { "%.2f ^8(poison chance)", poisonChance }, { "%.2f ^8(hit chance)", output.HitChance / 100 }, - { "%.2f ^8(additional poison chance)", additionalPoisonChance }, + { "%.2f ^8(additional poisons inflicted)", additionalPoisonStacks }, { "%.2f ^8(hits per second)", globalOutput.HitSpeed or globalOutput.Speed }, { "%g ^8(dps multiplier for this skill)", skillData.dpsMultiplier or 1 }, { "%g ^8(stack multiplier for this skill)", skillData.stackMultiplier or 1 }, diff --git a/src/Modules/ModParser.lua b/src/Modules/ModParser.lua index 99400fd417..2815b7b9f7 100644 --- a/src/Modules/ModParser.lua +++ b/src/Modules/ModParser.lua @@ -3687,6 +3687,9 @@ local specialModList = { } end, ["poisons on you expire (%d+)%% slower"] = function(num) return { mod("SelfPoisonDebuffExpirationRate", "BASE", -num) } end, ["(%d+)%% chance to inflict an additional poison on the same target when you inflict poison"] = function(num) return { mod("AdditionalPoisonChance", "BASE", num) } end, + ["inflict (%d+) additional poisons? on the same target when you inflict poisons? with this weapon"] = function(num) return { mod("AdditionalPoisonStacks", "BASE", num, { type = "Condition", var = "{Hand}Attack" } ) } end, + ["cannot poison enemies with at least (%d+) poisons? on them"] = function(num) return { mod("PoisonStackLimit", "BASE", num ) } end, + ["cannot inflict multiple poisons in the same hit"] = { flag("CannotMultiplePoison") }, -- Suppression ["y?o?u?r? ?chance to suppress spell damage is lucky"] = { flag("SpellSuppressionChanceIsLucky") }, ["y?o?u?r? ?chance to suppress spell damage is unlucky"] = { flag("SpellSuppressionChanceIsUnlucky") }, From 9e06ba29704546a10bef17e83b4c129d4dfff63b Mon Sep 17 00:00:00 2001 From: majochem <77203255+majochem@users.noreply.github.com> Date: Thu, 5 Mar 2026 12:22:47 +0100 Subject: [PATCH 03/10] Adjust `PoisonStacks` breakdown --- src/Modules/CalcOffence.lua | 5 ++++- src/Modules/CalcSections.lua | 11 ++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Modules/CalcOffence.lua b/src/Modules/CalcOffence.lua index 44d5b48647..ccbbd4a84d 100644 --- a/src/Modules/CalcOffence.lua +++ b/src/Modules/CalcOffence.lua @@ -4400,7 +4400,7 @@ function calcs.offence(env, actor, activeSkill) base = { "%.2fs ^8(poison duration)", globalOutput.PoisonDuration }, { "%.2f ^8(poison chance)", poisonChance }, { "%.2f ^8(hit chance)", output.HitChance / 100 }, - { "%.2f ^8(additional poisons inflicted)", additionalPoisonStacks }, + { "%.2f ^8(avg. # of poisons inflicted)", additionalPoisonStacks }, { "%.2f ^8(hits per second)", globalOutput.HitSpeed or globalOutput.Speed }, { "%g ^8(dps multiplier for this skill)", skillData.dpsMultiplier or 1 }, { "%g ^8(stack multiplier for this skill)", skillData.stackMultiplier or 1 }, @@ -4410,6 +4410,9 @@ function calcs.offence(env, actor, activeSkill) if skillModList:Flag(nil, "Condition:SinglePoison") then t_insert(globalBreakdown.PoisonStacks, "Capped to 1") end + if poisonStackLimit > 0 and PoisonStacks >= poisonStackLimit then + t_insert(globalBreakdown.PoisonStacks, "^8(affected by poison stack limit)") + end end for sub_pass = 1, 2 do dotCfg.skillCond["CriticalStrike"] = sub_pass ~= 1 diff --git a/src/Modules/CalcSections.lua b/src/Modules/CalcSections.lua index 81341707b3..3fdc15ba94 100644 --- a/src/Modules/CalcSections.lua +++ b/src/Modules/CalcSections.lua @@ -916,7 +916,16 @@ return { { label = "Main Hand", flag = "weapon1Attack", modName = "PoisonChance", modType = "BASE", cfg = "weapon1" }, { label = "Off Hand", flag = "weapon2Attack", modName = "PoisonChance", modType = "BASE", cfg = "weapon2" }, }, }, - { label = "Poison Stacks", { format = "{2:output:PoisonStacks}", { breakdown = "PoisonStacks" } }}, + { label = "Poison Stacks", { format = "{2:output:PoisonStacks}", + { breakdown = "PoisonStacks" }, + { label = "% chance to inflict 1 additional poison", notFlag = "attack", modName = { "AdditionalPoisonChance" }, modType = "BASE", cfg = "skill" }, + { label = "% chance to inflict 1 additional poison (Main Hand)", flag = "weapon1Attack", modName = { "AdditionalPoisonChance" }, modType = "BASE", cfg = "weapon1" }, + { label = "% chance to inflict 1 additional poison (Off Hand)", flag = "weapon2Attack", modName = { "AdditionalPoisonChance" }, modType = "BASE", cfg = "weapon2" }, + { label = "inflict # additional poisons", notFlag = "attack", modName = { "AdditionalPoisonStacks" }, modType = "BASE", cfg = "skill" }, + { label = "inflict # additional poisons (Main Hand)", flag = "weapon1Attack", modName = { "AdditionalPoisonStacks" }, modType = "BASE", cfg = "weapon1" }, + { label = "inflict # additional poisons (Off Hand)", flag = "weapon2Attack", modName = { "AdditionalPoisonStacks" }, modType = "BASE", cfg = "weapon2" }, + { label = "Poison Stack Limits", modName = { "PoisonStackLimit", "CannotMultiplePoison" } }, + }, }, { label = "Total Increased", { format = "{0:mod:1}%", { modName = { "Damage", "ChaosDamage" }, modType = "INC", cfg = "poison" }, }, }, { label = "Total More", { format = "{0:mod:1}%", { modName = { "Damage", "ChaosDamage" }, modType = "MORE", cfg = "poison" }, }, }, { label = "Eff. DoT Multi", notFlag = "attack", haveOutput = "PoisonDotMulti", { format = "x {2:output:PoisonDotMulti}", { breakdown = "PoisonDotMulti" }, { modName = { "DotMultiplier", "ChaosDotMultiplier" }, cfg = "poison" }, }, }, From a2dd9c9c6c02a3d3e820ba8c0066030119a4d3e5 Mon Sep 17 00:00:00 2001 From: majochem <77203255+majochem@users.noreply.github.com> Date: Thu, 5 Mar 2026 14:13:31 +0100 Subject: [PATCH 04/10] Add parsing for "Wither on Hit with this weapon against Enemies with at least 12 Poisons on them" Just setting `Condition:CanWither` flag for now, as actually checking for number of poisons seems overkill and eliminates too many edge cases --- src/Modules/ModParser.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Modules/ModParser.lua b/src/Modules/ModParser.lua index 2815b7b9f7..3aa27b15df 100644 --- a/src/Modules/ModParser.lua +++ b/src/Modules/ModParser.lua @@ -3690,6 +3690,7 @@ local specialModList = { ["inflict (%d+) additional poisons? on the same target when you inflict poisons? with this weapon"] = function(num) return { mod("AdditionalPoisonStacks", "BASE", num, { type = "Condition", var = "{Hand}Attack" } ) } end, ["cannot poison enemies with at least (%d+) poisons? on them"] = function(num) return { mod("PoisonStackLimit", "BASE", num ) } end, ["cannot inflict multiple poisons in the same hit"] = { flag("CannotMultiplePoison") }, + ["wither on hit with this weapon against enemies with at least (%d+) poisons on them"] = { flag("Condition:CanWither") }, -- Suppression ["y?o?u?r? ?chance to suppress spell damage is lucky"] = { flag("SpellSuppressionChanceIsLucky") }, ["y?o?u?r? ?chance to suppress spell damage is unlucky"] = { flag("SpellSuppressionChanceIsUnlucky") }, From 962aeba3ee987c9eb91504c417da18b0f87382a9 Mon Sep 17 00:00:00 2001 From: majochem <77203255+majochem@users.noreply.github.com> Date: Thu, 5 Mar 2026 15:40:34 +0100 Subject: [PATCH 05/10] Add `ModStoreClass:Min()` to `ModStore.lua` This is the counterpart to `ModStoreClass:Max()` and gives back the lowest modifier value. This makes sense for things like the poison stack limit, where neither "BASE" nor "OVERRIDE" would work for multiple mods --- src/Classes/ModStore.lua | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Classes/ModStore.lua b/src/Classes/ModStore.lua index ff0130c481..75c1436e4e 100644 --- a/src/Classes/ModStore.lua +++ b/src/Classes/ModStore.lua @@ -207,6 +207,17 @@ function ModStoreClass:Max(cfg, ...) return max end +function ModStoreClass:Min(cfg, ...) + local min + for _, value in ipairs(self:Tabulate("MIN", cfg, ...)) do + local val = self:EvalMod(value.mod, cfg) + if min == nil or val < min then + min = val + end + end + return min +end + ---HasMod --- Checks if a mod exists with the given properties. --- Useful for determining if the other aggregate functions will find From b1060e5ceb9c015b8cea0aa8c1bb731c892bf4ff Mon Sep 17 00:00:00 2001 From: majochem <77203255+majochem@users.noreply.github.com> Date: Thu, 5 Mar 2026 15:47:04 +0100 Subject: [PATCH 06/10] Change `PoisonStackLimit` to use `"MIN"` modType --- src/Modules/CalcOffence.lua | 6 +++--- src/Modules/ModParser.lua | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Modules/CalcOffence.lua b/src/Modules/CalcOffence.lua index ccbbd4a84d..4dc9336265 100644 --- a/src/Modules/CalcOffence.lua +++ b/src/Modules/CalcOffence.lua @@ -4371,14 +4371,14 @@ function calcs.offence(env, actor, activeSkill) end -- Calculate average number of poisons that will be active on the enemy at once - local poisonStackLimit = skillModList:Sum("BASE", cfg, "PoisonStackLimit") + local poisonStackLimit = skillModList:Min(cfg, "PoisonStackLimit") local PoisonStacks = output.HitChance / 100 * poisonChance * additionalPoisonStacks * skillData.dpsMultiplier * (skillData.stackMultiplier or 1) * quantityMultiplier if (globalOutput.HitSpeed or globalOutput.Speed) > 0 then --assume skills with no cast, attack, or cooldown time are single cast PoisonStacks = PoisonStacks * globalOutput.PoisonDuration * (globalOutput.HitSpeed or globalOutput.Speed) -- If stack limit exists, avg. poison stack is more complicated - if poisonStackLimit > 0 and additionalPoisonStacks > 1 and PoisonStacks > poisonStackLimit then + if poisonStackLimit and poisonStackLimit > 0 and additionalPoisonStacks > 1 and PoisonStacks > poisonStackLimit then -- Calc number of avg. poisons applied per hit (without hitrate multipliers) local singleHitPoisonChance = output.HitChance / 100 * poisonChance local singleHitPoisonStacks = singleHitPoisonChance * additionalPoisonStacks @@ -4410,7 +4410,7 @@ function calcs.offence(env, actor, activeSkill) if skillModList:Flag(nil, "Condition:SinglePoison") then t_insert(globalBreakdown.PoisonStacks, "Capped to 1") end - if poisonStackLimit > 0 and PoisonStacks >= poisonStackLimit then + if poisonStackLimit and PoisonStacks >= poisonStackLimit then t_insert(globalBreakdown.PoisonStacks, "^8(affected by poison stack limit)") end end diff --git a/src/Modules/ModParser.lua b/src/Modules/ModParser.lua index 3aa27b15df..7f915919e9 100644 --- a/src/Modules/ModParser.lua +++ b/src/Modules/ModParser.lua @@ -3688,7 +3688,7 @@ local specialModList = { ["poisons on you expire (%d+)%% slower"] = function(num) return { mod("SelfPoisonDebuffExpirationRate", "BASE", -num) } end, ["(%d+)%% chance to inflict an additional poison on the same target when you inflict poison"] = function(num) return { mod("AdditionalPoisonChance", "BASE", num) } end, ["inflict (%d+) additional poisons? on the same target when you inflict poisons? with this weapon"] = function(num) return { mod("AdditionalPoisonStacks", "BASE", num, { type = "Condition", var = "{Hand}Attack" } ) } end, - ["cannot poison enemies with at least (%d+) poisons? on them"] = function(num) return { mod("PoisonStackLimit", "BASE", num ) } end, + ["cannot poison enemies with at least (%d+) poisons? on them"] = function(num) return { mod("PoisonStackLimit", "MIN", num ) } end, ["cannot inflict multiple poisons in the same hit"] = { flag("CannotMultiplePoison") }, ["wither on hit with this weapon against enemies with at least (%d+) poisons on them"] = { flag("Condition:CanWither") }, -- Suppression From d9faae359f3495bb3d4ac83cd5fd210f33aa8497 Mon Sep 17 00:00:00 2001 From: majochem <77203255+majochem@users.noreply.github.com> Date: Thu, 5 Mar 2026 15:52:26 +0100 Subject: [PATCH 07/10] Add `"MAX"` & `"MIN"` modType explanation to docs "MAX" already existed and was used, but was missing from `modSyntax.md`, so I added the explanation for both now --- docs/modSyntax.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/modSyntax.md b/docs/modSyntax.md index 631c9d9f02..b0e0b49f40 100644 --- a/docs/modSyntax.md +++ b/docs/modSyntax.md @@ -13,6 +13,7 @@ Used as a key, so you can reference this mod elsewhere in PoB. Can really be an - "OVERRIDE": used when you want to ignore any calculations done on this mod and just use the value (e.g. "your resistances are 78%" from Loreweave) - "FLAG": used for conditions. Value will be true/false when this type is used. - When you need the "FLAG" ModType, consider using the function `flag(name, source, modFlags, keywordFlags, extraTags)` instead. This method shortens the code and clarifies the intent. For example, `flag("ZealotsOath", { type = "Condition", var = "UsingFlask" })` is the same as `mod("ZealotsOath", "FLAG", true, { type = "Condition", var = "UsingFlask" })` +- "MAX" and "MIN": used for values where only the highest or lowest value should take effect respectively. Examples are `"ImprovedMinionDamageAppliesToPlayer"` for "Increases and Reductions to Minion Damage apply ... at X% of their value" or `"PoisonStackLimit"` for "Cannot Poison Enemies with at least X Poisons on them" ### Value This represents the raw value of the mod. When it's used in the skills to map from the skill data, this will be `nil`, as it pulls the number from the gem based on the level. ### Source From bdacae5c3fd9efdeda1048db52c1d57a62d90c0b Mon Sep 17 00:00:00 2001 From: majochem <77203255+majochem@users.noreply.github.com> Date: Thu, 5 Mar 2026 16:08:00 +0100 Subject: [PATCH 08/10] Add `PoisonStackLimit` to Viper Strike of the Mamba --- src/Data/SkillStatMap.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Data/SkillStatMap.lua b/src/Data/SkillStatMap.lua index 6cb605c472..f3f8bb668d 100644 --- a/src/Data/SkillStatMap.lua +++ b/src/Data/SkillStatMap.lua @@ -150,6 +150,7 @@ return { }, ["cannot_poison_poisoned_enemies"] = { flag("Condition:SinglePoison"), + mod("PoisonStackLimit", "MIN", 1), }, ["spell_damage_modifiers_apply_to_skill_dot"] = { skill("dotIsSpell", true), From 1e4bb6c9714b91ef84cb25aac22e98a4bf517732 Mon Sep 17 00:00:00 2001 From: majochem <77203255+majochem@users.noreply.github.com> Date: Thu, 5 Mar 2026 16:40:06 +0100 Subject: [PATCH 09/10] Further improve PoisonStacks breakdown - Now shows active poison stack limit - Now shows what uncapped poison stacks would be (to gauge if you're close to being under cap) - Changed wording of "Capped to 1" for `Condition:SinglePoison` hint to "Assume non-Poisoned Enemy" because that is what it is actually used for --- src/Modules/CalcOffence.lua | 12 +++++++++--- src/Modules/CalcSections.lua | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Modules/CalcOffence.lua b/src/Modules/CalcOffence.lua index 4dc9336265..176560c5d0 100644 --- a/src/Modules/CalcOffence.lua +++ b/src/Modules/CalcOffence.lua @@ -4373,12 +4373,13 @@ function calcs.offence(env, actor, activeSkill) -- Calculate average number of poisons that will be active on the enemy at once local poisonStackLimit = skillModList:Min(cfg, "PoisonStackLimit") local PoisonStacks = output.HitChance / 100 * poisonChance * additionalPoisonStacks * skillData.dpsMultiplier * (skillData.stackMultiplier or 1) * quantityMultiplier + local uncappedPoisonStacks if (globalOutput.HitSpeed or globalOutput.Speed) > 0 then --assume skills with no cast, attack, or cooldown time are single cast PoisonStacks = PoisonStacks * globalOutput.PoisonDuration * (globalOutput.HitSpeed or globalOutput.Speed) -- If stack limit exists, avg. poison stack is more complicated - if poisonStackLimit and poisonStackLimit > 0 and additionalPoisonStacks > 1 and PoisonStacks > poisonStackLimit then + if poisonStackLimit and poisonStackLimit > 0 and PoisonStacks > poisonStackLimit then -- Calc number of avg. poisons applied per hit (without hitrate multipliers) local singleHitPoisonChance = output.HitChance / 100 * poisonChance local singleHitPoisonStacks = singleHitPoisonChance * additionalPoisonStacks @@ -4388,6 +4389,7 @@ function calcs.offence(env, actor, activeSkill) local maxPoisonStacks = numPoisoningHits * singleHitPoisonStacks -- Only use `maxPoisonStacks` if original value exceeds it + uncappedPoisonStacks = m_max(PoisonStacks, maxPoisonStacks) PoisonStacks = m_min(PoisonStacks, maxPoisonStacks) end end @@ -4408,10 +4410,14 @@ function calcs.offence(env, actor, activeSkill) total = s_format("= %.2f", PoisonStacks), }) if skillModList:Flag(nil, "Condition:SinglePoison") then - t_insert(globalBreakdown.PoisonStacks, "Capped to 1") + t_insert(globalBreakdown.PoisonStacks, "Assuming 'non-Poisoned' Enemy") end if poisonStackLimit and PoisonStacks >= poisonStackLimit then - t_insert(globalBreakdown.PoisonStacks, "^8(affected by poison stack limit)") + t_insert(globalBreakdown.PoisonStacks, "^8(affected by poison stack limit of: " .. poisonStackLimit .. ")") + if uncappedPoisonStacks then + t_insert(globalBreakdown.PoisonStacks, "^8(uncapped poison stacks: " .. s_format("%.2f", uncappedPoisonStacks) .. ")") + end + end end for sub_pass = 1, 2 do diff --git a/src/Modules/CalcSections.lua b/src/Modules/CalcSections.lua index 3fdc15ba94..7a43a8ae8c 100644 --- a/src/Modules/CalcSections.lua +++ b/src/Modules/CalcSections.lua @@ -924,7 +924,7 @@ return { { label = "inflict # additional poisons", notFlag = "attack", modName = { "AdditionalPoisonStacks" }, modType = "BASE", cfg = "skill" }, { label = "inflict # additional poisons (Main Hand)", flag = "weapon1Attack", modName = { "AdditionalPoisonStacks" }, modType = "BASE", cfg = "weapon1" }, { label = "inflict # additional poisons (Off Hand)", flag = "weapon2Attack", modName = { "AdditionalPoisonStacks" }, modType = "BASE", cfg = "weapon2" }, - { label = "Poison Stack Limits", modName = { "PoisonStackLimit", "CannotMultiplePoison" } }, + { label = "Poison Stack Limits", modName = { "PoisonStackLimit", "CannotMultiplePoison" }, cfg = "skill" }, }, }, { label = "Total Increased", { format = "{0:mod:1}%", { modName = { "Damage", "ChaosDamage" }, modType = "INC", cfg = "poison" }, }, }, { label = "Total More", { format = "{0:mod:1}%", { modName = { "Damage", "ChaosDamage" }, modType = "MORE", cfg = "poison" }, }, }, From 799ac91dbb45d714a3d5a6d039ebe8088b926348 Mon Sep 17 00:00:00 2001 From: majochem <77203255+majochem@users.noreply.github.com> Date: Thu, 5 Mar 2026 17:08:13 +0100 Subject: [PATCH 10/10] Appease the spell checking gods `hitrate` -> `hit rate` --- src/Modules/CalcOffence.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Modules/CalcOffence.lua b/src/Modules/CalcOffence.lua index 176560c5d0..48c2665922 100644 --- a/src/Modules/CalcOffence.lua +++ b/src/Modules/CalcOffence.lua @@ -4380,7 +4380,7 @@ function calcs.offence(env, actor, activeSkill) -- If stack limit exists, avg. poison stack is more complicated if poisonStackLimit and poisonStackLimit > 0 and PoisonStacks > poisonStackLimit then - -- Calc number of avg. poisons applied per hit (without hitrate multipliers) + -- Calc number of avg. poisons applied per hit (without hit rate multipliers) local singleHitPoisonChance = output.HitChance / 100 * poisonChance local singleHitPoisonStacks = singleHitPoisonChance * additionalPoisonStacks