Skip to content
Merged
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
4 changes: 3 additions & 1 deletion src/Modules/BuildDisplayStats.lua
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,15 @@ local displayStats = {
{ stat = "NetManaRegen", label = "Net Mana Recovery", fmt = "+.1f", color = colorCodes.MANA },
{ stat = "NetEnergyShieldRegen", label = "Net ES Recovery", fmt = "+.1f", color = colorCodes.ES },
{ },
{ stat = "Evasion", label = "Evasion rating", fmt = "d", color = colorCodes.EVASION, compPercent = true },
{ stat = "Evasion", label = "Evasion Rating", fmt = "d", color = colorCodes.EVASION, compPercent = true },
{ stat = "Spec:EvasionInc", label = "%Inc Evasion from Tree", color = colorCodes.EVASION, fmt = "d%%" },
{ stat = "EvadeChance", label = "Evade Chance", fmt = "d%%", color = colorCodes.EVASION, condFunc = function(v,o) return v > 0 and o.noSplitEvade end },
{ stat = "MeleeEvadeChance", label = "Melee Evade Chance", fmt = "d%%", color = colorCodes.EVASION, condFunc = function(v,o) return v > 0 and o.splitEvade end },
{ stat = "ProjectileEvadeChance", label = "Projectile Evade Chance", fmt = "d%%", color = colorCodes.EVASION, condFunc = function(v,o) return v > 0 and o.splitEvade end },
{ stat = "SpellEvadeChance", label = "Spell Evade Chance", fmt = "d%%", color = colorCodes.EVASION, condFunc = function(v,o) return v > 0 and o.splitEvade end },
{ stat = "SpellProjectileEvadeChance", label = "Spell Proj. Evade Chance", fmt = "d%%", color = colorCodes.EVASION, condFunc = function(v,o) return v > 0 and o.splitEvade end },
{ stat = "DeflectionRating", label = "Deflection Rating", fmt = "d", color = colorCodes.EVASION, compPercent = true },
{ stat = "DeflectChance", label = "Deflect Chance", fmt = "d%%", color = colorCodes.EVASION, compPercent = true },
{ },
{ stat = "Armour", label = "Armour", fmt = "d", compPercent = true },
{ stat = "Spec:ArmourInc", label = "%Inc Armour from Tree", fmt = "d%%" },
Expand Down
58 changes: 41 additions & 17 deletions src/Modules/CalcDefence.lua
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ function calcs.hitChance(evasion, accuracy, uncapped)
return uncapped and m_max(round(rawChance), 5) or m_max(m_min(round(rawChance), 100), 5)
end
-- Calculate Deflect chance
function calcs.deflectChance(evasion, accuracy, uncapped)
if accuracy < 0 then
return 5
function calcs.deflectChance(deflection, accuracy)
if deflection < 1 then
return 0
end
local rawChance = ( accuracy * 0.9 ) / ( accuracy + evasion * 0.2 ) * 100
return uncapped and m_max(round(rawChance), 0) or m_max(m_min(round(rawChance), 100), 0)
local rawChance = ( accuracy * 0.9 ) / ( accuracy + deflection * 0.2 ) * 100
return 100 - m_max(m_min(round(rawChance), 100), 0)
end
-- Calculate damage reduction from armour, float
function calcs.armourReductionF(armour, raw)
Expand Down Expand Up @@ -336,7 +336,7 @@ end
-- Based on code from FR and BS found in act_*.txt
---@param activeSkill/output/breakdown references table passed in from calc offence
---@param sourceType string type of incoming damage - it will be converted (taken as) from this type if applicable
---@param baseDmg for which to calculate the damage
---@param baseDmg string for which to calculate the damage
---@return table of taken damage parts, and number, sum of damages
function calcs.applyDmgTakenConversion(activeSkill, output, breakdown, sourceType, baseDmg)
local damageBreakdown = { }
Expand Down Expand Up @@ -1361,6 +1361,13 @@ function calcs.defence(env, actor)
output.EnergyShieldRecoveryCap = output.EnergyShield or 0
end

local enemyAccuracy = round(calcLib.val(enemyDB, "Accuracy"))
if modDB:Flag(nil, "EnemyAccuracyDistancePenalty") then
local enemyDistance = m_max(m_min(modDB:Sum("BASE", nil, "Multiplier:enemyDistance"), 120), 20)
local accuracyPenalty = 1 - ((enemyDistance - 20) / 100) * (1 - 0.1)
enemyAccuracy = m_floor(enemyAccuracy * accuracyPenalty)
end

if modDB:Flag(nil, "CannotEvade") or enemyDB:Flag(nil, "CannotBeEvaded") then
output.EvadeChance = 0
output.MeleeEvadeChance = 0
Expand All @@ -1374,12 +1381,6 @@ function calcs.defence(env, actor)
output.SpellEvadeChance = 100
output.SpellProjectileEvadeChance = 100
else
local enemyAccuracy = round(calcLib.val(enemyDB, "Accuracy"))
if modDB:Flag(nil, "EnemyAccuracyDistancePenalty") then
local enemyDistance = m_max(m_min(modDB:Sum("BASE", nil, "Multiplier:enemyDistance"), 120), 20)
local accuracyPenalty = 1 - ((enemyDistance - 20) / 100) * (1 - 0.1)
enemyAccuracy = m_floor(enemyAccuracy * accuracyPenalty)
end
local evadeChance = modDB:Sum("BASE", nil, "EvadeChance")
local hitChance = calcLib.mod(enemyDB, nil, "HitChance")
local evadeMax = modDB:Max(nil, "EvadeChanceMax") or data.misc.EvadeChanceCap
Expand Down Expand Up @@ -1428,6 +1429,21 @@ function calcs.defence(env, actor)
}
end
end

output.DeflectionRating = (output.Evasion * modDB:Sum("BASE", nil, "EvasionGainAsDeflection") / 100 + output.Armour * modDB:Sum("BASE", nil, "ArmourGainAsDeflection") / 100) * calcLib.mod(modDB, nil, "DeflectionRating")
output.DeflectChance = calcs.deflectChance(output.DeflectionRating, enemyAccuracy)
if modDB:Flag(nil, "DeflectIsLucky") then
local notDeflect = 1 - output.DeflectChance / 100
output.DeflectChance = (1 - notDeflect * notDeflect) * 100
end
output.DeflectEffect = m_min(m_max(data.misc.DeflectEffect + modDB:Sum("BASE", nil, "DeflectEffect"), 0), 100)
if breakdown then
breakdown.DeflectChance = {
s_format("Enemy level: %d ^8(%s the Configuration tab)", env.enemyLevel, env.configInput.enemyLevel and "overridden from" or "can be overridden in"),
s_format("Average enemy accuracy: %d", enemyAccuracy),
s_format("Approximate deflect chance: %d%%", output.DeflectChance),
}
end
end

local spellSuppressionChance = modDB:Sum("BASE", nil, "SpellSuppressionChance")
Expand Down Expand Up @@ -2345,15 +2361,16 @@ function calcs.buildDefenceEstimations(env, actor)
takenMult = (output[damageType.."SpellTakenHitMult"] + output[damageType.."AttackTakenHitMult"]) / 2
spellSuppressMult = output.EffectiveSpellSuppressionChance == 100 and (1 - output.SpellSuppressionEffect / 100 / 2) or 1
end
local deflectMulti = output.DeflectChance == 100 and (1 - output.DeflectEffect / 100) or 1
output[damageType.."EffectiveAppliedArmour"] = effectiveAppliedArmour
output[damageType.."ResistTakenHitMulti"] = resMult
local afterReductionMulti = takenMult * spellSuppressMult
local afterReductionMulti = takenMult * spellSuppressMult * deflectMulti
output[damageType.."AfterReductionTakenHitMulti"] = afterReductionMulti
local baseMult = resMult * reductMult
output[damageType.."BaseTakenHitMult"] = baseMult * afterReductionMulti
local takenMultReflect = output[damageType.."TakenReflect"]
local finalReflect = baseMult * takenMultReflect
output[damageType.."TakenHit"] = m_max(damage * baseMult + takenFlat, 0) * takenMult * spellSuppressMult + impaleDamage
output[damageType.."TakenHit"] = m_max(damage * baseMult + takenFlat, 0) * afterReductionMulti + impaleDamage
output[damageType.."TakenHitMult"] = (damage > 0) and (output[damageType.."TakenHit"] / damage) or 0
output["totalTakenHit"] = output["totalTakenHit"] + output[damageType.."TakenHit"]
if output.AnyTakenReflect then
Expand Down Expand Up @@ -2413,10 +2430,13 @@ function calcs.buildDefenceEstimations(env, actor)
if spellSuppressMult ~= 1 then
t_insert(breakdown[damageType.."TakenHitMult"], s_format("x Spell Suppression: %.3f", spellSuppressMult))
end
if deflectMulti ~= 1 then
t_insert(breakdown[damageType.."TakenHitMult"], s_format("x Deflection: %.3f", deflectMulti))
end
if impaleDamage ~= 0 then
t_insert(breakdown[damageType.."TakenHitMult"], s_format("+ Impale: %.1f", impaleDamage))
end
if takenMult ~= 1 or takenFlat ~= 0 or spellSuppressMult ~= 1 or impaleDamage ~= 0 then
if takenFlat ~= 0 or afterReductionMulti ~= 1 or impaleDamage ~= 0 then
t_insert(breakdown[damageType.."TakenHitMult"], s_format("= %.3f", output[damageType.."TakenHitMult"]))
end
if output.AnyTakenReflect then
Expand Down Expand Up @@ -3094,6 +3114,7 @@ function calcs.buildDefenceEstimations(env, actor)
DamageIn.EnergyShieldWhenHit = (DamageIn.EnergyShieldWhenHit or 0) + output.EnergyShieldOnSuppress * ( damageCategoryConfig == "Average" and 0.5 or 1 )
DamageIn.LifeWhenHit = (DamageIn.LifeWhenHit or 0) + output.LifeOnSuppress * ( damageCategoryConfig == "Average" and 0.5 or 1 )
end
local effectiveDeflectMulti = output.DeflectChance < 100 and 1 - output.DeflectChance * output.DeflectEffect / 10000 or 1
-- extra avoid chance
if damageCategoryConfig == "Projectile" or damageCategoryConfig == "SpellProjectile" then
ExtraAvoidChance = ExtraAvoidChance + output.AvoidProjectilesChance
Expand Down Expand Up @@ -3128,7 +3149,7 @@ function calcs.buildDefenceEstimations(env, actor)
end
averageAvoidChance = averageAvoidChance + AvoidChance
end
DamageIn[damageType] = output[damageType.."TakenHit"] * (blockEffect * suppressionEffect * (1 - AvoidChance / 100))
DamageIn[damageType] = output[damageType.."TakenHit"] * (blockEffect * suppressionEffect * effectiveDeflectMulti * (1 - AvoidChance / 100))
end
-- recoup initialisation
if output["anyRecoup"] > 0 then
Expand All @@ -3144,7 +3165,7 @@ function calcs.buildDefenceEstimations(env, actor)
output["LifeBelowHalfLossLostOverTime"] = 0
end
averageAvoidChance = averageAvoidChance / 5
output["ConfiguredDamageChance"] = 100 * (blockEffect * suppressionEffect * (1 - averageAvoidChance / 100))
output["ConfiguredDamageChance"] = 100 * (blockEffect * suppressionEffect * effectiveDeflectMulti * (1 - averageAvoidChance / 100))
output["NumberOfMitigatedDamagingHits"] = (output["ConfiguredDamageChance"] ~= 100 or DamageIn["TrackRecoupable"] or DamageIn["TrackLifeLossOverTime"] or DamageIn.GainWhenHit) and numberOfHitsToDie(DamageIn) or output["NumberOfDamagingHits"]
if breakdown then
breakdown["ConfiguredDamageChance"] = {
Expand All @@ -3156,6 +3177,9 @@ function calcs.buildDefenceEstimations(env, actor)
if suppressionEffect ~= 1 then
t_insert(breakdown["ConfiguredDamageChance"], s_format("x %.3f ^8(suppression effect)", suppressionEffect))
end
if effectiveDeflectMulti ~= 1 then
t_insert(breakdown["ConfiguredDamageChance"], s_format("x %.3f ^8(deflect effect)", effectiveDeflectMulti))
end
if averageAvoidChance > 0 then
t_insert(breakdown["ConfiguredDamageChance"], s_format("x %.2f ^8(chance for avoidance to fail)", 1 - averageAvoidChance / 100))
end
Expand Down
14 changes: 13 additions & 1 deletion src/Modules/CalcSections.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1693,12 +1693,24 @@ return {
{ modName = { "SpellDodgeChanceMax", "SpellDodgeChance" }, },
}, },
} },
{ defaultCollapsed = false, label = "Spell Suppression", data = {
--[[{ defaultCollapsed = false, label = "Spell Suppression", data = {
extra = "{0:output:SpellSuppressionChance}%",
{ label = "Suppression Ch.", { format = "{0:output:SpellSuppressionChance}% (+{0:output:SpellSuppressionChanceOverCap}%)", { modName = "SpellSuppressionChance" }, }, },
{ label = "Suppression Effect", { format = "{0:output:SpellSuppressionEffect}%", { modName = "SpellSuppressionEffect" }, }, },
{ label = "Life on Suppression", haveOutput = "LifeOnSuppress", { format = "{0:output:LifeOnSuppress}", { modName = "LifeOnSuppress" }, }, },
{ label = "ES on Suppression", haveOutput = "EnergyShieldOnSuppress", { format = "{0:output:EnergyShieldOnSuppress}", { modName = "EnergyShieldOnSuppress" }, }, },
} },]]
{ defaultCollapsed = false, label = "Deflection", data = {
extra = "{0:output:DeflectChance}%",
{ label = "Eva. as Deflection", { format = "{0:mod:1}%", { modName = "EvasionGainAsDeflection", modType = "BASE" }, }, },
{ label = "Arm. as Deflection", { format = "{0:mod:1}%", { modName = "ArmourGainAsDeflection", modType = "BASE" }, }, },
{ label = "Deflection Rating", { format = "{0:output:DeflectionRating}", { modName = "DeflectionRating" }, }, },
{ label = "Deflect Effect", { format = "{0:output:DeflectEffect}%", { modName = "DeflectEffect" }, }, },
{ label = "Deflect Chance", { format = "{0:output:DeflectChance}%",
{ breakdown = "DeflectChance" },
{ label = "Player modifiers", modName = { "DeflectChance" } },
{ label = "Enemy modifiers", modName = { "Accuracy" }, enemy = true },
}, },
} },
} },
-- misc resources
Expand Down
1 change: 1 addition & 0 deletions src/Modules/Data.lua
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ data.misc = { -- magic numbers
BlockChanceCap = 90,
SuppressionChanceCap = 100,
SuppressionEffect = 50,
DeflectEffect = data.gameConstants["BasePercentDamageDeflected"],
AvoidChanceCap = 75,
AccuracyFalloffStart = 20,
AccuracyFalloffEnd = 90,
Expand Down
8 changes: 7 additions & 1 deletion src/Modules/ModParser.lua
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,8 @@ local modNameList = {
["to dodge attacks and spell damage"] = { "AttackDodgeChance", "SpellDodgeChance" },
["to dodge attack and spell hits"] = { "AttackDodgeChance", "SpellDodgeChance" },
["to dodge attack or spell hits"] = { "AttackDodgeChance", "SpellDodgeChance" },
["deflection rating"] = { "DeflectionRating" },
["amount of damage prevented by deflection"] = { "DeflectEffect" },
["to suppress spell damage"] = { "SpellSuppressionChance" },
["amount of suppressed spell damage prevented"] = { "SpellSuppressionEffect" },
["to amount of suppressed spell damage prevented"] = { "SpellSuppressionEffect" },
Expand Down Expand Up @@ -3750,7 +3752,11 @@ local specialModList = {
},
["critical hits poison the enemy"] = { mod("PoisonChance", "OVERRIDE", 100, { type = "Condition", var = "CriticalStrike" })},
["always poison on hit with this weapon"] = { mod("PoisonChance", "OVERRIDE", 100 , nil, ModFlag.Weapon, 0, { type = "Condition", var = "{Hand}Attack" }, { type = "SkillType", skillType = SkillType.NonWeaponAttack, neg = true })},
-- Suppression
-- Suppression / deflection
["gain deflection rating equal to (%d+)%% of evasion rating"] = function(num) return { mod("EvasionGainAsDeflection", "BASE", num) } end,
["gain deflection rating equal to (%d+)%% of armour"] = function(num) return { mod("ArmourGainAsDeflection", "BASE", num) } end,
["prevent %+(%d+)%% of damage from deflected hits"] = function(num) return { mod("DeflectEffect", "BASE", num) } end,
["chance to deflect is lucky"] = { flag("DeflectIsLucky") },
["y?o?u?r? ?chance to suppress spell damage is lucky"] = { flag("SpellSuppressionChanceIsLucky") },
["y?o?u?r? ?chance to suppress spell damage is unlucky"] = { flag("SpellSuppressionChanceIsUnlucky") },
["prevent %+(%d+)%% of suppressed spell damage"] = function(num) return { mod("SpellSuppressionEffect", "BASE", num) } end,
Expand Down