From 8c74ce085a74497ac1a94db4c16734497988695c Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Tue, 5 May 2026 05:22:42 +1000 Subject: [PATCH 1/4] Cast on Ward break Currently assumes the trigger will occur at max rate similar to CWDT --- src/Data/SkillStatMap.lua | 3 +++ src/Modules/CalcTriggers.lua | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/src/Data/SkillStatMap.lua b/src/Data/SkillStatMap.lua index decd9d44f0..8b84834d16 100644 --- a/src/Data/SkillStatMap.lua +++ b/src/Data/SkillStatMap.lua @@ -264,6 +264,9 @@ return { ["cast_on_stunned_%"] = { skill("chanceToTriggerOnStun", nil, { type = "SkillType", skillType = SkillType.Triggerable }, { type = "SkillType", skillType = SkillType.Spell }), }, +["trigger_on_ward_break_%_chance"] = { + skill("chanceToTriggerOnWardBreak", nil, { type = "SkillType", skillType = SkillType.Triggerable }, { type = "SkillType", skillType = SkillType.Spell }), +}, ["trigger_on_attack_hit_against_rare_or_unique"] = { skill("triggerMarkOnRareOrUnique", true, { type = "SkillType", skillType = SkillType.Triggerable }, { type = "SkillType", skillType = SkillType.Mark }), }, diff --git a/src/Modules/CalcTriggers.lua b/src/Modules/CalcTriggers.lua index 5dea49c477..f1d3788272 100644 --- a/src/Modules/CalcTriggers.lua +++ b/src/Modules/CalcTriggers.lua @@ -1182,6 +1182,11 @@ local configTable = { return {triggerChance = env.player.mainSkill.skillData.chanceToTriggerOnStun, source = env.player.mainSkill} end, + ["cast on ward break"] = function(env) + env.player.mainSkill.skillFlags.globalTrigger = true + return {triggerChance = env.player.mainSkill.skillData.chanceToTriggerOnWardBreak, + source = env.player.mainSkill} + end, ["spellslinger"] = function(env) if env.player.mainSkill.activeEffect.grantedEffect.name == "Spellslinger" then env.player.mainSkill.skillFlags.skipEffectiveRate = true From ecad556ba3de112f1f5bf72ffe86920d875005ac Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Tue, 5 May 2026 05:28:27 +1000 Subject: [PATCH 2/4] Iron Flasks charges gained on Ward break Adds support for the mod `Iron Flasks gain 2 charges when your Ward Breaks` on the new Timeless jewel notable Currently shows the charges gained when hovering over the flask and also applies the condition for `UnbrokenWard` automatically if the charges gained are higher than the charges used --- src/Classes/ItemsTab.lua | 8 ++++++++ src/Data/ModCache.lua | 2 +- src/Modules/CalcPerform.lua | 10 ++++++++++ src/Modules/ModParser.lua | 3 +++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/Classes/ItemsTab.lua b/src/Classes/ItemsTab.lua index 5ed9db4862..60633a99b0 100644 --- a/src/Classes/ItemsTab.lua +++ b/src/Classes/ItemsTab.lua @@ -3907,6 +3907,10 @@ function ItemsTabClass:AddItemTooltip(tooltip, item, slot, dbMode, maxWidth) if not item.base.flask.mana and not item.base.flask.life then chargesGenerated = chargesGenerated + modDB:Sum("BASE", nil, "UtilityFlaskChargesGenerated") end + local chargesGeneratedOnWardBreak = 0 + if item.baseName == "Iron Flask" then + chargesGeneratedOnWardBreak = chargesGeneratedOnWardBreak + modDB:Sum("BASE", nil, "IronFlaskChargesGeneratedOnWardBreak") + end local chargesGeneratedPerFlask = modDB:Sum("BASE", nil, "FlaskChargesGeneratedPerEmptyFlask") local emptyFlaskSlots = 0 @@ -3917,12 +3921,16 @@ function ItemsTabClass:AddItemTooltip(tooltip, item, slot, dbMode, maxWidth) end chargesGeneratedPerFlask = chargesGeneratedPerFlask * emptyFlaskSlots chargesGenerated = chargesGenerated * gainMod + chargesGeneratedOnWardBreak = chargesGeneratedOnWardBreak * gainMod chargesGeneratedPerFlask = chargesGeneratedPerFlask * gainMod local totalChargesGenerated = chargesGenerated + chargesGeneratedPerFlask if totalChargesGenerated > 0 then t_insert(stats, s_format("^8Charges generated: ^7%.2f^8 per second", totalChargesGenerated)) end + if chargesGeneratedOnWardBreak > 0 then + t_insert(stats, s_format("^8Charges generated on Ward Break: ^7%.2f", chargesGeneratedOnWardBreak)) + end local chanceToNotConsumeCharges = m_min(modDB:Sum("BASE", nil, "FlaskChanceNotConsumeCharges"), 100) if chanceToNotConsumeCharges ~= 0 then diff --git a/src/Data/ModCache.lua b/src/Data/ModCache.lua index 7069e22f93..978da2ae10 100755 --- a/src/Data/ModCache.lua +++ b/src/Data/ModCache.lua @@ -9480,7 +9480,7 @@ c["Intelligence provides no inherent bonus to Maximum Mana"]={{[1]={flags=0,keyw c["Intimidate Enemies for 4 seconds on Block while holding a Shield"]={{[1]={[1]={type="Condition",var="BlockedRecently"},[2]={type="Condition",var="UsingShield"},flags=0,keywordFlags=0,name="EnemyModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="Condition:Intimidated",type="FLAG",value=true}}}},nil} c["Intimidate Enemies for 4 seconds on Hit with Attacks while at maximum Endurance Charges"]={{[1]={[1]={stat="EnduranceCharges",thresholdStat="EnduranceChargesMax",type="StatThreshold"},[2]={type="Condition",var="HitRecently"},flags=0,keywordFlags=0,name="EnemyModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="Condition:Intimidated",type="FLAG",value=true}}}},nil} c["Intimidate Enemies on Hit if you've cast Punishment in the past 10 seconds"]={{[1]={[1]={type="Condition",var="SelfCastPunishment"},flags=4,keywordFlags=0,name="EnemyModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="Condition:Intimidated",type="FLAG",value=true}}}},nil} -c["Iron Flasks gain 2 charges when your Ward Breaks"]={nil,"Iron Flasks gain 2 charges when your Ward Breaks "} +c["Iron Flasks gain 2 charges when your Ward Breaks"]={{[1]={flags=0,keywordFlags=0,name="IronFlaskChargesGeneratedOnWardBreak",type="BASE",value=2}},nil} c["Iron Grip"]={{[1]={flags=0,keywordFlags=0,name="Keystone",type="LIST",value="Iron Grip"}},nil} c["Iron Reflexes"]={{[1]={flags=0,keywordFlags=0,name="Keystone",type="LIST",value="Iron Reflexes"}},nil} c["Iron Reflexes while stationary"]={{[1]={[1]={type="Condition",var="Stationary"},flags=0,keywordFlags=0,name="Keystone",type="LIST",value="Iron Reflexes"}},nil} diff --git a/src/Modules/CalcPerform.lua b/src/Modules/CalcPerform.lua index e92bb405a1..2c90f7a863 100644 --- a/src/Modules/CalcPerform.lua +++ b/src/Modules/CalcPerform.lua @@ -1639,6 +1639,16 @@ function calcs.perform(env, skipEHP) flaskConditionsNonUtility["UsingManaFlask"] = true end end + if item.baseName == "Iron Flask" then + local chargesGeneratedOnWardBreak = modDB:Sum("BASE", nil, "IronFlaskChargesGeneratedOnWardBreak") + if chargesGeneratedOnWardBreak > 0 then + local gainMod = item.flaskData.gainMod * (1 + modDB:Sum("INC", nil, "FlaskChargesGained") / 100) + local chargesUsed = item.flaskData.chargesUsed * (1 + modDB:Sum("INC", nil, "FlaskChargesUsed") / 100) + if chargesGeneratedOnWardBreak * gainMod > chargesUsed then + flaskConditions["UnbrokenWard"] = true + end + end + end if onlyRecovery then if item.base.flask.life and not modDB:Flag(nil, "CannotRecoverLifeOutsideLeech") then diff --git a/src/Modules/ModParser.lua b/src/Modules/ModParser.lua index 965e814105..546b5049cc 100644 --- a/src/Modules/ModParser.lua +++ b/src/Modules/ModParser.lua @@ -4973,6 +4973,9 @@ local specialModList = { ["utility flasks gain (%d+) charges? every (%d+) seconds"] = function(num, _, div) return { mod("UtilityFlaskChargesGenerated", "BASE", num / div) } end, + ["iron flasks gain (%d+) charges? when your ward breaks"] = function(num) return { + mod("IronFlaskChargesGeneratedOnWardBreak", "BASE", num) + } end, ["life flasks gain (%d+) charges? every (%d+) seconds"] = function(num, _, div) return { mod("LifeFlaskChargesGenerated", "BASE", num / div) } end, From 82d20ab789f440c1b50f0ac0a058f6085b8da7e1 Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Tue, 5 May 2026 05:41:03 +1000 Subject: [PATCH 3/4] Celestial Mathematics + The Unbreaking Circle + unbroken ward mods Adds support for Celestial Mathematics cold damage gain mod While you have Unbroken Ward, your next non-channelling Attack you Use yourself breaks your Ward to gain Added Cold Damage equal to 25% of Ward Applies automatically if you have the iron flask with enough flask charges gained on break or can override with a config option Added eHP calcs for the chance for Ward to not break mod Adds support for remaining timeless jewel mods +2 to Ward per 10 Armour on Equipped Helmet +4 to Ward per 10 Evasion Rating on Equipped Gloves +8 to Ward per 10 Energy Shield on Equipped Boots 12% increased effect of Non-Curse Auras from your skills while your Ward is Broken 30% increased Armour while you have Unbroken Ward 30% increased Damage while you have Unbroken Ward 30% increased Evasion Rating while you have Unbroken Ward 50% less Damage Taken from Damage over Time while you have Unbroken Ward Damage Penetrates 8% Elemental Resistances while your Ward is Broken --- spec/System/TestDefence_spec.lua | 22 ++++++++++++++++++++ src/Data/ModCache.lua | 24 +++++++++++----------- src/Modules/CalcDefence.lua | 16 ++++++++++----- src/Modules/ConfigOptions.lua | 3 +++ src/Modules/ModParser.lua | 35 ++++++++++++++++++++++++-------- 5 files changed, 74 insertions(+), 26 deletions(-) diff --git a/spec/System/TestDefence_spec.lua b/spec/System/TestDefence_spec.lua index ea79c4f747..00b80cc13a 100644 --- a/spec/System/TestDefence_spec.lua +++ b/spec/System/TestDefence_spec.lua @@ -378,6 +378,28 @@ describe("TestDefence", function() assert.are.equals(15, build.calcsTab.calcsOutput.LightningResistOverCap) end) + it("ward chance to not break increases effective hit pool", function() + build.configTab.input.enemyIsBoss = "None" + build.configTab.input.customMods = "\z + +940 to maximum life\n\z + +200 to Ward\n\z + " + build.configTab:BuildModList() + runCallback("OnFrame") + assert.are.equals(987, build.calcsTab.calcsOutput.TotalEHP) + assert.are.equals(1200, build.calcsTab.calcsOutput.PhysicalMaximumHitTaken) + + build.configTab.input.customMods = "\z + +940 to maximum life\n\z + +200 to Ward\n\z + Ward has a 50% chance to not Break\n\z + " + build.configTab:BuildModList() + runCallback("OnFrame") + assert.are.equals(994, build.calcsTab.calcsOutput.TotalEHP) + assert.are.equals(1200, build.calcsTab.calcsOutput.PhysicalMaximumHitTaken) + end) + -- fun part it("armoured max hits", function() build.configTab.input.enemyIsBoss = "None" diff --git a/src/Data/ModCache.lua b/src/Data/ModCache.lua index 978da2ae10..4921a5c0f7 100755 --- a/src/Data/ModCache.lua +++ b/src/Data/ModCache.lua @@ -623,7 +623,7 @@ c["+2 to Maximum Life per 10 Dexterity"]={{[1]={[1]={div=10,stat="Dex",type="Per c["+2 to Maximum Life per 10 Intelligence"]={{[1]={[1]={div=10,stat="Int",type="PerStat"},flags=0,keywordFlags=0,name="Life",type="BASE",value=2}},nil} c["+2 to Maximum Power Charges"]={{[1]={flags=0,keywordFlags=0,name="PowerChargesMax",type="BASE",value=2}},nil} c["+2 to Maximum Rage"]={{[1]={flags=0,keywordFlags=0,name="MaximumRage",type="BASE",value=2}},nil} -c["+2 to Ward per 10 Armour on Equipped Helmet"]={{[1]={[1]={div=10,stat="Armour",type="PerStat"},flags=0,keywordFlags=0,name="Ward",type="BASE",value=2}}," on Equipped Helmet "} +c["+2 to Ward per 10 Armour on Equipped Helmet"]={{[1]={[1]={div=10,stat="ArmourOnHelmet",type="PerStat"},flags=0,keywordFlags=0,name="Ward",type="BASE",value=2}},nil} c["+2 to Weapon Range"]={{[1]={flags=0,keywordFlags=0,name="WeaponRange",type="BASE",value=2}},nil} c["+2 to maximum Snipe Stages"]={{[1]={[1]={effectType="Buff",type="GlobalEffect",unscalable=true},flags=0,keywordFlags=0,name="Multiplier:SnipeStagesMax",type="BASE",value=2}},nil} c["+2 to maximum number of Raised Zombies"]={{[1]={flags=0,keywordFlags=0,name="ActiveZombieLimit",type="BASE",value=2}},nil} @@ -1356,7 +1356,7 @@ c["+4 to Level of all Spell Skill Gems"]={{[1]={flags=0,keywordFlags=0,name="Gem c["+4 to Maximum Fanatic Charges"]={{}," Maximum Fanatic Charges "} c["+4 to Minimum Endurance Charges"]={{[1]={flags=0,keywordFlags=0,name="EnduranceChargesMin",type="BASE",value=4}},nil} c["+4 to Minimum Power Charges"]={{[1]={flags=0,keywordFlags=0,name="PowerChargesMin",type="BASE",value=4}},nil} -c["+4 to Ward per 10 Evasion Rating on Equipped Gloves"]={{[1]={[1]={div=10,stat="Evasion",type="PerStat"},flags=0,keywordFlags=0,name="Ward",type="BASE",value=4}}," on Equipped Gloves "} +c["+4 to Ward per 10 Evasion Rating on Equipped Gloves"]={{[1]={[1]={div=10,stat="EvasionOnGloves",type="PerStat"},flags=0,keywordFlags=0,name="Ward",type="BASE",value=4}},nil} c["+4 to all Attributes"]={{[1]={flags=0,keywordFlags=0,name="Str",type="BASE",value=4},[2]={flags=0,keywordFlags=0,name="Dex",type="BASE",value=4},[3]={flags=0,keywordFlags=0,name="Int",type="BASE",value=4},[4]={flags=0,keywordFlags=0,name="All",type="BASE",value=4}},nil} c["+4 to maximum number of Summoned Totems"]={{[1]={flags=0,keywordFlags=0,name="ActiveTotemLimit",type="BASE",value=4}},nil} c["+4% Chance to Block"]={{[1]={flags=0,keywordFlags=0,name="BlockChance",type="BASE",value=4}},nil} @@ -1703,7 +1703,7 @@ c["+8 to Intelligence"]={{[1]={flags=0,keywordFlags=0,name="Int",type="BASE",val c["+8 to Level of Socketed Gems"]={{[1]={[1]={slotName="{SlotName}",type="SocketedIn"},flags=0,keywordFlags=0,name="GemProperty",type="LIST",value={key="level",keyword="all",value=8}}},nil} c["+8 to Minimum Rage"]={{[1]={flags=0,keywordFlags=0,name="MinimumRage",type="BASE",value=8}},nil} c["+8 to Strength"]={{[1]={flags=0,keywordFlags=0,name="Str",type="BASE",value=8}},nil} -c["+8 to Ward per 10 Energy Shield on Equipped Boots"]={{[1]={flags=0,keywordFlags=0,name="Ward",type="BASE",value=8}}," per 10 Energy Shield on Equipped Boots "} +c["+8 to Ward per 10 Energy Shield on Equipped Boots"]={{[1]={[1]={div=10,stat="EnergyShieldOnBoots",type="PerStat"},flags=0,keywordFlags=0,name="Ward",type="BASE",value=8}},nil} c["+8 to all Attributes"]={{[1]={flags=0,keywordFlags=0,name="Str",type="BASE",value=8},[2]={flags=0,keywordFlags=0,name="Dex",type="BASE",value=8},[3]={flags=0,keywordFlags=0,name="Int",type="BASE",value=8},[4]={flags=0,keywordFlags=0,name="All",type="BASE",value=8}},nil} c["+8% Chance to Block"]={{[1]={flags=0,keywordFlags=0,name="BlockChance",type="BASE",value=8}},nil} c["+8% Chance to Block Attack Damage"]={{[1]={flags=0,keywordFlags=0,name="BlockChance",type="BASE",value=8}},nil} @@ -3015,7 +3015,7 @@ c["12% increased Warcry Cooldown Recovery Rate"]={{[1]={flags=0,keywordFlags=4,n c["12% increased Warcry Duration"]={{[1]={flags=0,keywordFlags=4,name="Duration",type="INC",value=12}},nil} c["12% increased Ward"]={{[1]={flags=0,keywordFlags=0,name="Ward",type="INC",value=12}},nil} c["12% increased effect of Non-Curse Auras from your Skills"]={{[1]={[1]={skillType=43,type="SkillType"},[2]={neg=true,skillType=79,type="SkillType"},flags=0,keywordFlags=0,name="AuraEffect",type="INC",value=12}},nil} -c["12% increased effect of Non-Curse Auras from your skills while your Ward is Broken"]={{[1]={[1]={skillType=43,type="SkillType"},[2]={neg=true,skillType=79,type="SkillType"},flags=0,keywordFlags=0,name="AuraEffect",type="INC",value=12}}," while your Ward is Broken "} +c["12% increased effect of Non-Curse Auras from your skills while your Ward is Broken"]={{[1]={[1]={skillType=43,type="SkillType"},[2]={neg=true,skillType=79,type="SkillType"},[3]={neg=true,type="Condition",var="UnbrokenWard"},flags=0,keywordFlags=0,name="AuraEffect",type="INC",value=12}},nil} c["12% increased maximum Energy Shield"]={{[1]={[1]={type="Global"},flags=0,keywordFlags=0,name="EnergyShield",type="INC",value=12}},nil} c["12% increased maximum Life"]={{[1]={flags=0,keywordFlags=0,name="Life",type="INC",value=12}},nil} c["12% increased maximum Mana"]={{[1]={flags=0,keywordFlags=0,name="Mana",type="INC",value=12}},nil} @@ -4767,7 +4767,7 @@ c["30% increased Armour"]={{[1]={flags=0,keywordFlags=0,name="Armour",type="INC" c["30% increased Armour and Evasion Rating during Onslaught"]={{[1]={[1]={type="Condition",var="Onslaught"},flags=0,keywordFlags=0,name="ArmourAndEvasion",type="INC",value=30}},nil} c["30% increased Armour and Evasion Rating while Fortified"]={{[1]={[1]={type="Condition",var="Fortified"},flags=0,keywordFlags=0,name="ArmourAndEvasion",type="INC",value=30}},nil} c["30% increased Armour if you have been Hit Recently"]={{[1]={[1]={type="Condition",var="BeenHitRecently"},flags=0,keywordFlags=0,name="Armour",type="INC",value=30}},nil} -c["30% increased Armour while you have Unbroken Ward"]={{[1]={flags=0,keywordFlags=0,name="Armour",type="INC",value=30}}," while you have Unbroken Ward "} +c["30% increased Armour while you have Unbroken Ward"]={{[1]={[1]={type="Condition",var="UnbrokenWard"},flags=0,keywordFlags=0,name="Armour",type="INC",value=30}},nil} c["30% increased Attack Critical Strike Chance while Dual Wielding"]={{[1]={[1]={type="Condition",var="DualWielding"},flags=1,keywordFlags=0,name="CritChance",type="INC",value=30}},nil} c["30% increased Attack Damage"]={{[1]={flags=1,keywordFlags=0,name="Damage",type="INC",value=30}},nil} c["30% increased Attack Damage if you've been Hit Recently"]={{[1]={[1]={type="Condition",var="BeenHitRecently"},flags=1,keywordFlags=0,name="Damage",type="INC",value=30}},nil} @@ -4813,7 +4813,7 @@ c["30% increased Damage while Ignited"]={{[1]={[1]={type="Condition",var="Ignite c["30% increased Damage while Leeching"]={{[1]={[1]={type="Condition",var="Leeching"},flags=0,keywordFlags=0,name="Damage",type="INC",value=30}},nil} c["30% increased Damage while in Blood Stance"]={{[1]={[1]={type="Condition",var="BloodStance"},flags=0,keywordFlags=0,name="Damage",type="INC",value=30}},nil} c["30% increased Damage while wielding a Wand"]={{[1]={[1]={type="Condition",var="UsingWand"},flags=0,keywordFlags=0,name="Damage",type="INC",value=30}},nil} -c["30% increased Damage while you have Unbroken Ward"]={{[1]={flags=0,keywordFlags=0,name="Damage",type="INC",value=30}}," while you have Unbroken Ward "} +c["30% increased Damage while you have Unbroken Ward"]={{[1]={[1]={type="Condition",var="UnbrokenWard"},flags=0,keywordFlags=0,name="Damage",type="INC",value=30}},nil} c["30% increased Damage while you have no Energy Shield"]={{[1]={[1]={neg=true,type="Condition",var="HaveEnergyShield"},flags=0,keywordFlags=0,name="Damage",type="INC",value=30}},nil} c["30% increased Damage with Ailments from Attack Skills"]={{[1]={flags=2048,keywordFlags=65536,name="Damage",type="INC",value=30}},nil} c["30% increased Damage with Bleeding"]={{[1]={flags=0,keywordFlags=4194304,name="Damage",type="INC",value=30}},nil} @@ -4862,7 +4862,7 @@ c["30% increased Evasion Rating and Armour"]={{[1]={flags=0,keywordFlags=0,name= c["30% increased Evasion Rating if you haven't been Hit Recently"]={{[1]={[1]={neg=true,type="Condition",var="BeenHitRecently"},flags=0,keywordFlags=0,name="Evasion",type="INC",value=30}},nil} c["30% increased Evasion Rating while Phasing"]={{[1]={[1]={type="Condition",var="Phasing"},flags=0,keywordFlags=0,name="Evasion",type="INC",value=30}},nil} c["30% increased Evasion Rating while you have Energy Shield"]={{[1]={[1]={type="Condition",var="HaveEnergyShield"},flags=0,keywordFlags=0,name="Evasion",type="INC",value=30}},nil} -c["30% increased Evasion Rating while you have Unbroken Ward"]={{[1]={flags=0,keywordFlags=0,name="Evasion",type="INC",value=30}}," while you have Unbroken Ward "} +c["30% increased Evasion Rating while you have Unbroken Ward"]={{[1]={[1]={type="Condition",var="UnbrokenWard"},flags=0,keywordFlags=0,name="Evasion",type="INC",value=30}},nil} c["30% increased Evasion Rating while you have an active Tincture"]={{[1]={[1]={type="Condition",var="UsingTincture"},flags=0,keywordFlags=0,name="Evasion",type="INC",value=30}},nil} c["30% increased Fire Damage"]={{[1]={flags=0,keywordFlags=0,name="FireDamage",type="INC",value=30}},nil} c["30% increased Fire Damage if you have used a Cold Skill Recently"]={{[1]={[1]={type="Condition",var="UsedColdSkillRecently"},flags=0,keywordFlags=0,name="FireDamage",type="INC",value=30}},nil} @@ -5816,7 +5816,7 @@ c["50% increased total Recovery per second from Life Leech"]={{[1]={flags=0,keyw c["50% less Cold Resistance"]={{[1]={flags=0,keywordFlags=0,name="ColdResist",type="MORE",value=-50}},nil} c["50% less Critical Strike Chance"]={{[1]={flags=0,keywordFlags=0,name="CritChance",type="MORE",value=-50}},nil} c["50% less Damage"]={{[1]={flags=0,keywordFlags=0,name="Damage",type="MORE",value=-50}},nil} -c["50% less Damage Taken from Damage over Time while you have Unbroken Ward"]={{[1]={flags=0,keywordFlags=0,name="DamageTakenOverTime",type="MORE",value=-50}}," while you have Unbroken Ward "} +c["50% less Damage Taken from Damage over Time while you have Unbroken Ward"]={{[1]={[1]={type="Condition",var="UnbrokenWard"},flags=0,keywordFlags=0,name="DamageTakenOverTime",type="MORE",value=-50}},nil} c["50% less Damage with Bleeding"]={{[1]={flags=0,keywordFlags=4194304,name="Damage",type="MORE",value=-50}},nil} c["50% less Duration"]={{[1]={flags=0,keywordFlags=0,name="Duration",type="MORE",value=-50}},nil} c["50% less Duration of Ignites you inflict"]={{[1]={flags=0,keywordFlags=0,name="EnemyIgniteDuration",type="MORE",value=-50}},nil} @@ -8189,7 +8189,7 @@ c["Damage Penetrates 6% Lightning Resistance"]={{[1]={flags=0,keywordFlags=0,nam c["Damage Penetrates 6% Lightning Resistance during Effect"]={{[1]={[1]={type="Condition",var="UsingFlask"},flags=0,keywordFlags=0,name="LightningPenetration",type="BASE",value=6}},nil} c["Damage Penetrates 6% of Enemy Elemental Resistances"]={{[1]={flags=0,keywordFlags=0,name="ElementalPenetration",type="BASE",value=6}},nil} c["Damage Penetrates 7% Chaos Resistance"]={{[1]={flags=0,keywordFlags=0,name="ChaosPenetration",type="BASE",value=7}},nil} -c["Damage Penetrates 8% Elemental Resistances while your Ward is Broken"]={{[1]={flags=0,keywordFlags=0,name="ElementalPenetration",type="BASE",value=8}}," while your Ward is Broken "} +c["Damage Penetrates 8% Elemental Resistances while your Ward is Broken"]={{[1]={[1]={neg=true,type="Condition",var="UnbrokenWard"},flags=0,keywordFlags=0,name="ElementalPenetration",type="BASE",value=8}},nil} c["Damage Penetrates 8% Lightning Resistance"]={{[1]={flags=0,keywordFlags=0,name="LightningPenetration",type="BASE",value=8}},nil} c["Damage Penetrates 8% of Enemy Elemental Resistances"]={{[1]={flags=0,keywordFlags=0,name="ElementalPenetration",type="BASE",value=8}},nil} c["Damage Penetrates 9% Elemental Resistances while you are Chilled"]={{[1]={[1]={type="Condition",var="Chilled"},flags=0,keywordFlags=0,name="ElementalPenetration",type="BASE",value=9}},nil} @@ -12339,8 +12339,8 @@ c["Warcry Skills have 30% increased Area of Effect"]={{[1]={[1]={skillType=73,ty c["Warcry Skills have 35% increased Area of Effect"]={{[1]={[1]={skillType=73,type="SkillType"},flags=0,keywordFlags=0,name="AreaOfEffect",type="INC",value=35}},nil} c["Warcry Skills have 40% increased Area of Effect"]={{[1]={[1]={skillType=73,type="SkillType"},flags=0,keywordFlags=0,name="AreaOfEffect",type="INC",value=40}},nil} c["Warcry Skills' Cooldown Time is 4 seconds"]={{[1]={flags=0,keywordFlags=4,name="CooldownRecovery",type="OVERRIDE",value=4}},nil} -c["Ward does not Break during Effect"]={{[1]={[1]={type="Condition",var="UsingFlask"},flags=0,keywordFlags=0,name="WardNotBreak",type="FLAG",value=true}},nil} -c["Ward has a 60% chance to not Break"]={nil,"Ward has a 60% chance to not Break "} +c["Ward does not Break during Effect"]={{[1]={[1]={type="Condition",var="UsingFlask"},flags=0,keywordFlags=0,name="Condition:WardNotBreak",type="FLAG",value=true},[2]={[1]={type="Condition",var="UsingFlask"},flags=0,keywordFlags=0,name="Condition:UnbrokenWard",type="FLAG",value=true}},nil} +c["Ward has a 60% chance to not Break"]={{[1]={flags=0,keywordFlags=0,name="WardAvoidBreakChance",type="BASE",value=60}},nil} c["Weapons you Animate create an additional copy"]={nil,"Weapons you Animate create an additional copy "} c["When 90% of your Hex's Duration Expires on an Enemy, Eat 1 Soul per Enemy Power"]={{[1]={flags=0,keywordFlags=0,name="Condition:CanHaveSoulEater",type="FLAG",value=true}},nil} c["When Hit during effect, 25% of Life loss from Damage taken occurs over 4 seconds instead"]={{[1]={[1]={type="Condition",var="UsingFlask"},flags=0,keywordFlags=0,name="LifeLossPrevented",type="BASE",value=25}},nil} @@ -12384,7 +12384,7 @@ c["While not on Full Life, Sacrifice 20% of Mana per Second to Recover that much c["While on Low Life, Life Flasks gain 5 Charges every 3 seconds"]={{[1]={[1]={type="Condition",var="LowLife"},flags=0,keywordFlags=0,name="LifeFlaskChargesGenerated",type="BASE",value=1.6666666666667}},nil} c["While on Low Life, Life Flasks gain 6 Charges every 3 seconds"]={{[1]={[1]={type="Condition",var="LowLife"},flags=0,keywordFlags=0,name="LifeFlaskChargesGenerated",type="BASE",value=2}},nil} c["While there are at least five nearby Allies, you and nearby Allies have Onslaught"]={{[1]={[1]={threshold=5,type="MultiplierThreshold",var="NearbyAlly"},flags=0,keywordFlags=0,name="ExtraAura",type="LIST",value={mod={flags=0,keywordFlags=0,name="Onslaught",type="FLAG",value=true}}}},nil} -c["While you have Unbroken Ward, your next non-channelling Attack you Use yourself breaks your Ward to gain Added Cold Damage equal to 25% of Ward"]={nil,"While you have Unbroken Ward, your next non-channelling Attack you Use yourself breaks your Ward to gain Added Cold Damage equal to 25% of Ward "} +c["While you have Unbroken Ward, your next non-channelling Attack you Use yourself breaks your Ward to gain Added Cold Damage equal to 25% of Ward"]={{[1]={[1]={percent=25,stat="Ward",type="PercentStat"},[2]={neg=true,skillType=57,type="SkillType"},[3]={skillType=1,type="SkillType"},[4]={neg=true,type="Condition",var="WardNotBreak"},[5]={type="Condition",var="UnbrokenWard"},flags=0,keywordFlags=0,name="ColdMin",type="BASE",value=1},[2]={[1]={percent=25,stat="Ward",type="PercentStat"},[2]={neg=true,skillType=57,type="SkillType"},[3]={skillType=1,type="SkillType"},[4]={neg=true,type="Condition",var="WardNotBreak"},[5]={type="Condition",var="UnbrokenWard"},flags=0,keywordFlags=0,name="ColdMax",type="BASE",value=1}},nil} c["While your Passive Skill Tree connects to a class' starting location, you gain:"]={{},nil} c["Wicked Ward"]={{[1]={flags=0,keywordFlags=0,name="Keystone",type="LIST",value="Wicked Ward"}},nil} c["Wind Dancer"]={{[1]={flags=0,keywordFlags=0,name="Keystone",type="LIST",value="Wind Dancer"}},nil} diff --git a/src/Modules/CalcDefence.lua b/src/Modules/CalcDefence.lua index cfb1699e22..b916523e5c 100644 --- a/src/Modules/CalcDefence.lua +++ b/src/Modules/CalcDefence.lua @@ -215,7 +215,8 @@ function calcs.reducePoolsByDamage(poolTable, damageTable, actor) end local ward = poolTbl.Ward or output.Ward or 0 - local restoreWard = modDB:Flag(nil, "WardNotBreak") and ward or 0 + local wardActiveChance = poolTbl.WardActiveChance or (ward > 0 and 1 or 0) + local wardAvoidBreakChance = modDB:Flag(nil, "Condition:WardNotBreak") and 1 or m_min(modDB:Sum("BASE", nil, "WardAvoidBreakChance") / 100, 1) local energyShield = poolTbl.EnergyShield or output.EnergyShieldRecoveryCap local mana = poolTbl.Mana or output.ManaUnreserved or 0 @@ -395,7 +396,8 @@ function calcs.reducePoolsByDamage(poolTable, damageTable, actor) AlliesTakenBeforeYou = alliesTakenBeforeYou, Aegis = aegis, Guard = guard, - Ward = m_floor(restoreWard), + Ward = m_floor(ward < wardBeforeHit and (wardAvoidBreakChance > 0 and wardBeforeHit or 0) or ward), + WardActiveChance = ward < wardBeforeHit and wardActiveChance * wardAvoidBreakChance or wardActiveChance, EnergyShield = m_floor(energyShield), Mana = m_floor(mana), Life = m_floor(life), @@ -2538,7 +2540,10 @@ function calcs.buildDefenceEstimations(env, actor) end if numHits == 0 then return m_huge - elseif modDB:Flag(nil, "WardNotBreak") and output.Ward > 0 and numHits < output.Ward then + end + + local wardAvoidBreakChance = modDB:Flag(nil, "Condition:WardNotBreak") and 1 or m_min(modDB:Sum("BASE", nil, "WardAvoidBreakChance") / 100, 1) + if wardAvoidBreakChance == 1 and output.Ward > 0 and numHits < output.Ward then return m_huge else numHits = 0 @@ -2546,7 +2551,7 @@ function calcs.buildDefenceEstimations(env, actor) local ward = output.Ward or 0 -- don't apply non-perma ward for speed up calcs as it won't zero it correctly per hit - if (not modDB:Flag(nil, "WardNotBreak")) and DamageIn["cycles"] > 1 then + if wardAvoidBreakChance < 1 and DamageIn["cycles"] > 1 then ward = 0 end local aegis = { } @@ -2658,7 +2663,8 @@ function calcs.buildDefenceEstimations(env, actor) -- to speed it up, run recursively but accelerated local speedUp = data.misc.ehpCalcSpeedUp DamageIn["cyclesRan"] = DamageIn["cyclesRan"] or false - if not DamageIn["cyclesRan"] and poolTable.Life > 0 and DamageIn["iterations"] < maxIterations then + local wardAvoidBreakActive = wardAvoidBreakChance < 1 and (poolTable.WardActiveChance or 0) > 0.01 + if not DamageIn["cyclesRan"] and not wardAvoidBreakActive and poolTable.Life > 0 and DamageIn["iterations"] < maxIterations then Damage = { } for _, damageType in ipairs(dmgTypeList) do Damage[damageType] = DamageIn[damageType] * speedUp diff --git a/src/Modules/ConfigOptions.lua b/src/Modules/ConfigOptions.lua index b81c0a65c9..c8f9b348b9 100644 --- a/src/Modules/ConfigOptions.lua +++ b/src/Modules/ConfigOptions.lua @@ -192,6 +192,9 @@ return { { var = "conditionHaveEnergyShield", type = "check", label = "Do you always have ^x88FFFFEnergy Shield?", ifCond = "HaveEnergyShield", apply = function(val, modList, enemyModList) modList:NewMod("Condition:HaveEnergyShield", "FLAG", true, "Config") end }, + { var = "conditionUnbrokenWard", type = "check", label = "Do you have unbroken ^xFFFF77Ward?", ifCond = "UnbrokenWard", tooltip = "You will automatically be considered to have unbroken ^xFFFF77Ward ^7if you have ^xAF6025Olroth's Resolve ^7active or\nan Iron Flask with enough Flask Charges gained on ^xFFFF77Ward ^7Break.",apply = function(val, modList, enemyModList) + modList:NewMod("Condition:UnbrokenWard", "FLAG", true, "Config") + end }, { var = "minionsConditionFullLife", type = "check", label = "Are your Minions always on Full ^xE05030Life?", ifMinionCond = "FullLife", apply = function(val, modList, enemyModList) modList:NewMod("MinionModifier", "LIST", { mod = modLib.createMod("Condition:FullLife", "FLAG", true, "Config") }, "Config") end }, diff --git a/src/Modules/ModParser.lua b/src/Modules/ModParser.lua index 546b5049cc..deedd18474 100644 --- a/src/Modules/ModParser.lua +++ b/src/Modules/ModParser.lua @@ -1442,20 +1442,30 @@ local modTagList = { ["per (%d+)%% chance to block attack damage"] = function(num) return { tag = { type = "PerStat", stat = "BlockChance", div = num } } end, ["per (%d+)%% chance to block spell damage"] = function(num) return { tag = { type = "PerStat", stat = "SpellBlockChance", div = num } } end, ["per (%d+) of the lowest of armour and evasion rating"] = function(num) return { tag = { type = "PerStat", stat = "LowestOfArmourAndEvasion", div = num } } end, - ["per (%d+) maximum energy shield on equipped helmet"] = function(num) return { tag = { type = "PerStat", stat = "EnergyShieldOnHelmet", div = num } } end, + ["per (%d+) energy shield on equipped gloves"] = function(num) return { tag = { type = "PerStat", stat = "EnergyShieldOnGloves", div = num } } end, ["per (%d+) maximum energy shield on helmet"] = function(num) return { tag = { type = "PerStat", stat = "EnergyShieldOnHelmet", div = num } } end, - ["per (%d+) evasion rating on body armour"] = function(num) return { tag = { type = "PerStat", stat = "EvasionOnBody Armour", div = num } } end, - ["per (%d+) evasion rating on equipped body armour"] = function(num) return { tag = { type = "PerStat", stat = "EvasionOnBody Armour", div = num } } end, - ["per (%d+) armour on equipped shield"] = function(num) return { tag = { type = "PerStat", stat = "ArmourOnWeapon 2", div = num } } end, - ["per (%d+) armour or evasion rating on shield"] = function(num) return { tag = { type = "PerStat", statList = { "ArmourOnWeapon 2", "EvasionOnWeapon 2" }, div = num } } end, - ["per (%d+) armour or evasion rating on equipped shield"] = function(num) return { tag = { type = "PerStat", statList = { "ArmourOnWeapon 2", "EvasionOnWeapon 2" }, div = num } } end, - ["per (%d+) evasion rating on equipped shield"] = function(num) return { tag = { type = "PerStat", stat = "EvasionOnWeapon 2", div = num } } end, + ["per (%d+) maximum energy shield on equipped helmet"] = function(num) return { tag = { type = "PerStat", stat = "EnergyShieldOnHelmet", div = num } } end, + ["per (%d+) energy shield on equipped helmet"] = function(num) return { tag = { type = "PerStat", stat = "EnergyShieldOnHelmet", div = num } } end, + ["per (%d+) energy shield on equipped boots"] = function(num) return { tag = { type = "PerStat", stat = "EnergyShieldOnBoots", div = num } } end, + ["per (%d+) energy shield on equipped body armour"] = function(num) return { tag = { type = "PerStat", stat = "EnergyShieldOnBody Armour", div = num } } end, ["per (%d+) maximum energy shield on equipped shield"] = function(num) return { tag = { type = "PerStat", stat = "EnergyShieldOnWeapon 2", div = num } } end, ["per (%d+) maximum energy shield on shield"] = function(num) return { tag = { type = "PerStat", stat = "EnergyShieldOnWeapon 2", div = num } } end, + ["per (%d+) evasion rating on equipped gloves"] = function(num) return { tag = { type = "PerStat", stat = "EvasionOnGloves", div = num } } end, + ["per (%d+) evasion rating on equipped helmet"] = function(num) return { tag = { type = "PerStat", stat = "EvasionOnHelmet", div = num } } end, ["per (%d+) evasion on equipped boots"] = function(num) return { tag = { type = "PerStat", stat = "EvasionOnBoots", div = num } } end, ["per (%d+) evasion on boots"] = function(num) return { tag = { type = "PerStat", stat = "EvasionOnBoots", div = num } } end, - ["per (%d+) armour on equipped gloves"] = function(num) return { tag = { type = "PerStat", stat = "ArmourOnGloves", div = num } } end, + ["per (%d+) evasion rating on equipped boots"] = function(num) return { tag = { type = "PerStat", stat = "EvasionOnBoots", div = num } } end, + ["per (%d+) evasion rating on body armour"] = function(num) return { tag = { type = "PerStat", stat = "EvasionOnBody Armour", div = num } } end, + ["per (%d+) evasion rating on equipped body armour"] = function(num) return { tag = { type = "PerStat", stat = "EvasionOnBody Armour", div = num } } end, + ["per (%d+) evasion rating on equipped shield"] = function(num) return { tag = { type = "PerStat", stat = "EvasionOnWeapon 2", div = num } } end, ["per (%d+) armour on gloves"] = function(num) return { tag = { type = "PerStat", stat = "ArmourOnGloves", div = num } } end, + ["per (%d+) armour on equipped gloves"] = function(num) return { tag = { type = "PerStat", stat = "ArmourOnGloves", div = num } } end, + ["per (%d+) armour on equipped helmet"] = function(num) return { tag = { type = "PerStat", stat = "ArmourOnHelmet", div = num } } end, + ["per (%d+) armour on equipped boots"] = function(num) return { tag = { type = "PerStat", stat = "ArmourOnBoots", div = num } } end, + ["per (%d+) armour on equipped body armour"] = function(num) return { tag = { type = "PerStat", stat = "ArmourOnBody Armour", div = num } } end, + ["per (%d+) armour on equipped shield"] = function(num) return { tag = { type = "PerStat", stat = "ArmourOnWeapon 2", div = num } } end, + ["per (%d+) armour or evasion rating on shield"] = function(num) return { tag = { type = "PerStat", statList = { "ArmourOnWeapon 2", "EvasionOnWeapon 2" }, div = num } } end, + ["per (%d+) armour or evasion rating on equipped shield"] = function(num) return { tag = { type = "PerStat", statList = { "ArmourOnWeapon 2", "EvasionOnWeapon 2" }, div = num } } end, ["per (%d+)%% chaos resistance"] = function(num) return { tag = { type = "PerStat", stat = "ChaosResist", div = num } } end, ["per (%d+)%% cold resistance above 75%%"] = function(num) return { tag = { type = "PerStat", stat = "ColdResistOver75", div = num } } end, ["per (%d+)%% lightning resistance above 75%%"] = function(num) return { tag = { type = "PerStat", stat = "LightningResistOver75", div = num } } end, @@ -1635,6 +1645,8 @@ local modTagList = { ["while you have a linked target"] = { tag = { type = "MultiplierThreshold", var = "LinkedTargets", threshold = 1 } }, ["while you have fortify"] = { tag = { type = "Condition", var = "Fortified" } }, ["while you have phasing"] = { tag = { type = "Condition", var = "Phasing" } }, + ["while you have unbroken ward"] = { tag = { type = "Condition", var = "UnbrokenWard" } }, + ["while your ward is broken"] = { tag = { type = "Condition", var = "UnbrokenWard", neg = true } }, ["if you[' ]h?a?ve suppressed spell damage recently"] = { tag = { type = "Condition", var = "SuppressedRecently" } }, ["while you have elusive"] = { tag = { type = "Condition", var = "Elusive" } }, ["while physical aegis is depleted"] = { tag = { type = "Condition", var = "PhysicalAegisDepleted" } }, @@ -2300,6 +2312,7 @@ local specialModList = { ["increases and reductions to mana regeneration rate instead apply to rage regeneration rate"] = { flag("ManaRegenToRageRegen") }, ["increases and reductions to maximum energy shield instead apply to ward"] = { flag("EnergyShieldToWard") }, ["(%d+)%% of damage taken bypasses ward"] = function(num) return { mod("WardBypass", "BASE", num) } end, + ["ward has a (%d+)%% chance to not break"] = function(num) return { mod("WardAvoidBreakChance", "BASE", num) } end, ["maximum energy shield is (%d+)"] = function(num) return { mod("EnergyShield", "OVERRIDE", num ) } end, ["while not on full life, sacrifice ([%d%.]+)%% of mana per second to recover that much life"] = function(num) return { mod("ManaDegenPercent", "BASE", num, { type = "Condition", var = "FullLife", neg = true }), @@ -4819,7 +4832,7 @@ local specialModList = { } end, ["phasing while on low life"] = { flag("Condition:Phasing", { type = "Condition", var = "LowLife" }) }, ["cannot be ignited while on low life"] = { flag("IgniteImmune", { type = "Condition", var = "LowLife" }), }, - ["ward does not break during f?l?a?s?k? ?effect"] = { flag("WardNotBreak", { type = "Condition", var = "UsingFlask" }) }, + ["ward does not break during f?l?a?s?k? ?effect"] = { flag("Condition:WardNotBreak", { type = "Condition", var = "UsingFlask" }), flag("Condition:UnbrokenWard", { type = "Condition", var = "UsingFlask" }) }, ["stun threshold is based on energy shield instead of life"] = { flag("StunThresholdBasedOnEnergyShieldInsteadOfLife"), mod("StunThresholdEnergyShieldPercent", "BASE", 100), @@ -5239,6 +5252,10 @@ local specialModList = { mod("ChaosMin", "BASE", 1, { type = "PercentStat", stat = "Ward", percent = num }), mod("ChaosMax", "BASE", 1, { type = "PercentStat", stat = "Ward", percent = num }), } end, + ["while you have unbroken ward, your next non%-channelling attack you use yourself breaks your ward to gain added cold damage equal to (%d+)%% of ward"] = function(num) return { + mod("ColdMin", "BASE", 1, { type = "PercentStat", stat = "Ward", percent = num }, { type = "SkillType", skillType = SkillType.Channel, neg = true }, { type = "SkillType", skillType = SkillType.Attack }, { type = "Condition", var = "WardNotBreak", neg = true }, { type = "Condition", var = "UnbrokenWard" }), + mod("ColdMax", "BASE", 1, { type = "PercentStat", stat = "Ward", percent = num }, { type = "SkillType", skillType = SkillType.Channel, neg = true }, { type = "SkillType", skillType = SkillType.Attack }, { type = "Condition", var = "WardNotBreak", neg = true }, { type = "Condition", var = "UnbrokenWard" }), + } end, ["spells deal added chaos damage equal to (%d+)%% of your maximum life"] = function(num) return { mod("ChaosMin", "BASE", 1, { type = "PercentStat", stat = "Life", percent = num }, { type = "SkillType", skillType = SkillType.Spell }), mod("ChaosMax", "BASE", 1, { type = "PercentStat", stat = "Life", percent = num }, { type = "SkillType", skillType = SkillType.Spell }), From 4ab39a89278e8d974732bc92ed6894f900206629 Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Tue, 5 May 2026 05:43:10 +1000 Subject: [PATCH 4/4] Olroth Bloodline Add support for Enhanced Starlight Damage taken bypasses Unbroken Ward if the Hit deals less Damage than 15% of Ward and Boon of the Sun 30% faster Restoration of Ward per Enemy Hit taken Recently Added a test for Enhanced Starlight --- spec/System/TestDefence_spec.lua | 25 +++++++++++++++++++++++++ src/Data/ModCache.lua | 5 ++--- src/Modules/CalcDefence.lua | 7 ++++++- src/Modules/ModParser.lua | 2 ++ 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/spec/System/TestDefence_spec.lua b/spec/System/TestDefence_spec.lua index 00b80cc13a..7510a22413 100644 --- a/spec/System/TestDefence_spec.lua +++ b/spec/System/TestDefence_spec.lua @@ -400,6 +400,31 @@ describe("TestDefence", function() assert.are.equals(1200, build.calcsTab.calcsOutput.PhysicalMaximumHitTaken) end) + it("small hits bypass unbroken ward", function() + build.configTab.input.enemyIsBoss = "None" + build.configTab.input.customMods = "\z + +940 to maximum life\n\z + +200 to Ward\n\z + Damage taken bypasses Unbroken Ward if the Hit deals less Damage than 15% of Ward\n\z + " + build.configTab:BuildModList() + runCallback("OnFrame") + assert.are.equals(350, build.calcsTab.calcsOutput.TotalEHP) + assert.are.equals(1200, build.calcsTab.calcsOutput.PhysicalMaximumHitTaken) + + local poolsRemaining = poolsRemainingAfterTypeMaxHit("Physical") + assert.are.equals(0, poolsRemaining.Ward) + assert.are.equals(0, poolsRemaining.Life) + + poolsRemaining = build.calcsTab.calcs.reducePoolsByDamage(nil, { Physical = 29 }, build.calcsTab.calcsEnv.player) + assert.are.equals(200, poolsRemaining.Ward) + assert.are.equals(971, poolsRemaining.Life) + + poolsRemaining = build.calcsTab.calcs.reducePoolsByDamage(nil, { Physical = 30 }, build.calcsTab.calcsEnv.player) + assert.are.equals(0, poolsRemaining.Ward) + assert.are.equals(1000, poolsRemaining.Life) + end) + -- fun part it("armoured max hits", function() build.configTab.input.enemyIsBoss = "None" diff --git a/src/Data/ModCache.lua b/src/Data/ModCache.lua index 4921a5c0f7..1b1bd2bf97 100755 --- a/src/Data/ModCache.lua +++ b/src/Data/ModCache.lua @@ -4753,7 +4753,7 @@ c["30% chance to take 50% less Area Damage from Hits"]={{[1]={flags=512,keywordF c["30% chance when you pay a Skill's Cost to gain that much Mana"]={{[1]={flags=0,keywordFlags=0,name="Mana",type="BASE",value=30}}," when you pay a Skill's Cost to gain that much "} c["30% chance when you pay a Skill's Cost to gain that much Mana +35 to maximum Energy Shield"]={{[1]={flags=0,keywordFlags=0,name="Mana",type="BASE",value=30}}," when you pay a Skill's Cost to gain that much +35 to maximum Energy Shield "} c["30% faster Restoration of Ward"]={{[1]={flags=0,keywordFlags=0,name="WardRechargeFaster",type="INC",value=30}},nil} -c["30% faster Restoration of Ward per Enemy Hit taken Recently"]={{[1]={flags=0,keywordFlags=0,name="WardRechargeFaster",type="INC",value=30}}," per Enemy Hit taken Recently "} +c["30% faster Restoration of Ward per Enemy Hit taken Recently"]={{[1]={[1]={type="Multiplier",var="BeenHitRecently"},flags=0,keywordFlags=0,name="WardRechargeFaster",type="INC",value=30}},nil} c["30% increased Accuracy Rating with Wands"]={{[1]={flags=8388612,keywordFlags=0,name="Accuracy",type="INC",value=30}},nil} c["30% increased Area Damage"]={{[1]={flags=512,keywordFlags=0,name="Damage",type="INC",value=30}},nil} c["30% increased Area of Effect"]={{[1]={flags=0,keywordFlags=0,name="AreaOfEffect",type="INC",value=30}},nil} @@ -8207,8 +8207,7 @@ c["Damage of Enemies Hitting you is Unlucky while you have a Magic Ring Equipped c["Damage of Enemies Hitting you is Unlucky while you have a Magic Ring Equipped You are Hexproof if you have a Magic Ring in right slot"]={nil,"Damage of Enemies Hitting you is Unlucky while you have a Magic Ring Equipped You are Hexproof if you have a Magic Ring in right slot "} c["Damage of Hits against you is Lucky"]={nil,"Damage of Hits is Lucky "} c["Damage over Time Multiplier for Ailments is equal to Critical Strike Multiplier"]={{[1]={flags=0,keywordFlags=0,name="DotMultiplierIsCritMultiplier",type="FLAG",value=true}},nil} -c["Damage taken bypasses Unbroken Ward if the Hit deals less Damage than 15% of Ward"]={nil,"Damage taken bypasses Unbroken Ward if the Hit deals less Damage than 15% of Ward "} -c["Damage taken bypasses Unbroken Ward if the Hit deals less Damage than 15% of Ward 70% increased Ward from Equipped Armour Items"]={nil,"Damage taken bypasses Unbroken Ward if the Hit deals less Damage than 15% of Ward 70% increased Ward from Equipped Armour Items "} +c["Damage taken bypasses Unbroken Ward if the Hit deals less Damage than 15% of Ward"]={{[1]={flags=0,keywordFlags=0,name="WardBypassBelowPercent",type="BASE",value=15}},nil} c["Damage taken from Blocked Hits cannot bypass Energy Shield"]={{[1]={[1]={neg=true,type="Condition",var="EVBypass"},flags=0,keywordFlags=0,name="BlockedDamageDoesntBypassES",type="FLAG",value=true}},nil} c["Damage taken from Unblocked hits always bypasses Energy Shield"]={{[1]={[1]={neg=true,type="Condition",var="EVBypass"},flags=0,keywordFlags=0,name="UnblockedDamageDoesBypassES",type="FLAG",value=true}},nil} c["Damage to Surrounding Targets"]={nil,"Damage to Surrounding Targets "} diff --git a/src/Modules/CalcDefence.lua b/src/Modules/CalcDefence.lua index b916523e5c..0b760234fd 100644 --- a/src/Modules/CalcDefence.lua +++ b/src/Modules/CalcDefence.lua @@ -165,8 +165,10 @@ function calcs.reducePoolsByDamage(poolTable, damageTable, actor) local modDB = actor.modDB local poolTbl = poolTable or { } + local damageTotal = 0 for damageType, damage in pairs(damageTable) do damageTable[damageType] = damage > 0 and m_ceil(damage) or nil + damageTotal = damageTotal + (damageTable[damageType] or 0) end local alliesTakenBeforeYou = poolTbl.AlliesTakenBeforeYou @@ -217,6 +219,7 @@ function calcs.reducePoolsByDamage(poolTable, damageTable, actor) local ward = poolTbl.Ward or output.Ward or 0 local wardActiveChance = poolTbl.WardActiveChance or (ward > 0 and 1 or 0) local wardAvoidBreakChance = modDB:Flag(nil, "Condition:WardNotBreak") and 1 or m_min(modDB:Sum("BASE", nil, "WardAvoidBreakChance") / 100, 1) + local wardBypassBelow = modDB:Sum("BASE", nil, "WardBypassBelowPercent") / 100 local energyShield = poolTbl.EnergyShield or output.EnergyShieldRecoveryCap local mana = poolTbl.Mana or output.ManaUnreserved or 0 @@ -227,6 +230,7 @@ function calcs.reducePoolsByDamage(poolTable, damageTable, actor) local overkillDamage = 0 ward = ward >= 0 and ward or 0 + local wardBeforeHit = ward energyShield = energyShield >= 0 and energyShield or 0 mana = mana >= 0 and mana or 0 life = life >= 0 and life or 0 @@ -297,9 +301,10 @@ function calcs.reducePoolsByDamage(poolTable, damageTable, actor) damageRemainder = damageRemainder - tempDamage resourcesLostToTypeDamage[damageType].sharedGuard = tempDamage >= 1 and tempDamage or nil end - if ward > 0 then + if ward > 0 and (wardBypassBelow == 0 or damageTotal >= wardBeforeHit * wardBypassBelow) then local tempDamage = m_min(damageRemainder * (1 - (modDB:Sum("BASE", nil, "WardBypass") or 0) / 100), ward) ward = ward - tempDamage + tempDamage = tempDamage * wardActiveChance damageRemainder = damageRemainder - tempDamage resourcesLostToTypeDamage[damageType].ward = tempDamage >= 1 and tempDamage or nil end diff --git a/src/Modules/ModParser.lua b/src/Modules/ModParser.lua index deedd18474..0dcc4cffaa 100644 --- a/src/Modules/ModParser.lua +++ b/src/Modules/ModParser.lua @@ -1852,6 +1852,7 @@ local modTagList = { ["branded enemy's"] = { tag = { type = "MultiplierThreshold", var = "BrandsAttachedToEnemy", threshold = 1 } }, ["to enemies they're attached to"] = { tag = { type = "MultiplierThreshold", var = "BrandsAttachedToEnemy", threshold = 1 } }, ["for each hit you've taken recently up to a maximum of (%d+)%%"] = function(num) return { tag = { type = "Multiplier", var = "BeenHitRecently", limit = num, limitTotal = true } } end, + ["per enemy hit taken recently"] = { tag = { type = "Multiplier", var = "BeenHitRecently" } }, ["for each nearby enemy, up to (%d+)%%"] = function(num) return { tag = { type = "Multiplier", var = "NearbyEnemies", limit = num, limitTotal = true } } end, ["for each nearby enemy, up to a maximum of (%d+)%%"] = function(num) return { tag = { type = "Multiplier", var = "NearbyEnemies", limit = num, limitTotal = true } } end, ["while you have iron reflexes"] = { tag = { type = "Condition", var = "HaveIronReflexes" } }, @@ -2313,6 +2314,7 @@ local specialModList = { ["increases and reductions to maximum energy shield instead apply to ward"] = { flag("EnergyShieldToWard") }, ["(%d+)%% of damage taken bypasses ward"] = function(num) return { mod("WardBypass", "BASE", num) } end, ["ward has a (%d+)%% chance to not break"] = function(num) return { mod("WardAvoidBreakChance", "BASE", num) } end, + ["damage taken bypasses unbroken ward if the hit deals less damage than (%d+)%% of ward"] = function(num) return { mod("WardBypassBelowPercent", "BASE", num) } end, ["maximum energy shield is (%d+)"] = function(num) return { mod("EnergyShield", "OVERRIDE", num ) } end, ["while not on full life, sacrifice ([%d%.]+)%% of mana per second to recover that much life"] = function(num) return { mod("ManaDegenPercent", "BASE", num, { type = "Condition", var = "FullLife", neg = true }),