Skip to content

Commit 7a734ae

Browse files
authored
Improve Accuracy Breakdown for Excess Hit Chance (#969)
Co-authored-by: majochem <majochem@users.noreply.github.com>
1 parent f86ae4e commit 7a734ae

File tree

1 file changed

+40
-35
lines changed

1 file changed

+40
-35
lines changed

src/Modules/CalcOffence.lua

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2243,7 +2243,8 @@ function calcs.offence(env, actor, activeSkill)
22432243
output.Accuracy = m_max(0, m_floor(base * (1 + inc / 100) * more))
22442244
local accuracyVsEnemy = m_max(0, m_floor(baseVsEnemy * (1 + incVsEnemy / 100) * moreVsEnemy))
22452245
local accuracyVsEnemyBase = accuracyVsEnemy
2246-
if not modDB:Flag(cfg, "NoAccuracyDistancePenalty") then
2246+
local noAccuracyDistancePenalty = modDB:Flag(cfg, "NoAccuracyDistancePenalty") -- saving this in local variable as it gets used again later
2247+
if not noAccuracyDistancePenalty then
22472248
accuracyVsEnemy = m_floor(accuracyVsEnemy * accuracyPenalty)
22482249
end
22492250
if breakdown then
@@ -2278,56 +2279,60 @@ function calcs.offence(env, actor, activeSkill)
22782279
}
22792280
end
22802281
end
2281-
if not isAttack or skillModList:Flag(cfg, "CannotBeEvaded") or skillData.cannotBeEvaded or (env.mode_effective and enemyDB:Flag(nil, "CannotEvade")) then
2282+
if not isAttack then
22822283
output.AccuracyHitChance = 100
22832284
else
22842285
local enemyEvasion = m_max(round(calcLib.val(enemyDB, "Evasion")), 0)
22852286
local hitChanceMod = calcLib.mod(skillModList, cfg, "HitChance")
2286-
output.AccuracyHitChance = calcs.hitChance(enemyEvasion, accuracyVsEnemy) * hitChanceMod
2287-
local hitChances = {}
2288-
for _, distance in ipairs(distances) do
2289-
local adjustedAccuracy = m_floor(accuracyVsEnemyBase * accuracyPenalties["accuracyPenalty" .. distance .. "m"])
2290-
hitChances["hitChance" .. distance .. "m"] = calcs.hitChance(enemyEvasion, adjustedAccuracy) * hitChanceMod
2287+
local cannotBeEvaded = skillModList:Flag(cfg, "CannotBeEvaded") or skillData.cannotBeEvaded or (env.mode_effective and enemyDB:Flag(nil, "CannotEvade"))
2288+
output.AccuracyHitChance = (cannotBeEvaded and 100) or calcs.hitChance(enemyEvasion, accuracyVsEnemy) * hitChanceMod
2289+
-- Accounting for mods that enable "Chance to hit with Attacks can exceed 100%"
2290+
local exceedsHitChance = skillModList:Flag(nil,"Condition:HitChanceCanExceed100") and calcs.hitChance(enemyEvasion, (m_floor(accuracyVsEnemyBase * accuracyPenalties["accuracyPenalty" .. distances[1] .. "m"])) * hitChanceMod) -- Check for flag and at least 100% hit chance at minimum distance
2291+
output.AccuracyHitChanceUncapped = exceedsHitChance and m_max(calcs.hitChance(enemyEvasion, accuracyVsEnemy, true) * calcLib.mod(skillModList, cfg, "HitChance"), output.AccuracyHitChance) -- keep higher chance in case of "CannotBeEvaded"
2292+
local handCondition = (pass.label == "Off Hand") and "OffHandAttack" or "MainHandAttack"
2293+
if exceedsHitChance and output.AccuracyHitChanceUncapped - 100 > 0 then
2294+
skillModList:NewMod("Multiplier:ExcessHitChance", "BASE", round(output.AccuracyHitChanceUncapped - 100, 2), "HitChanceCanExceed100", { type = "Condition", var = handCondition})
22912295
end
22922296
if breakdown then
22932297
breakdown.AccuracyHitChance = {
22942298
"Enemy level: " .. env.enemyLevel .. (env.configInput.enemyLevel and " ^8(overridden from the Configuration tab" or " ^8(can be overridden in the Configuration tab)"),
22952299
"Enemy evasion: " .. enemyEvasion,
22962300
"",
22972301
"Approximate hit chance at:",
2302+
}
2303+
-- Calculating individual hit chances at different distances
2304+
local hitChances = {}
2305+
hitChances[1] = {distance = enemyDistance}
2306+
local buffers = {
2307+
dist = {" ", " ", " ", ""}, -- these define the number of required spaces based on string length to have the numbers aligned (it's not a simple length * x due to linting that happens later)
2308+
chance = {" ", " ", ""}
22982309
}
2299-
for i = 1, #distances do
2300-
local lower = distances[i]
2301-
local upper = distances[i + 1] or lower
2302-
if enemyDistance ~= lower then -- This ugly formatting keeps the text aligned in the display
2303-
t_insert(breakdown.AccuracyHitChance, string.rep(" ", 4 - string.len(lower))..string.rep(" ", string.len(lower) - 2)..lower .. "m: "..hitChances["hitChance" .. lower .. "m"].."%")
2304-
end
2305-
if enemyDistance >= lower and enemyDistance < upper then
2306-
t_insert(breakdown.AccuracyHitChance, string.rep(" ", 4 - string.len(enemyDistance))..string.rep(" ", string.len(enemyDistance) - 2)..enemyDistance.."m: "..output.AccuracyHitChance.."% ^8(current config)")
2310+
for _, distance in ipairs(distances) do -- put distance values in order, incl. config value
2311+
if distance < hitChances[#hitChances].distance then
2312+
t_insert(hitChances, #hitChances, { distance = distance })
2313+
elseif distance > hitChances[#hitChances].distance then
2314+
t_insert(hitChances, { distance = distance })
23072315
end
23082316
end
2317+
for _, entry in ipairs(hitChances) do
2318+
entry.adjustedAccuracy = ((entry.distance == enemyDistance) and accuracyVsEnemy) or m_floor(accuracyVsEnemyBase * ((noAccuracyDistancePenalty and 1) or accuracyPenalties["accuracyPenalty" .. entry.distance .. "m"])) -- checking here for noAccuracyDistancePenalty again because it's otherwise only used to calculate accuracyVsEnemy, which uses the config distance value
2319+
entry.capped = ((entry.distance == enemyDistance) and output.AccuracyHitChance) or (cannotBeEvaded and 100) or m_max(calcs.hitChance(enemyEvasion, entry.adjustedAccuracy) * hitChanceMod)
2320+
entry.uncapped = exceedsHitChance and m_max(calcs.hitChance(enemyEvasion, entry.adjustedAccuracy, true) * hitChanceMod, entry.capped) -- compare to capped to account for cannotBeEvaded
2321+
entry.excess = exceedsHitChance and entry.uncapped > 100 and entry.uncapped - entry.capped
2322+
entry.distBuffer = buffers.dist[string.len(entry.distance)] -- buffer defines the number of spaces needed to align the output numbers
2323+
entry.cappedBuffer = buffers.chance[string.len(entry.capped)]
2324+
entry.excessText = entry.excess and " ^8(+" .. buffers.chance[string.len(entry.excess)+1] .. entry.excess .. "%)" or ""
2325+
entry.config = (entry.distance == enemyDistance) and " ^8(current config)" or ""
2326+
t_insert(breakdown.AccuracyHitChance, entry.distBuffer .. entry.distance .. "m: " .. entry.cappedBuffer .. entry.capped .. "%" .. entry.excessText .. entry.config)
2327+
end
2328+
-- Add note for uncapped hit chance / "Chance to hit with Attacks can exceed 100%"
2329+
if exceedsHitChance then
2330+
t_insert(breakdown.AccuracyHitChance, "") -- empty line for better readability
2331+
t_insert(breakdown.AccuracyHitChance, "^8Note: Your hit chance can exceed 100%.\nExcess values are shown as (+x%)")
2332+
t_insert(breakdown.AccuracyHitChance, "") -- empty line for better readability
2333+
end
23092334
end
23102335
end
2311-
-- Accounting for mods that enable "Chance to hit with Attacks can exceed 100%"
2312-
if skillModList:Flag(nil,"Condition:HitChanceCanExceed100") and output.AccuracyHitChance == 100 then
2313-
local enemyEvasion = m_max(round(calcLib.val(enemyDB, "Evasion")), 0)
2314-
output.AccuracyHitChanceUncapped = m_max(calcs.hitChance(enemyEvasion, accuracyVsEnemy, true) * calcLib.mod(skillModList, cfg, "HitChance"), output.AccuracyHitChance) -- keep higher chance in case of "CannotBeEvaded"
2315-
if breakdown and breakdown.AccuracyHitChance then
2316-
t_insert(breakdown.AccuracyHitChance, "Uncapped hit chance: " .. output.AccuracyHitChanceUncapped .. "%")
2317-
elseif breakdown then
2318-
breakdown.AccuracyHitChance = {
2319-
"Enemy level: "..env.enemyLevel..(env.configInput.enemyLevel and " ^8(overridden from the Configuration tab" or " ^8(can be overridden in the Configuration tab)"),
2320-
"Enemy evasion: "..enemyEvasion,
2321-
"Approximate hit chance: "..output.AccuracyHitChance.."%",
2322-
"Uncapped hit chance: " .. output.AccuracyHitChanceUncapped .. "%"
2323-
}
2324-
end
2325-
local handCondition = (pass.label == "Off Hand") and "OffHandAttack" or "MainHandAttack"
2326-
if output.AccuracyHitChanceUncapped - 100 > 0 then
2327-
skillModList:NewMod("Multiplier:ExcessHitChance", "BASE", round(output.AccuracyHitChanceUncapped - 100, 2), "HitChanceCanExceed100", { type = "Condition", var = handCondition})
2328-
end
2329-
2330-
end
23312336
--enemy block chance
23322337
output.enemyBlockChance = m_max(m_min((enemyDB:Sum("BASE", cfg, "BlockChance") or 0), 100) - skillModList:Sum("BASE", cfg, "reduceEnemyBlock"), 0)
23332338
if enemyDB:Flag(nil, "CannotBlockAttacks") and isAttack then

0 commit comments

Comments
 (0)