Skip to content

Commit 1ec5ffd

Browse files
Edvinas-SmitaLocalIdentity
andauthored
Add support for Deflection (#1300)
* Add support for deflection * No deflect at 0 rating and DR at 100% * Use data from Misc.lua --------- Co-authored-by: LocalIdentity <localidentity2@gmail.com>
1 parent 6681296 commit 1ec5ffd

5 files changed

Lines changed: 65 additions & 20 deletions

File tree

src/Modules/BuildDisplayStats.lua

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,13 +156,15 @@ local displayStats = {
156156
{ stat = "NetManaRegen", label = "Net Mana Recovery", fmt = "+.1f", color = colorCodes.MANA },
157157
{ stat = "NetEnergyShieldRegen", label = "Net ES Recovery", fmt = "+.1f", color = colorCodes.ES },
158158
{ },
159-
{ stat = "Evasion", label = "Evasion rating", fmt = "d", color = colorCodes.EVASION, compPercent = true },
159+
{ stat = "Evasion", label = "Evasion Rating", fmt = "d", color = colorCodes.EVASION, compPercent = true },
160160
{ stat = "Spec:EvasionInc", label = "%Inc Evasion from Tree", color = colorCodes.EVASION, fmt = "d%%" },
161161
{ stat = "EvadeChance", label = "Evade Chance", fmt = "d%%", color = colorCodes.EVASION, condFunc = function(v,o) return v > 0 and o.noSplitEvade end },
162162
{ stat = "MeleeEvadeChance", label = "Melee Evade Chance", fmt = "d%%", color = colorCodes.EVASION, condFunc = function(v,o) return v > 0 and o.splitEvade end },
163163
{ stat = "ProjectileEvadeChance", label = "Projectile Evade Chance", fmt = "d%%", color = colorCodes.EVASION, condFunc = function(v,o) return v > 0 and o.splitEvade end },
164164
{ stat = "SpellEvadeChance", label = "Spell Evade Chance", fmt = "d%%", color = colorCodes.EVASION, condFunc = function(v,o) return v > 0 and o.splitEvade end },
165165
{ stat = "SpellProjectileEvadeChance", label = "Spell Proj. Evade Chance", fmt = "d%%", color = colorCodes.EVASION, condFunc = function(v,o) return v > 0 and o.splitEvade end },
166+
{ stat = "DeflectionRating", label = "Deflection Rating", fmt = "d", color = colorCodes.EVASION, compPercent = true },
167+
{ stat = "DeflectChance", label = "Deflect Chance", fmt = "d%%", color = colorCodes.EVASION, compPercent = true },
166168
{ },
167169
{ stat = "Armour", label = "Armour", fmt = "d", compPercent = true },
168170
{ stat = "Spec:ArmourInc", label = "%Inc Armour from Tree", fmt = "d%%" },

src/Modules/CalcDefence.lua

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ function calcs.hitChance(evasion, accuracy, uncapped)
3636
return uncapped and m_max(round(rawChance), 5) or m_max(m_min(round(rawChance), 100), 5)
3737
end
3838
-- Calculate Deflect chance
39-
function calcs.deflectChance(evasion, accuracy, uncapped)
40-
if accuracy < 0 then
41-
return 5
39+
function calcs.deflectChance(deflection, accuracy)
40+
if deflection < 1 then
41+
return 0
4242
end
43-
local rawChance = ( accuracy * 0.9 ) / ( accuracy + evasion * 0.2 ) * 100
44-
return uncapped and m_max(round(rawChance), 0) or m_max(m_min(round(rawChance), 100), 0)
43+
local rawChance = ( accuracy * 0.9 ) / ( accuracy + deflection * 0.2 ) * 100
44+
return 100 - m_max(m_min(round(rawChance), 100), 0)
4545
end
4646
-- Calculate damage reduction from armour, float
4747
function calcs.armourReductionF(armour, raw)
@@ -336,7 +336,7 @@ end
336336
-- Based on code from FR and BS found in act_*.txt
337337
---@param activeSkill/output/breakdown references table passed in from calc offence
338338
---@param sourceType string type of incoming damage - it will be converted (taken as) from this type if applicable
339-
---@param baseDmg for which to calculate the damage
339+
---@param baseDmg string for which to calculate the damage
340340
---@return table of taken damage parts, and number, sum of damages
341341
function calcs.applyDmgTakenConversion(activeSkill, output, breakdown, sourceType, baseDmg)
342342
local damageBreakdown = { }
@@ -1361,6 +1361,13 @@ function calcs.defence(env, actor)
13611361
output.EnergyShieldRecoveryCap = output.EnergyShield or 0
13621362
end
13631363

1364+
local enemyAccuracy = round(calcLib.val(enemyDB, "Accuracy"))
1365+
if modDB:Flag(nil, "EnemyAccuracyDistancePenalty") then
1366+
local enemyDistance = m_max(m_min(modDB:Sum("BASE", nil, "Multiplier:enemyDistance"), 120), 20)
1367+
local accuracyPenalty = 1 - ((enemyDistance - 20) / 100) * (1 - 0.1)
1368+
enemyAccuracy = m_floor(enemyAccuracy * accuracyPenalty)
1369+
end
1370+
13641371
if modDB:Flag(nil, "CannotEvade") or enemyDB:Flag(nil, "CannotBeEvaded") then
13651372
output.EvadeChance = 0
13661373
output.MeleeEvadeChance = 0
@@ -1374,12 +1381,6 @@ function calcs.defence(env, actor)
13741381
output.SpellEvadeChance = 100
13751382
output.SpellProjectileEvadeChance = 100
13761383
else
1377-
local enemyAccuracy = round(calcLib.val(enemyDB, "Accuracy"))
1378-
if modDB:Flag(nil, "EnemyAccuracyDistancePenalty") then
1379-
local enemyDistance = m_max(m_min(modDB:Sum("BASE", nil, "Multiplier:enemyDistance"), 120), 20)
1380-
local accuracyPenalty = 1 - ((enemyDistance - 20) / 100) * (1 - 0.1)
1381-
enemyAccuracy = m_floor(enemyAccuracy * accuracyPenalty)
1382-
end
13831384
local evadeChance = modDB:Sum("BASE", nil, "EvadeChance")
13841385
local hitChance = calcLib.mod(enemyDB, nil, "HitChance")
13851386
local evadeMax = modDB:Max(nil, "EvadeChanceMax") or data.misc.EvadeChanceCap
@@ -1435,6 +1436,21 @@ function calcs.defence(env, actor)
14351436
}
14361437
end
14371438
end
1439+
1440+
output.DeflectionRating = (output.Evasion * modDB:Sum("BASE", nil, "EvasionGainAsDeflection") / 100 + output.Armour * modDB:Sum("BASE", nil, "ArmourGainAsDeflection") / 100) * calcLib.mod(modDB, nil, "DeflectionRating")
1441+
output.DeflectChance = calcs.deflectChance(output.DeflectionRating, enemyAccuracy)
1442+
if modDB:Flag(nil, "DeflectIsLucky") then
1443+
local notDeflect = 1 - output.DeflectChance / 100
1444+
output.DeflectChance = (1 - notDeflect * notDeflect) * 100
1445+
end
1446+
output.DeflectEffect = m_min(m_max(data.misc.DeflectEffect + modDB:Sum("BASE", nil, "DeflectEffect"), 0), 100)
1447+
if breakdown then
1448+
breakdown.DeflectChance = {
1449+
s_format("Enemy level: %d ^8(%s the Configuration tab)", env.enemyLevel, env.configInput.enemyLevel and "overridden from" or "can be overridden in"),
1450+
s_format("Average enemy accuracy: %d", enemyAccuracy),
1451+
s_format("Approximate deflect chance: %d%%", output.DeflectChance),
1452+
}
1453+
end
14381454
end
14391455

14401456
local spellSuppressionChance = modDB:Sum("BASE", nil, "SpellSuppressionChance")
@@ -2352,15 +2368,16 @@ function calcs.buildDefenceEstimations(env, actor)
23522368
takenMult = (output[damageType.."SpellTakenHitMult"] + output[damageType.."AttackTakenHitMult"]) / 2
23532369
spellSuppressMult = output.EffectiveSpellSuppressionChance == 100 and (1 - output.SpellSuppressionEffect / 100 / 2) or 1
23542370
end
2371+
local deflectMulti = output.DeflectChance == 100 and (1 - output.DeflectEffect / 100) or 1
23552372
output[damageType.."EffectiveAppliedArmour"] = effectiveAppliedArmour
23562373
output[damageType.."ResistTakenHitMulti"] = resMult
2357-
local afterReductionMulti = takenMult * spellSuppressMult
2374+
local afterReductionMulti = takenMult * spellSuppressMult * deflectMulti
23582375
output[damageType.."AfterReductionTakenHitMulti"] = afterReductionMulti
23592376
local baseMult = resMult * reductMult
23602377
output[damageType.."BaseTakenHitMult"] = baseMult * afterReductionMulti
23612378
local takenMultReflect = output[damageType.."TakenReflect"]
23622379
local finalReflect = baseMult * takenMultReflect
2363-
output[damageType.."TakenHit"] = m_max(damage * baseMult + takenFlat, 0) * takenMult * spellSuppressMult + impaleDamage
2380+
output[damageType.."TakenHit"] = m_max(damage * baseMult + takenFlat, 0) * afterReductionMulti + impaleDamage
23642381
output[damageType.."TakenHitMult"] = (damage > 0) and (output[damageType.."TakenHit"] / damage) or 0
23652382
output["totalTakenHit"] = output["totalTakenHit"] + output[damageType.."TakenHit"]
23662383
if output.AnyTakenReflect then
@@ -2420,10 +2437,13 @@ function calcs.buildDefenceEstimations(env, actor)
24202437
if spellSuppressMult ~= 1 then
24212438
t_insert(breakdown[damageType.."TakenHitMult"], s_format("x Spell Suppression: %.3f", spellSuppressMult))
24222439
end
2440+
if deflectMulti ~= 1 then
2441+
t_insert(breakdown[damageType.."TakenHitMult"], s_format("x Deflection: %.3f", deflectMulti))
2442+
end
24232443
if impaleDamage ~= 0 then
24242444
t_insert(breakdown[damageType.."TakenHitMult"], s_format("+ Impale: %.1f", impaleDamage))
24252445
end
2426-
if takenMult ~= 1 or takenFlat ~= 0 or spellSuppressMult ~= 1 or impaleDamage ~= 0 then
2446+
if takenFlat ~= 0 or afterReductionMulti ~= 1 or impaleDamage ~= 0 then
24272447
t_insert(breakdown[damageType.."TakenHitMult"], s_format("= %.3f", output[damageType.."TakenHitMult"]))
24282448
end
24292449
if output.AnyTakenReflect then
@@ -3101,6 +3121,7 @@ function calcs.buildDefenceEstimations(env, actor)
31013121
DamageIn.EnergyShieldWhenHit = (DamageIn.EnergyShieldWhenHit or 0) + output.EnergyShieldOnSuppress * ( damageCategoryConfig == "Average" and 0.5 or 1 )
31023122
DamageIn.LifeWhenHit = (DamageIn.LifeWhenHit or 0) + output.LifeOnSuppress * ( damageCategoryConfig == "Average" and 0.5 or 1 )
31033123
end
3124+
local effectiveDeflectMulti = output.DeflectChance < 100 and 1 - output.DeflectChance * output.DeflectEffect / 10000 or 1
31043125
-- extra avoid chance
31053126
if damageCategoryConfig == "Projectile" or damageCategoryConfig == "SpellProjectile" then
31063127
ExtraAvoidChance = ExtraAvoidChance + output.AvoidProjectilesChance
@@ -3135,7 +3156,7 @@ function calcs.buildDefenceEstimations(env, actor)
31353156
end
31363157
averageAvoidChance = averageAvoidChance + AvoidChance
31373158
end
3138-
DamageIn[damageType] = output[damageType.."TakenHit"] * (blockEffect * suppressionEffect * (1 - AvoidChance / 100))
3159+
DamageIn[damageType] = output[damageType.."TakenHit"] * (blockEffect * suppressionEffect * effectiveDeflectMulti * (1 - AvoidChance / 100))
31393160
end
31403161
-- recoup initialisation
31413162
if output["anyRecoup"] > 0 then
@@ -3151,7 +3172,7 @@ function calcs.buildDefenceEstimations(env, actor)
31513172
output["LifeBelowHalfLossLostOverTime"] = 0
31523173
end
31533174
averageAvoidChance = averageAvoidChance / 5
3154-
output["ConfiguredDamageChance"] = 100 * (blockEffect * suppressionEffect * (1 - averageAvoidChance / 100))
3175+
output["ConfiguredDamageChance"] = 100 * (blockEffect * suppressionEffect * effectiveDeflectMulti * (1 - averageAvoidChance / 100))
31553176
output["NumberOfMitigatedDamagingHits"] = (output["ConfiguredDamageChance"] ~= 100 or DamageIn["TrackRecoupable"] or DamageIn["TrackLifeLossOverTime"] or DamageIn.GainWhenHit) and numberOfHitsToDie(DamageIn) or output["NumberOfDamagingHits"]
31563177
if breakdown then
31573178
breakdown["ConfiguredDamageChance"] = {
@@ -3163,6 +3184,9 @@ function calcs.buildDefenceEstimations(env, actor)
31633184
if suppressionEffect ~= 1 then
31643185
t_insert(breakdown["ConfiguredDamageChance"], s_format("x %.3f ^8(suppression effect)", suppressionEffect))
31653186
end
3187+
if effectiveDeflectMulti ~= 1 then
3188+
t_insert(breakdown["ConfiguredDamageChance"], s_format("x %.3f ^8(deflect effect)", effectiveDeflectMulti))
3189+
end
31663190
if averageAvoidChance > 0 then
31673191
t_insert(breakdown["ConfiguredDamageChance"], s_format("x %.2f ^8(chance for avoidance to fail)", 1 - averageAvoidChance / 100))
31683192
end

src/Modules/CalcSections.lua

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1693,12 +1693,24 @@ return {
16931693
{ modName = { "SpellDodgeChanceMax", "SpellDodgeChance" }, },
16941694
}, },
16951695
} },
1696-
{ defaultCollapsed = false, label = "Spell Suppression", data = {
1696+
--[[{ defaultCollapsed = false, label = "Spell Suppression", data = {
16971697
extra = "{0:output:SpellSuppressionChance}%",
16981698
{ label = "Suppression Ch.", { format = "{0:output:SpellSuppressionChance}% (+{0:output:SpellSuppressionChanceOverCap}%)", { modName = "SpellSuppressionChance" }, }, },
16991699
{ label = "Suppression Effect", { format = "{0:output:SpellSuppressionEffect}%", { modName = "SpellSuppressionEffect" }, }, },
17001700
{ label = "Life on Suppression", haveOutput = "LifeOnSuppress", { format = "{0:output:LifeOnSuppress}", { modName = "LifeOnSuppress" }, }, },
17011701
{ label = "ES on Suppression", haveOutput = "EnergyShieldOnSuppress", { format = "{0:output:EnergyShieldOnSuppress}", { modName = "EnergyShieldOnSuppress" }, }, },
1702+
} },]]
1703+
{ defaultCollapsed = false, label = "Deflection", data = {
1704+
extra = "{0:output:DeflectChance}%",
1705+
{ label = "Eva. as Deflection", { format = "{0:mod:1}%", { modName = "EvasionGainAsDeflection", modType = "BASE" }, }, },
1706+
{ label = "Arm. as Deflection", { format = "{0:mod:1}%", { modName = "ArmourGainAsDeflection", modType = "BASE" }, }, },
1707+
{ label = "Deflection Rating", { format = "{0:output:DeflectionRating}", { modName = "DeflectionRating" }, }, },
1708+
{ label = "Deflect Effect", { format = "{0:output:DeflectEffect}%", { modName = "DeflectEffect" }, }, },
1709+
{ label = "Deflect Chance", { format = "{0:output:DeflectChance}%",
1710+
{ breakdown = "DeflectChance" },
1711+
{ label = "Player modifiers", modName = { "DeflectChance" } },
1712+
{ label = "Enemy modifiers", modName = { "Accuracy" }, enemy = true },
1713+
}, },
17021714
} },
17031715
} },
17041716
-- misc resources

src/Modules/Data.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ data.misc = { -- magic numbers
181181
BlockChanceCap = 90,
182182
SuppressionChanceCap = 100,
183183
SuppressionEffect = 50,
184+
DeflectEffect = data.gameConstants["BasePercentDamageDeflected"],
184185
AvoidChanceCap = 75,
185186
AccuracyFalloffStart = 20,
186187
AccuracyFalloffEnd = 90,

src/Modules/ModParser.lua

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,8 @@ local modNameList = {
319319
["to dodge attacks and spell damage"] = { "AttackDodgeChance", "SpellDodgeChance" },
320320
["to dodge attack and spell hits"] = { "AttackDodgeChance", "SpellDodgeChance" },
321321
["to dodge attack or spell hits"] = { "AttackDodgeChance", "SpellDodgeChance" },
322+
["deflection rating"] = { "DeflectionRating" },
323+
["amount of damage prevented by deflection"] = { "DeflectEffect" },
322324
["to suppress spell damage"] = { "SpellSuppressionChance" },
323325
["amount of suppressed spell damage prevented"] = { "SpellSuppressionEffect" },
324326
["to amount of suppressed spell damage prevented"] = { "SpellSuppressionEffect" },
@@ -3751,7 +3753,11 @@ local specialModList = {
37513753
},
37523754
["critical hits poison the enemy"] = { mod("PoisonChance", "OVERRIDE", 100, { type = "Condition", var = "CriticalStrike" })},
37533755
["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 })},
3754-
-- Suppression
3756+
-- Suppression / deflection
3757+
["gain deflection rating equal to (%d+)%% of evasion rating"] = function(num) return { mod("EvasionGainAsDeflection", "BASE", num) } end,
3758+
["gain deflection rating equal to (%d+)%% of armour"] = function(num) return { mod("ArmourGainAsDeflection", "BASE", num) } end,
3759+
["prevent %+(%d+)%% of damage from deflected hits"] = function(num) return { mod("DeflectEffect", "BASE", num) } end,
3760+
["chance to deflect is lucky"] = { flag("DeflectIsLucky") },
37553761
["y?o?u?r? ?chance to suppress spell damage is lucky"] = { flag("SpellSuppressionChanceIsLucky") },
37563762
["y?o?u?r? ?chance to suppress spell damage is unlucky"] = { flag("SpellSuppressionChanceIsUnlucky") },
37573763
["prevent %+(%d+)%% of suppressed spell damage"] = function(num) return { mod("SpellSuppressionEffect", "BASE", num) } end,

0 commit comments

Comments
 (0)