From 6f5116a0f0f5e01d63e27dc5f191676e63998938 Mon Sep 17 00:00:00 2001 From: Tomas Slusny Date: Sat, 29 Jan 2022 01:31:59 +0100 Subject: [PATCH] Add support for Petrified Blood EHP This adds support for Petrified Blood life loss prevention to EHP calcs in similar fashion as Mind over Matter. The prevention gain is checked against being at low life or not, below low life it will use full value (e.g 40 base) above full life it will use halved value (Pathfinders for example utilize this mechanic and recover to full only via flasks). Signed-off-by: Tomas Slusny --- src/Data/Skills/act_str.lua | 3 +++ src/Export/Skills/act_str.txt | 3 +++ src/Modules/CalcDefence.lua | 51 ++++++++++++++++++++++++++++++----- src/Modules/CalcSections.lua | 22 +++++++++++++++ 4 files changed, 72 insertions(+), 7 deletions(-) diff --git a/src/Data/Skills/act_str.lua b/src/Data/Skills/act_str.lua index 88c631b923c..52ba7288eac 100644 --- a/src/Data/Skills/act_str.lua +++ b/src/Data/Skills/act_str.lua @@ -4929,6 +4929,9 @@ skills["PetrifiedBlood"] = { ["skill_grants_life_cost_%_mana_cost_while_not_on_low_life"] = { mod("ManaCostAsLifeCost", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff", unscalable = true }, { type="Condition", var="LowLife", neg = true }), }, + ["petrified_blood_%_life_loss_below_half_from_hit_to_prevent"] = { + mod("LifeLossBelowHalfPrevented", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff" }), + }, }, baseFlags = { }, diff --git a/src/Export/Skills/act_str.txt b/src/Export/Skills/act_str.txt index 43ea9e40411..46d5049def2 100644 --- a/src/Export/Skills/act_str.txt +++ b/src/Export/Skills/act_str.txt @@ -873,6 +873,9 @@ local skills, mod, flag, skill = ... ["skill_grants_life_cost_%_mana_cost_while_not_on_low_life"] = { mod("ManaCostAsLifeCost", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff", unscalable = true }, { type="Condition", var="LowLife", neg = true }), }, + ["petrified_blood_%_life_loss_below_half_from_hit_to_prevent"] = { + mod("LifeLossBelowHalfPrevented", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff" }), + }, }, #mods diff --git a/src/Modules/CalcDefence.lua b/src/Modules/CalcDefence.lua index fff9c69a8e4..54a98ac0482 100644 --- a/src/Modules/CalcDefence.lua +++ b/src/Modules/CalcDefence.lua @@ -825,18 +825,52 @@ function calcs.defence(env, actor) output[damageType.."EnergyShieldBypass"] = m_max(m_min(output[damageType.."EnergyShieldBypass"], 100), 0) end + + -- Prevented life loss (Petrified Blood) + output.AnyPreventedLifeLoss = false + for _, damageType in ipairs(dmgTypeList) do + preventedLifeLoss = modDB:Sum("BASE", nil, "LifeLossBelowHalfPrevented") + + if not modDB:Flag(nil, "Condition:LowLife") then + preventedLifeLoss = preventedLifeLoss / 2 + end + + output[damageType.."PreventedLifeLoss"] = preventedLifeLoss + if output[damageType.."PreventedLifeLoss"] > 0 then + output.AnyPreventedLifeLoss = true + local sourcePool = m_max(output.LifeUnreserved or 0, 0) + local poolProtected = sourcePool / (output[damageType.."PreventedLifeLoss"] / 100) * (1 - output[damageType.."PreventedLifeLoss"] / 100) + output[damageType.."PreventedEffectiveLife"] = m_max(sourcePool - poolProtected, 0) + m_min(sourcePool, poolProtected) / (1 - output[damageType.."PreventedLifeLoss"] / 100) + if breakdown then + if output[damageType.."PreventedLifeLoss"] then + breakdown[damageType.."PreventedLifeLoss"] = { + s_format("Total life protected:"), + s_format("%d ^8(%s)", sourcePool, "unreserved life"), + s_format("/ %.2f ^8(portion of damage prevented)", output[damageType.."PreventedLifeLoss"] / 100), + s_format("x %.2f ^8(portion taken from life)", 1 - output[damageType.."PreventedLifeLoss"] / 100), + s_format("= %d", poolProtected), + s_format("Effective life: %d", output[damageType.."PreventedEffectiveLife"]) + } + end + end + else + output[damageType.."PreventedEffectiveLife"] = output.LifeUnreserved + end + end + -- Guard output.AnyGuard = false for _, damageType in ipairs(dmgTypeList) do output[damageType.."GuardAbsorbRate"] = m_min(modDB:Sum("BASE", nil, "GuardAbsorbRate") + modDB:Sum("BASE", nil, damageType.."GuardAbsorbRate"), 100) if output[damageType.."GuardAbsorbRate"] > 0 then output.AnyGuard = true + local sourcePool = m_max(output[damageType.."PreventedEffectiveLife"] or 0, 0) output[damageType.."GuardAbsorb"] = calcLib.val(modDB, "GuardAbsorbLimit") + calcLib.val(modDB, damageType.."GuardAbsorbLimit") local lifeProtected = output[damageType.."GuardAbsorb"] / (output[damageType.."GuardAbsorbRate"] / 100) * (1 - output[damageType.."GuardAbsorbRate"] / 100) if output[damageType.."GuardAbsorbRate"] >= 100 then - output[damageType.."GuardEffectiveLife"] = output.LifeUnreserved + output[damageType.."GuardAbsorb"] + output[damageType.."GuardEffectiveLife"] = sourcePool + output[damageType.."GuardAbsorb"] else - output[damageType.."GuardEffectiveLife"] = m_max(output.LifeUnreserved - lifeProtected, 0) + m_min(output.LifeUnreserved, lifeProtected) / (1 - output[damageType.."GuardAbsorbRate"] / 100) + output[damageType.."GuardEffectiveLife"] = m_max(sourcePool - lifeProtected, 0) + m_min(sourcePool, lifeProtected) / (1 - output[damageType.."GuardAbsorbRate"] / 100) end if breakdown then breakdown[damageType.."GuardAbsorb"] = { @@ -845,11 +879,11 @@ function calcs.defence(env, actor) s_format("/ %.2f ^8(portion taken from guard)", output[damageType.."GuardAbsorbRate"] / 100), s_format("x %.2f ^8(portion taken from life and energy shield)", 1 - output[damageType.."GuardAbsorbRate"] / 100), s_format("= %d", lifeProtected), - s_format("Guard life protection: %d", output[damageType.."GuardEffectiveLife"] - output.LifeUnreserved) + s_format("Guard life protection: %d", output[damageType.."GuardEffectiveLife"] - sourcePool) } end else - output[damageType.."GuardEffectiveLife"] = output.LifeUnreserved + output[damageType.."GuardEffectiveLife"] = output[damageType.."PreventedEffectiveLife"] end end @@ -921,7 +955,7 @@ function calcs.defence(env, actor) output[damageType.."TotalPool"] = output[damageType.."TotalPool"] + (output.EnergyShieldRecoveryCap or output.EnergyShield) end if output[damageType.."GuardAbsorbRate"] > 0 then - local guardRemain = output[damageType.."GuardAbsorb"] - (output[damageType.."GuardEffectiveLife"] - output.LifeUnreserved) + local guardRemain = output[damageType.."GuardAbsorb"] - (output[damageType.."GuardEffectiveLife"] - output[damageType.."PreventedEffectiveLife"]) local espool = output[damageType.."TotalPool"] - output[damageType.."ManaEffectiveLife"] if guardRemain > 0 then local poolProtected = guardRemain / (output[damageType.."GuardAbsorbRate"] / 100) * (1 - output[damageType.."GuardAbsorbRate"] / 100) @@ -942,8 +976,11 @@ function calcs.defence(env, actor) breakdown[damageType.."TotalPool"] = { s_format("Life: %d", output.LifeUnreserved) } - if output[damageType.."GuardEffectiveLife"] ~= output.LifeUnreserved then - t_insert(breakdown[damageType.."TotalPool"], s_format("Guard skill: %d", output[damageType.."GuardEffectiveLife"] - output.LifeUnreserved + output[damageType.."GuardEffectivePool"])) + if output[damageType.."PreventedEffectiveLife"] ~= output.LifeUnreserved then + t_insert(breakdown[damageType.."TotalPool"], s_format("Damage prevented: %d", output[damageType.."PreventedEffectiveLife"] - output.LifeUnreserved)) + end + if output[damageType.."GuardEffectiveLife"] ~= output[damageType.."PreventedEffectiveLife"] then + t_insert(breakdown[damageType.."TotalPool"], s_format("Guard skill: %d", output[damageType.."GuardEffectiveLife"] - output[damageType.."PreventedEffectiveLife"] + output[damageType.."GuardEffectivePool"])) end if output[damageType.."ManaEffectiveLife"] ~= output[damageType.."GuardEffectiveLife"] then t_insert(breakdown[damageType.."TotalPool"], s_format("%s through MoM: %d", manatext, output[damageType.."ManaEffectiveLife"] - output[damageType.."GuardEffectiveLife"])) diff --git a/src/Modules/CalcSections.lua b/src/Modules/CalcSections.lua index c5df6006cd5..0875ee3e8b8 100644 --- a/src/Modules/CalcSections.lua +++ b/src/Modules/CalcSections.lua @@ -1579,6 +1579,28 @@ return { { modName = {"ChaosEnergyShieldBypass", "ChaosNotBypassEnergyShield", "BlockedDamageDoesntBypassES", "UnblockedDamageDoesBypassES"} }, }, }, + { label = "Prevented Life Loss", haveOutput = "AnyPreventedLifeLoss", + { format = "{0:output:PhysicalPreventedLifeLoss}", + { breakdown = "PhysicalPreventedLifeLoss" }, + { modName = "LifeLossBelowHalfPrevented" }, + }, + { format = "{0:output:LightningPreventedLifeLoss}", + { breakdown = "LightningPreventedLifeLoss" }, + { modName = "LifeLossBelowHalfPrevented" }, + }, + { format = "{0:output:ColdPreventedLifeLoss}", + { breakdown = "ColdPreventedLifeLoss" }, + { modName = "LifeLossBelowHalfPrevented" }, + }, + { format = "{0:output:FirePreventedLifeLoss}", + { breakdown = "FirePreventedLifeLoss" }, + { modName = "LifeLossBelowHalfPrevented" }, + }, + { format = "{0:output:ChaosPreventedLifeLoss}", + { breakdown = "ChaosPreventedLifeLoss" }, + { modName = "LifeLossBelowHalfPrevented" }, + }, + }, { label = "Guard", haveOutput = "AnyGuard", { format = "{0:output:PhysicalGuardAbsorb}", { breakdown = "PhysicalGuardAbsorb" },