Improve MoM+EB(+ES bypass) calculations (#8590)
This commit is contained in:
@@ -840,4 +840,188 @@ describe("TestDefence", function()
|
||||
assert.are.equals(0, floor(poolsRemaining.Life))
|
||||
assert.are.equals(0, floor(poolsRemaining.OverkillDamage))
|
||||
end)
|
||||
|
||||
it("MoM + EB", function()
|
||||
build.configTab.input.enemyIsBoss = "None"
|
||||
-- enough mana and es, 0% and 100% bypass
|
||||
build.configTab.input.customMods = [[
|
||||
50% of damage is taken from mana before life
|
||||
energy shield protects mana instead of life
|
||||
+40 to maximum life
|
||||
+1960 to mana
|
||||
+2000 to energy shield
|
||||
You have no intelligence
|
||||
+60% to all resistances
|
||||
]]
|
||||
build.configTab:BuildModList()
|
||||
runCallback("OnFrame")
|
||||
assert.are.equals(200, build.calcsTab.calcsOutput.FireMaximumHitTaken)
|
||||
assert.are.equals(200, build.calcsTab.calcsOutput.ChaosMaximumHitTaken)
|
||||
local poolsRemaining = poolsRemainingAfterTypeMaxHit("Chaos")
|
||||
assert.are.equals(2000, round(poolsRemaining.EnergyShield))
|
||||
assert.are.equals(1900, round(poolsRemaining.Mana))
|
||||
assert.are.equals(0, floor(poolsRemaining.Life))
|
||||
assert.are.equals(0, floor(poolsRemaining.OverkillDamage))
|
||||
poolsRemaining = poolsRemainingAfterTypeMaxHit("Fire")
|
||||
assert.are.equals(1900, round(poolsRemaining.EnergyShield))
|
||||
assert.are.equals(2000, round(poolsRemaining.Mana))
|
||||
assert.are.equals(0, floor(poolsRemaining.Life))
|
||||
assert.are.equals(0, floor(poolsRemaining.OverkillDamage))
|
||||
|
||||
-- enough mana and es, 50% bypass
|
||||
build.configTab.input.customMods = [[
|
||||
50% of damage is taken from mana before life
|
||||
energy shield protects mana instead of life
|
||||
50% of non-chaos damage taken bypasses energy shield
|
||||
+40 to maximum life
|
||||
+1960 to mana
|
||||
+2000 to energy shield
|
||||
You have no intelligence
|
||||
+60% to all resistances
|
||||
]]
|
||||
build.configTab:BuildModList()
|
||||
runCallback("OnFrame")
|
||||
assert.are.equals(200, build.calcsTab.calcsOutput.FireMaximumHitTaken)
|
||||
poolsRemaining = poolsRemainingAfterTypeMaxHit("Fire")
|
||||
assert.are.equals(1950, round(poolsRemaining.EnergyShield))
|
||||
assert.are.equals(1950, round(poolsRemaining.Mana))
|
||||
assert.are.equals(0, floor(poolsRemaining.Life))
|
||||
assert.are.equals(0, floor(poolsRemaining.OverkillDamage))
|
||||
|
||||
-- es bottleneck, 0% and 100% bypass
|
||||
build.configTab.input.customMods = [[
|
||||
50% of damage is taken from mana before life
|
||||
energy shield protects mana instead of life
|
||||
+40 to maximum life
|
||||
+1960 to mana
|
||||
+50 to energy shield
|
||||
You have no intelligence
|
||||
+60% to all resistances
|
||||
]]
|
||||
build.configTab:BuildModList()
|
||||
runCallback("OnFrame")
|
||||
assert.are.equals(200, build.calcsTab.calcsOutput.FireMaximumHitTaken)
|
||||
assert.are.equals(200, build.calcsTab.calcsOutput.ChaosMaximumHitTaken)
|
||||
poolsRemaining = poolsRemainingAfterTypeMaxHit("Chaos")
|
||||
assert.are.equals(50, round(poolsRemaining.EnergyShield))
|
||||
assert.are.equals(1900, round(poolsRemaining.Mana))
|
||||
assert.are.equals(0, floor(poolsRemaining.Life))
|
||||
assert.are.equals(0, floor(poolsRemaining.OverkillDamage))
|
||||
poolsRemaining = poolsRemainingAfterTypeMaxHit("Fire")
|
||||
assert.are.equals(0, round(poolsRemaining.EnergyShield))
|
||||
assert.are.equals(1950, round(poolsRemaining.Mana))
|
||||
assert.are.equals(0, floor(poolsRemaining.Life))
|
||||
assert.are.equals(0, floor(poolsRemaining.OverkillDamage))
|
||||
|
||||
-- es bottleneck, 50% bypass
|
||||
build.configTab.input.customMods = [[
|
||||
50% of damage is taken from mana before life
|
||||
energy shield protects mana instead of life
|
||||
50% of non-chaos damage taken bypasses energy shield
|
||||
+40 to maximum life
|
||||
+1960 to mana
|
||||
+40 to energy shield
|
||||
You have no intelligence
|
||||
+60% to all resistances
|
||||
]]
|
||||
build.configTab:BuildModList()
|
||||
runCallback("OnFrame")
|
||||
assert.are.equals(200, build.calcsTab.calcsOutput.FireMaximumHitTaken)
|
||||
poolsRemaining = poolsRemainingAfterTypeMaxHit("Fire")
|
||||
assert.are.equals(0, round(poolsRemaining.EnergyShield))
|
||||
assert.are.equals(1940, round(poolsRemaining.Mana))
|
||||
assert.are.equals(0, floor(poolsRemaining.Life))
|
||||
assert.are.equals(0, floor(poolsRemaining.OverkillDamage))
|
||||
|
||||
-- mana bottleneck, 0% and 100% bypass
|
||||
build.configTab.input.customMods = [[
|
||||
50% of damage is taken from mana before life
|
||||
energy shield protects mana instead of life
|
||||
+40 to maximum life
|
||||
+2000 to energy shield
|
||||
You have no intelligence
|
||||
+60% to all resistances
|
||||
]]
|
||||
build.configTab:BuildModList()
|
||||
runCallback("OnFrame")
|
||||
assert.are.equals(200, build.calcsTab.calcsOutput.FireMaximumHitTaken)
|
||||
assert.are.equals(140, build.calcsTab.calcsOutput.ChaosMaximumHitTaken)
|
||||
poolsRemaining = poolsRemainingAfterTypeMaxHit("Chaos")
|
||||
assert.are.equals(2000, round(poolsRemaining.EnergyShield))
|
||||
assert.are.equals(0, round(poolsRemaining.Mana))
|
||||
assert.are.equals(0, floor(poolsRemaining.Life))
|
||||
assert.are.equals(0, floor(poolsRemaining.OverkillDamage))
|
||||
poolsRemaining = poolsRemainingAfterTypeMaxHit("Fire")
|
||||
assert.are.equals(1900, round(poolsRemaining.EnergyShield))
|
||||
assert.are.equals(40, round(poolsRemaining.Mana))
|
||||
assert.are.equals(0, floor(poolsRemaining.Life))
|
||||
assert.are.equals(0, floor(poolsRemaining.OverkillDamage))
|
||||
|
||||
-- mana bottleneck, 50% bypass
|
||||
-- taking 160 damage in this scenario:
|
||||
-- 160 damage is split to 80 damage straight to life, 80 damage to MoM pools
|
||||
-- 50% of the 80 MoM pool damage is taken by ES and 50% bypasses
|
||||
-- pool of 20 mana takes 40 damage, gets depleted and the remaining damage continues on to life, for a total of 100 damage to life
|
||||
build.configTab.input.customMods = [[
|
||||
50% of damage is taken from mana before life
|
||||
energy shield protects mana instead of life
|
||||
50% of non-chaos damage taken bypasses energy shield
|
||||
+40 to maximum life
|
||||
-20 to mana
|
||||
+2000 to energy shield
|
||||
You have no intelligence
|
||||
+60% to all resistances
|
||||
]]
|
||||
build.configTab:BuildModList()
|
||||
runCallback("OnFrame")
|
||||
assert.are.equals(160, build.calcsTab.calcsOutput.FireMaximumHitTaken)
|
||||
poolsRemaining = poolsRemainingAfterTypeMaxHit("Fire")
|
||||
assert.are.equals(1960, round(poolsRemaining.EnergyShield))
|
||||
assert.are.equals(0, round(poolsRemaining.Mana))
|
||||
assert.are.equals(0, floor(poolsRemaining.Life))
|
||||
assert.are.equals(0, floor(poolsRemaining.OverkillDamage))
|
||||
|
||||
-- mana+es bottleneck, 0% and 100% bypass
|
||||
build.configTab.input.customMods = [[
|
||||
50% of damage is taken from mana before life
|
||||
energy shield protects mana instead of life
|
||||
+940 to maximum life
|
||||
+50 to energy shield
|
||||
You have no intelligence
|
||||
+60% to all resistances
|
||||
]]
|
||||
build.configTab:BuildModList()
|
||||
runCallback("OnFrame")
|
||||
assert.are.equals(1090, build.calcsTab.calcsOutput.FireMaximumHitTaken)
|
||||
assert.are.equals(1040, build.calcsTab.calcsOutput.ChaosMaximumHitTaken)
|
||||
poolsRemaining = poolsRemainingAfterTypeMaxHit("Chaos")
|
||||
assert.are.equals(50, round(poolsRemaining.EnergyShield))
|
||||
assert.are.equals(0, round(poolsRemaining.Mana))
|
||||
assert.are.equals(0, floor(poolsRemaining.Life))
|
||||
assert.are.equals(0, floor(poolsRemaining.OverkillDamage))
|
||||
poolsRemaining = poolsRemainingAfterTypeMaxHit("Fire")
|
||||
assert.are.equals(0, round(poolsRemaining.EnergyShield))
|
||||
assert.are.equals(0, round(poolsRemaining.Mana))
|
||||
assert.are.equals(0, floor(poolsRemaining.Life))
|
||||
assert.are.equals(0, floor(poolsRemaining.OverkillDamage))
|
||||
|
||||
-- mana+es bottleneck, 50% bypass
|
||||
build.configTab.input.customMods = [[
|
||||
50% of damage is taken from mana before life
|
||||
energy shield protects mana instead of life
|
||||
50% of non-chaos damage taken bypasses energy shield
|
||||
+940 to maximum life
|
||||
+50 to energy shield
|
||||
You have no intelligence
|
||||
+60% to all resistances
|
||||
]]
|
||||
build.configTab:BuildModList()
|
||||
runCallback("OnFrame")
|
||||
assert.are.equals(1090, build.calcsTab.calcsOutput.FireMaximumHitTaken)
|
||||
poolsRemaining = poolsRemainingAfterTypeMaxHit("Fire")
|
||||
assert.are.equals(0, round(poolsRemaining.EnergyShield))
|
||||
assert.are.equals(0, round(poolsRemaining.Mana))
|
||||
assert.are.equals(0, floor(poolsRemaining.Life))
|
||||
assert.are.equals(0, floor(poolsRemaining.OverkillDamage))
|
||||
end)
|
||||
end)
|
||||
@@ -215,10 +215,33 @@ function calcs.reducePoolsByDamage(poolTable, damageTable, actor)
|
||||
local LifeLossLostOverTime = poolTbl.LifeLossLostOverTime or 0
|
||||
local LifeBelowHalfLossLostOverTime = poolTbl.LifeBelowHalfLossLostOverTime or 0
|
||||
local overkillDamage = 0
|
||||
|
||||
ward = ward >= 0 and ward or 0
|
||||
energyShield = energyShield >= 0 and energyShield or 0
|
||||
mana = mana >= 0 and mana or 0
|
||||
life = life >= 0 and life or 0
|
||||
|
||||
-- Initializing MoM(+EB(+Bypass)) pools here saves logic later to avoid overusing pools for every damageType
|
||||
local resourcesLostToTypeDamage = { Physical = { }, Lightning = { }, Cold = { }, Fire = { }, Chaos = { } }
|
||||
local MoMPoolRemaining = m_huge
|
||||
local esPoolRemaining = m_huge
|
||||
|
||||
local lifeHitPoolInitial = calcLifeHitPoolWithLossPrevention(life, output.Life, output.preventedLifeLoss, lifeLossBelowHalfPrevented)
|
||||
local MoMPoolsRemaining = { }
|
||||
local esPoolsRemaining = { }
|
||||
for damageType, _ in pairs(damageTable) do
|
||||
local MoMEffect = m_min(output.sharedMindOverMatter + output[damageType.."MindOverMatter"], 100) / 100
|
||||
local maxMoM = lifeHitPoolInitial / (1 - MoMEffect) - lifeHitPoolInitial
|
||||
local MoMPool = MoMEffect < 1 and m_min(maxMoM, mana) or mana
|
||||
local lifePlusMoMHitPool = lifeHitPoolInitial + MoMPool
|
||||
local esBypass = output[damageType.."EnergyShieldBypass"] / 100 or 0
|
||||
local esPool = esBypass > 0 and m_min(lifePlusMoMHitPool / esBypass - lifePlusMoMHitPool, energyShield) or energyShield
|
||||
if modDB:Flag(nil, "EnergyShieldProtectsMana") and esBypass < 1 then
|
||||
esPoolsRemaining[damageType] = m_floor(m_min(maxMoM * (1 - esBypass), energyShield, lifePlusMoMHitPool / (1 - (1 - esBypass) * MoMEffect) - lifePlusMoMHitPool))
|
||||
MoMPoolsRemaining[damageType] = m_floor(m_min(maxMoM - esPoolsRemaining[damageType], MoMPool))
|
||||
else
|
||||
esPoolsRemaining[damageType] = m_floor(esPool)
|
||||
MoMPoolsRemaining[damageType] = m_floor(MoMPool)
|
||||
end
|
||||
end
|
||||
|
||||
for _, damageType in ipairs(dmgTypeList) do
|
||||
local damageRemainder = damageTable[damageType]
|
||||
if damageRemainder then
|
||||
@@ -270,39 +293,37 @@ function calcs.reducePoolsByDamage(poolTable, damageTable, actor)
|
||||
damageRemainder = damageRemainder - tempDamage
|
||||
resourcesLostToTypeDamage[damageType].ward = tempDamage >= 1 and tempDamage or nil
|
||||
end
|
||||
local esBypass = output[damageType.."EnergyShieldBypass"] / 100 or 0
|
||||
local lifeHitPool = calcLifeHitPoolWithLossPrevention(life, output.Life, output.preventedLifeLoss, lifeLossBelowHalfPrevented)
|
||||
local MoMEffect = m_min(output.sharedMindOverMatter + output[damageType.."MindOverMatter"], 100) / 100
|
||||
local MoMPool = MoMEffect < 1 and m_min(lifeHitPool / (1 - MoMEffect) - lifeHitPool, mana) or mana
|
||||
local lifePlusMoMHitPool = lifeHitPool + MoMPool
|
||||
local esBypass = output[damageType.."EnergyShieldBypass"] / 100 or 0
|
||||
local esPool = esPoolsRemaining[damageType]
|
||||
if energyShield > 0 and not modDB:Flag(nil, "EnergyShieldProtectsMana") and esBypass < 1 then
|
||||
local esPool = esBypass > 0 and m_min(lifePlusMoMHitPool / esBypass - lifePlusMoMHitPool, energyShield) or energyShield
|
||||
local tempDamage = m_min(damageRemainder * (1 - esBypass), esPool)
|
||||
esPoolRemaining = m_min(esPoolRemaining, esPool - tempDamage)
|
||||
for damageType2, _ in pairs(damageTable) do
|
||||
esPoolsRemaining[damageType2] = m_max(esPoolsRemaining[damageType2] - tempDamage, 0)
|
||||
end
|
||||
energyShield = energyShield - tempDamage
|
||||
damageRemainder = damageRemainder - tempDamage
|
||||
resourcesLostToTypeDamage[damageType].energyShield = tempDamage >= 1 and tempDamage or nil
|
||||
elseif esBypass == 1 then
|
||||
esPoolRemaining = 0
|
||||
end
|
||||
if MoMEffect > 0 and mana > 0 then
|
||||
local MoMDamage = damageRemainder * MoMEffect
|
||||
if MoMEffect > 0 then
|
||||
local MoMDamage = m_ceil(damageRemainder * MoMEffect)
|
||||
if modDB:Flag(nil, "EnergyShieldProtectsMana") and energyShield > 0 and esBypass < 1 then
|
||||
local MoMEBPool = esBypass > 0 and m_min(MoMPool / esBypass - MoMPool, energyShield) or energyShield
|
||||
local tempDamage = m_min(MoMDamage * (1 - esBypass), MoMEBPool)
|
||||
esPoolRemaining = m_min(esPoolRemaining, MoMEBPool - tempDamage)
|
||||
local tempDamage = m_ceil(m_min(MoMDamage * (1 - esBypass), esPool))
|
||||
for damageType2, _ in pairs(damageTable) do
|
||||
esPoolsRemaining[damageType2] = m_floor(m_max(esPoolsRemaining[damageType2] - tempDamage, 0))
|
||||
end
|
||||
energyShield = energyShield - tempDamage
|
||||
damageRemainder = damageRemainder - tempDamage
|
||||
MoMDamage = MoMDamage - tempDamage
|
||||
resourcesLostToTypeDamage[damageType].energyShield = tempDamage >= 1 and tempDamage or nil
|
||||
end
|
||||
local tempDamage = m_min(MoMDamage, MoMPool)
|
||||
MoMPoolRemaining = m_min(MoMPoolRemaining, MoMPool - tempDamage)
|
||||
local tempDamage = m_ceil(m_min(MoMDamage, MoMPoolsRemaining[damageType]))
|
||||
for damageType2, _ in pairs(damageTable) do
|
||||
MoMPoolsRemaining[damageType2] = m_floor(m_max(MoMPoolsRemaining[damageType2] - tempDamage, 0))
|
||||
end
|
||||
mana = mana - tempDamage
|
||||
damageRemainder = damageRemainder - tempDamage
|
||||
resourcesLostToTypeDamage[damageType].mana = tempDamage >= 1 and tempDamage or nil
|
||||
else
|
||||
MoMPoolRemaining = 0
|
||||
end
|
||||
if output.preventedLifeLossTotal > 0 then
|
||||
local halfLife = output.Life * 0.5
|
||||
@@ -351,7 +372,14 @@ function calcs.reducePoolsByDamage(poolTable, damageTable, actor)
|
||||
resourcesLostToTypeDamage[damageType].overkill = damageRemainder >= 1 and damageRemainder or nil
|
||||
end
|
||||
end
|
||||
local hitPoolRemaining = calcLifeHitPoolWithLossPrevention(life, output.Life, output.preventedLifeLoss, lifeLossBelowHalfPrevented) +
|
||||
|
||||
local MoMPoolRemaining = m_huge
|
||||
local esPoolRemaining = m_huge
|
||||
for damageType, _ in pairs(damageTable) do
|
||||
MoMPoolRemaining = m_min(MoMPoolRemaining, MoMPoolsRemaining[damageType])
|
||||
esPoolRemaining = m_min(esPoolRemaining, esPoolsRemaining[damageType])
|
||||
end
|
||||
local hitPoolRemaining = (life >= 1 and calcLifeHitPoolWithLossPrevention(life, output.Life, output.preventedLifeLoss, lifeLossBelowHalfPrevented) or 0) +
|
||||
(MoMPoolRemaining ~= m_huge and MoMPoolRemaining or 0) + (esPoolRemaining ~= m_huge and esPoolRemaining or 0)
|
||||
|
||||
return {
|
||||
@@ -2210,43 +2238,39 @@ function calcs.buildDefenceEstimations(env, actor)
|
||||
output.OnlySharedMindOverMatter = false
|
||||
output.AnySpecificMindOverMatter = false
|
||||
output["sharedMindOverMatter"] = m_min(modDB:Sum("BASE", nil, "DamageTakenFromManaBeforeLife"), 100)
|
||||
local function calcMoMEBPool(lifePool, MoMEffect, esBypass)
|
||||
local mana = m_max(output.ManaUnreserved or 0, 0)
|
||||
local maxMoMPool = MoMEffect < 1 and lifePool / (1 - MoMEffect) - lifePool or m_huge
|
||||
local maxManaUsable = m_floor(m_min(mana, maxMoMPool))
|
||||
local maxESUsable = modDB:Flag(nil, "EnergyShieldProtectsMana") and esBypass < 1 and
|
||||
m_floor(m_min(
|
||||
output.EnergyShieldRecoveryCap,
|
||||
maxMoMPool * (1 - esBypass),
|
||||
(lifePool + maxManaUsable) / (1 - (1 - esBypass) * MoMEffect) - (lifePool + maxManaUsable)
|
||||
)) or 0
|
||||
local manaUsed = m_floor(m_min(maxMoMPool - maxESUsable, maxManaUsable))
|
||||
return lifePool + manaUsed + maxESUsable, maxManaUsable, manaUsed, maxESUsable
|
||||
end
|
||||
if output["sharedMindOverMatter"] > 0 then
|
||||
output.OnlySharedMindOverMatter = true
|
||||
local sourcePool = m_max(output.ManaUnreserved or 0, 0)
|
||||
local sourceHitPool = sourcePool
|
||||
local manatext = "unreserved mana"
|
||||
local MindOverMatter = output["sharedMindOverMatter"] / 100
|
||||
local esBypass = output.MinimumBypass / 100
|
||||
if modDB:Flag(nil, "EnergyShieldProtectsMana") and esBypass < 1 then
|
||||
manatext = manatext.." + non-bypassed energy shield"
|
||||
if esBypass > 0 then
|
||||
local manaProtected = m_min(sourcePool / esBypass - sourcePool, output.EnergyShieldRecoveryCap)
|
||||
sourcePool = m_max(sourcePool - manaProtected, -output.LifeRecoverable) + m_min(sourcePool + output.LifeRecoverable, manaProtected) / esBypass
|
||||
sourceHitPool = m_max(sourceHitPool - manaProtected, -output.LifeHitPool) + m_min(sourceHitPool + output.LifeHitPool, manaProtected) / esBypass
|
||||
else
|
||||
sourcePool = sourcePool + output.EnergyShieldRecoveryCap
|
||||
sourceHitPool = sourcePool
|
||||
end
|
||||
end
|
||||
local poolProtected = sourcePool / (output["sharedMindOverMatter"] / 100) * (1 - output["sharedMindOverMatter"] / 100)
|
||||
local hitPoolProtected = sourceHitPool / (output["sharedMindOverMatter"] / 100) * (1 - output["sharedMindOverMatter"] / 100)
|
||||
if output["sharedMindOverMatter"] >= 100 then
|
||||
poolProtected = m_huge
|
||||
output["sharedManaEffectiveLife"] = output.LifeRecoverable + sourcePool
|
||||
output["sharedMoMHitPool"] = output.LifeHitPool + sourceHitPool
|
||||
else
|
||||
output["sharedManaEffectiveLife"] = m_max(output.LifeRecoverable - poolProtected, 0) + m_min(output.LifeRecoverable, poolProtected) / (1 - output["sharedMindOverMatter"] / 100)
|
||||
output["sharedMoMHitPool"] = m_max(output.LifeHitPool - hitPoolProtected, 0) + m_min(output.LifeHitPool, hitPoolProtected) / (1 - output["sharedMindOverMatter"] / 100)
|
||||
end
|
||||
|
||||
local sharedMoMPool, breakdownMaxMana, breakdownMana, breakdownES = calcMoMEBPool(output.LifeRecoverable, MindOverMatter, esBypass)
|
||||
output["sharedManaEffectiveLife"] = sharedMoMPool
|
||||
output["sharedMoMHitPool"] = calcMoMEBPool(output.LifeHitPool, MindOverMatter, esBypass)
|
||||
|
||||
if breakdown then
|
||||
if output["sharedMindOverMatter"] then
|
||||
local lifeProtected = (breakdownMana + breakdownES) / MindOverMatter - (breakdownMana + breakdownES)
|
||||
breakdown["sharedMindOverMatter"] = {
|
||||
s_format("Total life protected:"),
|
||||
s_format("%d ^8(%s)", sourcePool, manatext),
|
||||
s_format("/ %.2f ^8(portion taken from mana)", output["sharedMindOverMatter"] / 100),
|
||||
s_format("x %.2f ^8(portion taken from life)", 1 - output["sharedMindOverMatter"] / 100),
|
||||
s_format("= %d", poolProtected),
|
||||
s_format("Effective life: %d", output["sharedManaEffectiveLife"])
|
||||
s_format("Effective life from Mind over Matter:"),
|
||||
s_format("%d life (%d life is protected)", output.LifeRecoverable, lifeProtected),
|
||||
s_format("%d mana%s", breakdownMana, breakdownMana == breakdownMaxMana and "" or s_format(" (%d mana shadowed by energy shield)", breakdownMaxMana - breakdownMana)),
|
||||
}
|
||||
if modDB:Flag(nil, "EnergyShieldProtectsMana") then t_insert(breakdown["sharedMindOverMatter"], s_format("%d energy shield", breakdownES)) end
|
||||
t_insert(breakdown["sharedMindOverMatter"], s_format("%d + %d %s= %d", output.LifeRecoverable, breakdownMana,
|
||||
modDB:Flag(nil, "EnergyShieldProtectsMana") and s_format("+ %d ", breakdownES) or "", sharedMoMPool)
|
||||
)
|
||||
end
|
||||
end
|
||||
else
|
||||
@@ -2256,44 +2280,28 @@ function calcs.buildDefenceEstimations(env, actor)
|
||||
for _, damageType in ipairs(dmgTypeList) do
|
||||
output[damageType.."MindOverMatter"] = m_min(modDB:Sum("BASE", nil, damageType.."DamageTakenFromManaBeforeLife"), 100 - output["sharedMindOverMatter"])
|
||||
if output[damageType.."MindOverMatter"] > 0 or (output[damageType.."EnergyShieldBypass"] > output.MinimumBypass and output["sharedMindOverMatter"] > 0) then
|
||||
local MindOverMatter = output[damageType.."MindOverMatter"] + output["sharedMindOverMatter"]
|
||||
output.ehpSectionAnySpecificTypes = true
|
||||
output.AnySpecificMindOverMatter = true
|
||||
output.OnlySharedMindOverMatter = false
|
||||
local sourcePool = m_max(output.ManaUnreserved or 0, 0)
|
||||
local sourceHitPool = sourcePool
|
||||
local manatext = "unreserved mana"
|
||||
if modDB:Flag(nil, "EnergyShieldProtectsMana") and output[damageType.."EnergyShieldBypass"] < 100 then
|
||||
manatext = manatext.." + non-bypassed energy shield"
|
||||
if output[damageType.."EnergyShieldBypass"] > 0 then
|
||||
local manaProtected = output.EnergyShieldRecoveryCap / (1 - output[damageType.."EnergyShieldBypass"] / 100) * (output[damageType.."EnergyShieldBypass"] / 100)
|
||||
sourcePool = m_max(sourcePool - manaProtected, -output.LifeRecoverable) + m_min(sourcePool + output.LifeRecoverable, manaProtected) / (output[damageType.."EnergyShieldBypass"] / 100)
|
||||
sourceHitPool = m_max(sourceHitPool - manaProtected, -output.LifeHitPool) + m_min(sourceHitPool + output.LifeHitPool, manaProtected) / (output[damageType.."EnergyShieldBypass"] / 100)
|
||||
else
|
||||
sourcePool = sourcePool + output.EnergyShieldRecoveryCap
|
||||
sourceHitPool = sourcePool
|
||||
end
|
||||
end
|
||||
local poolProtected = sourcePool / (MindOverMatter / 100) * (1 - MindOverMatter / 100)
|
||||
local hitPoolProtected = sourceHitPool / (MindOverMatter / 100) * (1 - MindOverMatter / 100)
|
||||
if MindOverMatter >= 100 then
|
||||
poolProtected = m_huge
|
||||
output[damageType.."ManaEffectiveLife"] = output.LifeRecoverable + sourcePool
|
||||
output[damageType.."MoMHitPool"] = output.LifeHitPool + sourceHitPool
|
||||
else
|
||||
output[damageType.."ManaEffectiveLife"] = m_max(output.LifeRecoverable - poolProtected, 0) + m_min(output.LifeRecoverable, poolProtected) / (1 - MindOverMatter / 100)
|
||||
output[damageType.."MoMHitPool"] = m_max(output.LifeHitPool - hitPoolProtected, 0) + m_min(output.LifeHitPool, hitPoolProtected) / (1 - MindOverMatter / 100)
|
||||
end
|
||||
local MindOverMatter = (output[damageType.."MindOverMatter"] + output["sharedMindOverMatter"]) / 100
|
||||
local esBypass = output[damageType.."EnergyShieldBypass"] / 100
|
||||
|
||||
local typedMoMPool, breakdownMaxMana, breakdownMana, breakdownES = calcMoMEBPool(output.LifeRecoverable, MindOverMatter, esBypass)
|
||||
output[damageType.."ManaEffectiveLife"] = typedMoMPool
|
||||
output[damageType.."MoMHitPool"] = calcMoMEBPool(output.LifeHitPool, MindOverMatter, esBypass)
|
||||
|
||||
if breakdown then
|
||||
if output[damageType.."MindOverMatter"] then
|
||||
local lifeProtected = (breakdownMana + breakdownES) / MindOverMatter - (breakdownMana + breakdownES)
|
||||
breakdown[damageType.."MindOverMatter"] = {
|
||||
s_format("Total life protected:"),
|
||||
s_format("%d ^8(%s)", sourcePool, manatext),
|
||||
s_format("/ %.2f ^8(portion taken from mana)", MindOverMatter / 100),
|
||||
s_format("x %.2f ^8(portion taken from life)", 1 - MindOverMatter / 100),
|
||||
s_format("= %d", poolProtected),
|
||||
s_format("Effective life: %d", output[damageType.."ManaEffectiveLife"])
|
||||
s_format("Effective life from Mind over Matter:"),
|
||||
s_format("%d life (%d life is protected)", output.LifeRecoverable, lifeProtected),
|
||||
s_format("%d mana%s", breakdownMana, breakdownMana == breakdownMaxMana and "" or s_format(" (%d mana shadowed by energy shield)", breakdownMaxMana - breakdownMana)),
|
||||
}
|
||||
if modDB:Flag(nil, "EnergyShieldProtectsMana") then t_insert(breakdown[damageType.."MindOverMatter"], s_format("%d energy shield", breakdownES)) end
|
||||
t_insert(breakdown[damageType.."MindOverMatter"], s_format("%d + %d %s= %d", output.LifeRecoverable, breakdownMana,
|
||||
modDB:Flag(nil, "EnergyShieldProtectsMana") and s_format("+ %d ", breakdownES) or "", typedMoMPool)
|
||||
)
|
||||
end
|
||||
end
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user