From 91a55712108837d7a09aa9abe5f8ae4ffda2f2e6 Mon Sep 17 00:00:00 2001 From: LocalIdentity <31035929+LocalIdentity@users.noreply.github.com> Date: Sat, 15 Nov 2025 14:42:34 +1100 Subject: [PATCH] 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 --- src/Modules/Build.lua | 4 ++++ src/Modules/BuildDisplayStats.lua | 6 +++--- src/Modules/CalcDefence.lua | 26 ++++++++++++++------------ src/Modules/CalcOffence.lua | 21 ++++++++++++++------- src/Modules/CalcSections.lua | 4 ++-- 5 files changed, 37 insertions(+), 24 deletions(-) diff --git a/src/Modules/Build.lua b/src/Modules/Build.lua index 700f0d85..474af435 100644 --- a/src/Modules/Build.lua +++ b/src/Modules/Build.lua @@ -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) diff --git a/src/Modules/BuildDisplayStats.lua b/src/Modules/BuildDisplayStats.lua index cac3b4aa..1b4061e5 100644 --- a/src/Modules/BuildDisplayStats.lua +++ b/src/Modules/BuildDisplayStats.lua @@ -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 }, diff --git a/src/Modules/CalcDefence.lua b/src/Modules/CalcDefence.lua index bed0ba0f..37f2a11d 100644 --- a/src/Modules/CalcDefence.lua +++ b/src/Modules/CalcDefence.lua @@ -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 diff --git a/src/Modules/CalcOffence.lua b/src/Modules/CalcOffence.lua index 0a95d8b6..2fc4fd58 100644 --- a/src/Modules/CalcOffence.lua +++ b/src/Modules/CalcOffence.lua @@ -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 diff --git a/src/Modules/CalcSections.lua b/src/Modules/CalcSections.lua index b2e6e8ee..3b7c81b9 100644 --- a/src/Modules/CalcSections.lua +++ b/src/Modules/CalcSections.lua @@ -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" }, }, },