Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions spec/System/TestDefence_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,53 @@ 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)

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"
Expand Down
8 changes: 8 additions & 0 deletions src/Classes/ItemsTab.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
31 changes: 15 additions & 16 deletions src/Data/ModCache.lua

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions src/Data/SkillStatMap.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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 }),
},
Expand Down
23 changes: 17 additions & 6 deletions src/Modules/CalcDefence.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -215,7 +217,9 @@ 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 wardBypassBelow = modDB:Sum("BASE", nil, "WardBypassBelowPercent") / 100

local energyShield = poolTbl.EnergyShield or output.EnergyShieldRecoveryCap
local mana = poolTbl.Mana or output.ManaUnreserved or 0
Expand All @@ -226,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
Expand Down Expand Up @@ -296,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
Expand Down Expand Up @@ -395,7 +401,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),
Expand Down Expand Up @@ -2538,15 +2545,18 @@ 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
end

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 = { }
Expand Down Expand Up @@ -2658,7 +2668,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
Expand Down
10 changes: 10 additions & 0 deletions src/Modules/CalcPerform.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions src/Modules/CalcTriggers.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/Modules/ConfigOptions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down
Loading
Loading