Fix calculation when using Unexciting Runegraft (#9251)

I implemented the Unexciting mod incorrectly in the past making it negate any lucky or unlucky mod instead of applying the correct formula
Unexciting rolls three times and keeps the median result -> 3p^2 - 2p^3

This affects Block Chance, Suppress chance and crit chance

Also changed the sidebar for Block and Suppress chance to use 3 and 2 significant figures as when using azadi's crest for double lucky the block chance values can easily get to 99 and require more precision
Made sure so add a new handler that trims off 0 after the decimal so regular block builds don't show 75.000% block

Co-authored-by: LocalIdentity <localidentity2@gmail.com>
This commit is contained in:
LocalIdentity
2025-11-15 14:42:34 +11:00
committed by GitHub
parent 12da634e87
commit 91a5571210
5 changed files with 37 additions and 24 deletions

View File

@@ -1535,6 +1535,10 @@ function buildMode:FormatStat(statData, statVal, overCapStatVal, colorOverride)
end
local valStr = s_format("%"..statData.fmt, val)
local number, suffix = valStr:match("^([%+%-]?%d+%.%d+)(%D*)$")
if number then
valStr = number:gsub("0+$", ""):gsub("%.$", "") .. suffix
end
valStr:gsub("%.", main.decimalSeparator)
valStr = color .. formatNumSep(valStr)

View File

@@ -159,11 +159,11 @@ local displayStats = {
{ stat = "Spec:ArmourInc", label = "%Inc Armour from Tree", fmt = "d%%" },
{ stat = "PhysicalDamageReduction", label = "Phys. Damage Reduction", fmt = "d%%", condFunc = function() return true end },
{ },
{ stat = "EffectiveBlockChance", label = "Block Chance", fmt = "d%%", overCapStat = "BlockChanceOverCap" },
{ stat = "EffectiveSpellBlockChance", label = "Spell Block Chance", fmt = "d%%", overCapStat = "SpellBlockChanceOverCap" },
{ stat = "EffectiveBlockChance", label = "Block Chance", fmt = ".3f%%", overCapStat = "BlockChanceOverCap" },
{ stat = "EffectiveSpellBlockChance", label = "Spell Block Chance", fmt = ".3f%%", overCapStat = "SpellBlockChanceOverCap" },
{ stat = "AttackDodgeChance", label = "Attack Dodge Chance", fmt = "d%%", overCapStat = "AttackDodgeChanceOverCap" },
{ stat = "SpellDodgeChance", label = "Spell Dodge Chance", fmt = "d%%", overCapStat = "SpellDodgeChanceOverCap" },
{ stat = "EffectiveSpellSuppressionChance", label = "Spell Suppression Chance", fmt = "d%%", overCapStat = "SpellSuppressionChanceOverCap" },
{ stat = "EffectiveSpellSuppressionChance", label = "Spell Suppression Chance", fmt = ".2f%%", overCapStat = "SpellSuppressionChanceOverCap" },
{ },
{ stat = "FireResist", label = "Fire Resistance", fmt = "d%%", color = colorCodes.FIRE, condFunc = function() return true end, overCapStat = "FireResistOverCap"},
{ stat = "FireResistOverCap", label = "Fire Res. Over Max", fmt = "d%%", hideStat = true },

View File

@@ -720,19 +720,20 @@ function calcs.defence(env, actor)
if modDB:Flag(nil, "ExtremeLuck") then
blockRolls = blockRolls * 2
end
if modDB:Flag(nil, "Unexciting") then
blockRolls = 0
end
end
-- unlucky config to lower the value of block, dodge, evade etc for ehp
if env.configInput.EHPUnluckyWorstOf and env.configInput.EHPUnluckyWorstOf ~= 1 then
blockRolls = -env.configInput.EHPUnluckyWorstOf / 2
end
if blockRolls ~= 0 then
if blockRolls > 0 then
output["Effective"..blockType] = (1 - (1 - output["Effective"..blockType] / 100) ^ (blockRolls + 1)) * 100
local blockChance = output["Effective"..blockType] / 100
if modDB:Flag(nil, "Unexciting") then
-- Unexciting rolls three times and keeps the median result -> 3p^2 - 2p^3
output["Effective"..blockType] = (3 * blockChance ^ 2 - 2 * blockChance ^ 3) * 100
elseif blockRolls > 0 then
output["Effective"..blockType] = (1 - (1 - blockChance) ^ (blockRolls + 1)) * 100
else
output["Effective"..blockType] = (output["Effective"..blockType] / 100) ^ m_abs(blockRolls) * output["Effective"..blockType]
output["Effective"..blockType] = blockChance ^ m_abs(blockRolls) * output["Effective"..blockType]
end
end
end
@@ -1096,19 +1097,20 @@ function calcs.defence(env, actor)
if modDB:Flag(nil, "ExtremeLuck") then
suppressRolls = suppressRolls * 2
end
if modDB:Flag(nil, "Unexciting") then
suppressRolls = 0
end
end
-- unlucky config to lower the value of block, dodge, evade etc for ehp
if env.configInput.EHPUnluckyWorstOf and env.configInput.EHPUnluckyWorstOf ~= 1 then
suppressRolls = -env.configInput.EHPUnluckyWorstOf / 2
end
if suppressRolls ~= 0 then
if suppressRolls > 0 then
output.EffectiveSpellSuppressionChance = (1 - (1 - output.EffectiveSpellSuppressionChance / 100) ^ (suppressRolls + 1)) * 100
local suppressChance = output.EffectiveSpellSuppressionChance / 100
if modDB:Flag(nil, "Unexciting") then
-- Unexciting rolls three times and keeps the median result -> 3p^2 - 2p^3
output.EffectiveSpellSuppressionChance = (3 * suppressChance ^ 2 - 2 * suppressChance ^ 3) * 100
elseif suppressRolls > 0 then
output.EffectiveSpellSuppressionChance = (1 - (1 - suppressChance) ^ (suppressRolls + 1)) * 100
else
output.EffectiveSpellSuppressionChance = (output.EffectiveSpellSuppressionChance / 100) ^ m_abs(suppressRolls) * output.EffectiveSpellSuppressionChance
output.EffectiveSpellSuppressionChance = suppressChance ^ m_abs(suppressRolls) * output.EffectiveSpellSuppressionChance
end
end

View File

@@ -2882,11 +2882,13 @@ function calcs.offence(env, actor, activeSkill)
if skillModList:Flag(skillCfg, "ExtremeLuck") then
critRolls = critRolls * 2
end
if skillModList:Flag(skillCfg, "Unexciting") then
critRolls = 0
end
if critRolls ~= 0 then
output.CritChance = (1 - (1 - output.CritChance / 100) ^ (critRolls + 1)) * 100
if modDB:Flag(nil, "Unexciting") then
-- Unexciting rolls three times and keeps the median result -> 3p^2 - 2p^3
output.CritChance = (3 * (output.CritChance / 100) ^ 2 - 2 * (output.CritChance / 100) ^ 3) * 100
else
output.CritChance = (1 - (1 - output.CritChance / 100) ^ (critRolls + 1)) * 100
end
end
local preHitCheckCritChance = output.CritChance
local preSkillUseCritChance= output.CritChance
@@ -2921,8 +2923,13 @@ function calcs.offence(env, actor, activeSkill)
end
if env.mode_effective and (critRolls ~= 0 or skillModList:Flag(skillCfg, "Every3UseCrit") or skillModList:Flag(skillCfg, "Every5UseCrit")) then
if critRolls ~= 0 then
t_insert(breakdown.CritChance, "Crit Chance is Lucky:")
t_insert(breakdown.CritChance, s_format("1 - (1 - %.4f)^ %d", preLuckyCritChance / 100, critRolls + 1))
if skillModList:Flag(skillCfg, "Unexciting") then
t_insert(breakdown.CritChance, "Crit Chance is Unexciting:")
t_insert(breakdown.CritChance, s_format("(3 x %.4f^ 2) - (2 x %.4f^ 3)", preLuckyCritChance / 100, preLuckyCritChance / 100))
else
t_insert(breakdown.CritChance, "Crit Chance is Lucky:")
t_insert(breakdown.CritChance, s_format("1 - (1 - %.4f)^ %d", preLuckyCritChance / 100, critRolls + 1))
end
end
if skillModList:Flag(skillCfg, "Every3UseCrit") then
t_insert(breakdown.CritChance, s_format("+ %.2f%% ^8(crit every 3rd use)", (2 * preSkillUseCritChance + 100) / 3 - preSkillUseCritChance))
@@ -3154,7 +3161,7 @@ function calcs.offence(env, actor, activeSkill)
damageTypeHitAvgUnlucky = ((rolls - 1) * damageTypeHitMin / rolls + damageTypeHitMax / rolls)
if damageTypeLuckyChance >= 0 then
damageTypeHitAvg = damageTypeHitAvgNotLucky * (1 - damageTypeLuckyChance) + damageTypeHitAvgLucky * damageTypeLuckyChance
else
else
damageTypeHitAvg = damageTypeHitAvgNotLucky * (1 - m_abs(damageTypeLuckyChance)) + damageTypeHitAvgUnlucky * m_abs(damageTypeLuckyChance)
end
if (damageTypeHitMin ~= 0 or damageTypeHitMax ~= 0) and env.mode_effective then

View File

@@ -1600,7 +1600,7 @@ return {
{ label = "Avoid Chaos Chance", haveOutput = "AvoidChaosDamageChance", { format = "{0:output:AvoidChaosDamageChance}%", { modName = "AvoidChaosDamageChance" }, }, },
{ label = "Avoid Proj Ch.", haveOutput = "AvoidProjectilesChance", { format = "{0:output:AvoidProjectilesChance}%", { modName = "AvoidProjectilesChance" }, }, },
} }, { defaultCollapsed = false, label = "Block", data = {
extra = "{0:output:EffectiveBlockChance}%/{0:output:EffectiveSpellBlockChance}%",
extra = "{3:output:EffectiveBlockChance}%/{3:output:EffectiveSpellBlockChance}%",
{ label = "Block Chance", { format = "{0:output:BlockChance}% (+{0:output:BlockChanceOverCap}%)",
{ breakdown = "BlockChance" },
{ modName = { "BlockChance", "ReplaceShieldBlock" } },
@@ -1629,7 +1629,7 @@ return {
}, },
} },
{ defaultCollapsed = false, label = "Spell Suppression", data = {
extra = "{0:output:SpellSuppressionChance}%",
extra = "{2:output:EffectiveSpellSuppressionChance}%",
{ 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" }, }, },