From 6163076c7cf2e802793d70eef799e88ee412efe6 Mon Sep 17 00:00:00 2001 From: Antaresque Date: Tue, 29 Apr 2025 00:09:03 +0200 Subject: [PATCH 1/6] Add prototype version of critforking --- src/Data/ModCache.lua | 3 +-- src/Modules/BuildDisplayStats.lua | 1 + src/Modules/CalcOffence.lua | 30 ++++++++++++++++++++++++++++++ src/Modules/ModParser.lua | 1 + 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/Data/ModCache.lua b/src/Data/ModCache.lua index f6d3595b15..23a49e3db6 100755 --- a/src/Data/ModCache.lua +++ b/src/Data/ModCache.lua @@ -3821,8 +3821,7 @@ c["Fire Damage from Hits Contributes to Shock Chance instead of Ignite Chance an c["Fire Damage from Hits Contributes to Shock Chance instead of Ignite Chance and Magnitude Cold Damage from Hits Contributes to Ignite Chance and Magnitude instead of Chill Magnitude or Freeze Buildup"]={nil,"Fire Damage from Hits Contributes to Shock Chance instead of Ignite Chance and Magnitude Cold Damage from Hits Contributes to Ignite Chance and Magnitude instead of Chill Magnitude or Freeze Buildup "} c["Fire Exposure you inflict lowers Total Fire Resistance by an extra 5%"]={{[1]={flags=0,keywordFlags=0,name="ExtraFireExposure",type="BASE",value=-5}},nil} c["Flasks gain 0.17 charges per Second"]={{[1]={flags=0,keywordFlags=0,name="FlaskChargesGenerated",type="BASE",value=0.17}},nil} -c["Forks Critical Hits"]={nil,"Forks Critical Hits "} -c["Forks Critical Hits 10% of Skill Mana Costs Converted to Life Costs"]={nil,"Forks Critical Hits 10% of Skill Mana Costs Converted to Life Costs "} +c["Forks Critical Hits"]={{[1]={flags=0,keywordFlags=0,name="ForkCrit",type="FLAG",value=true}},nil} c["Freeze as though dealing Cold damage equal to 10% of your maximum Mana when Hit"]={nil,"Freeze as though dealing Cold damage equal to 10% of your maximum Mana when Hit "} c["Frenzy or Power Charge"]={nil,"Frenzy or Power Charge "} c["Fully Armour Broken enemies you kill with Hits Shatter"]={nil,"Fully Armour Broken enemies you kill with Hits Shatter "} diff --git a/src/Modules/BuildDisplayStats.lua b/src/Modules/BuildDisplayStats.lua index 7355aa3120..ff4b7e0910 100644 --- a/src/Modules/BuildDisplayStats.lua +++ b/src/Modules/BuildDisplayStats.lua @@ -33,6 +33,7 @@ local displayStats = { { stat = "PreEffectiveCritChance", label = "Crit Chance", fmt = ".2f%%", flag = "hit" }, { stat = "CritChance", label = "Effective Crit Chance", fmt = ".2f%%", flag = "hit", condFunc = function(v,o) return v ~= o.PreEffectiveCritChance end }, { stat = "CritMultiplier", label = "Crit Multiplier", fmt = "d%%", pc = true, condFunc = function(v,o) return (o.CritChance or 0) > 0 end }, + --{ stat = "CritMultiplier", label = "Effective Crit Multiplier", fmt = "d%%", pc = true, condFunc = function(v,o) return (o.CritChance or 0) > 0 and v ~= o.PreEffectiveCritMultiplier end }, { stat = "HitChance", label = "Hit Chance", fmt = ".0f%%", flag = "attack" }, { stat = "HitChance", label = "Hit Chance", fmt = ".0f%%", condFunc = function(v,o) return o.enemyHasSpellBlock end }, { stat = "AccuracyHitChanceUncapped", label = "Uncap. Hit Chance", fmt = ".0f%%", flag = "attack", condFunc = function(v,o) if o.AccuracyHitChanceUncapped then return o.AccuracyHitChanceUncapped > 100 end end }, diff --git a/src/Modules/CalcOffence.lua b/src/Modules/CalcOffence.lua index 42a348a894..48dddd215c 100644 --- a/src/Modules/CalcOffence.lua +++ b/src/Modules/CalcOffence.lua @@ -3172,6 +3172,12 @@ function calcs.offence(env, actor, activeSkill) if env.mode_effective and skillModList:Flag(cfg, "CritChanceLucky") then output.CritChance = (1 - (1 - output.CritChance / 100) ^ 2) * 100 end + output.PreForkCritChance = output.CritChance + local preForkCritChance = output.CritChance + if env.mode_effective and skillModList:Flag(cfg, "ForkCrit") then + output.CritChance = (1 - (1 - output.CritChance / 100) ^ 2) * 100 + end + output.PreHitCheckCritChance = output.CritChance local preHitCheckCritChance = output.CritChance if env.mode_effective then output.CritChance = output.CritChance * output.AccuracyHitChance / 100 @@ -3198,6 +3204,11 @@ function calcs.offence(env, actor, activeSkill) if env.mode_effective and skillModList:Flag(cfg, "CritChanceLucky") then t_insert(breakdown.CritChance, "Crit Chance is Lucky:") t_insert(breakdown.CritChance, s_format("1 - (1 - %.4f) x (1 - %.4f)", preLuckyCritChance / 100, preLuckyCritChance / 100)) + t_insert(breakdown.CritChance, s_format("= %.2f%%", preForkCritChance)) + end + if env.mode_effective and skillModList:Flag(cfg, "ForkCrit") then + t_insert(breakdown.CritChance, "Critical Strike Forks:") + t_insert(breakdown.CritChance, s_format("1 - (1 - %.4f) x (1 - %.4f)", preForkCritChance / 100, preForkCritChance / 100)) t_insert(breakdown.CritChance, s_format("= %.2f%%", preHitCheckCritChance)) end if env.mode_effective and output.AccuracyHitChance < 100 then @@ -3221,6 +3232,22 @@ function calcs.offence(env, actor, activeSkill) if multiOverride then extraDamage = multiOverride / 100 end + + output.PreEffectiveCritMultiplier = 1 + extraDamage + -- if crit forks are enabled, roll for crit twice and add multiplier for each + if env.mode_effective and skillModList:Flag(cfg, "ForkCrit") then + -- get crit chance and calculate odds of critting twice + local critChancePercentage = output.PreForkCritChance + local preHitCheckCritChance = output.PreHitCheckCritChance + local forkMultiChance = (critChancePercentage ^ 2) / preHitCheckCritChance + + local damageBonus = extraDamage + local forkedBonus = forkMultiChance * extraDamage / 100 + + extraDamage = damageBonus + forkedBonus + skillModList:NewMod("CritMultiplier", "MORE", m_floor(forkMultiChance), "Forked Crit Damage Bonus") + end + if env.mode_effective then local enemyInc = 1 + enemyDB:Sum("INC", nil, "SelfCritMultiplier") / 100 extraDamage = extraDamage + enemyDB:Sum("BASE", nil, "SelfCritMultiplier") / 100 @@ -3233,6 +3260,8 @@ function calcs.offence(env, actor, activeSkill) } end end + + output.CritMultiplier = 1 + m_max(0, extraDamage) end local critChancePercentage = output.CritChance / 100 @@ -3896,6 +3925,7 @@ function calcs.offence(env, actor, activeSkill) -- Combine crit stats, average damage and DPS combineStat("PreEffectiveCritChance", "AVERAGE") combineStat("CritChance", "AVERAGE") + combineStat("PreEffectiveCritMultiplier", "AVERAGE") combineStat("CritMultiplier", "AVERAGE") combineStat("AverageDamage", "DPS") combineStat("PvpAverageDamage", "DPS") diff --git a/src/Modules/ModParser.lua b/src/Modules/ModParser.lua index 61ef8ef791..d252377157 100644 --- a/src/Modules/ModParser.lua +++ b/src/Modules/ModParser.lua @@ -3316,6 +3316,7 @@ local specialModList = { ["minion critical hits do not deal extra damage"] = { mod("MinionModifier", "LIST", { mod = flag("NoCritMultiplier") }) }, ["lightning damage with non%-critical hits is lucky"] = { flag("LightningNoCritLucky") }, ["your damage with critical hits is lucky"] = { flag("CritLucky") }, + ["forks critical hits"] = { flag("ForkCrit") }, ["critical hits deal no damage"] = { mod("Damage", "MORE", -100, { type = "Condition", var = "CriticalStrike" }) }, ["critical hit chance is increased by uncapped lightning resistance"] = { flag("CritChanceIncreasedByUncappedLightningRes") }, ["critical hit chance is increased by lightning resistance"] = { flag("CritChanceIncreasedByLightningRes") }, From 8803d9c284a6431c241e885cd48337fdf3792b52 Mon Sep 17 00:00:00 2001 From: Antaresque Date: Tue, 29 Apr 2025 00:22:55 +0200 Subject: [PATCH 2/6] Failsafe for 100% crit chance override --- src/Modules/CalcOffence.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Modules/CalcOffence.lua b/src/Modules/CalcOffence.lua index 48dddd215c..f373708199 100644 --- a/src/Modules/CalcOffence.lua +++ b/src/Modules/CalcOffence.lua @@ -3151,6 +3151,8 @@ function calcs.offence(env, actor, activeSkill) if critOverride == 100 then output.PreEffectiveCritChance = 100 + output.PreForkCritChance = 100 + output.PreHitCheckCritChance = 100 output.CritChance = 100 else local base = 0 From d2c9b451b5e42700fdc0f76362495b2118af259a Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Sun, 4 May 2025 23:24:35 +1000 Subject: [PATCH 3/6] Fix Crit effect calculation The forked effective crit multi was using the preHitCheckCritChance value for some reason which was skewing the values from being correct e.g. With 50% crit chance you have a 25% chance for your crit to hit to be a forking crit not 33.33% chance that it was calculating --- src/Modules/CalcOffence.lua | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/Modules/CalcOffence.lua b/src/Modules/CalcOffence.lua index 1ae377273e..5a3003570e 100644 --- a/src/Modules/CalcOffence.lua +++ b/src/Modules/CalcOffence.lua @@ -3240,14 +3240,11 @@ function calcs.offence(env, actor, activeSkill) if env.mode_effective and skillModList:Flag(cfg, "ForkCrit") then -- get crit chance and calculate odds of critting twice local critChancePercentage = output.PreForkCritChance - local preHitCheckCritChance = output.PreHitCheckCritChance - local forkMultiChance = (critChancePercentage ^ 2) / preHitCheckCritChance - + local forkMultiChance = (critChancePercentage ^ 2) / 100 local damageBonus = extraDamage - local forkedBonus = forkMultiChance * extraDamage / 100 - + local forkedBonus = forkMultiChance * extraDamage / 100 extraDamage = damageBonus + forkedBonus - skillModList:NewMod("CritMultiplier", "MORE", m_floor(forkMultiChance), "Forked Crit Damage Bonus") + skillModList:NewMod("CritMultiplier", "MORE", floor(forkMultiChance, 2), "Forked Crit Damage Bonus") end if env.mode_effective then @@ -3262,8 +3259,6 @@ function calcs.offence(env, actor, activeSkill) } end end - - output.CritMultiplier = 1 + m_max(0, extraDamage) end local critChancePercentage = output.CritChance / 100 From d859b8762868213050689f035ba5e57137c3f059 Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Wed, 7 May 2025 05:18:01 +1000 Subject: [PATCH 4/6] Fix Calcs when accuracy is below 100% The game uses the effective crit chance when calculating the chance for lucky or forked crits so we need to incorporate the effect from accuracy earlier --- src/Modules/CalcOffence.lua | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/Modules/CalcOffence.lua b/src/Modules/CalcOffence.lua index 5a3003570e..d2ed475233 100644 --- a/src/Modules/CalcOffence.lua +++ b/src/Modules/CalcOffence.lua @@ -3152,7 +3152,6 @@ function calcs.offence(env, actor, activeSkill) if critOverride == 100 then output.PreEffectiveCritChance = 100 output.PreForkCritChance = 100 - output.PreHitCheckCritChance = 100 output.CritChance = 100 else local base = 0 @@ -3170,6 +3169,10 @@ function calcs.offence(env, actor, activeSkill) output.CritChance = m_max(output.CritChance, 0) end output.PreEffectiveCritChance = output.CritChance + local preHitCheckCritChance = output.CritChance + if env.mode_effective then + output.CritChance = output.CritChance * output.AccuracyHitChance / 100 + end local preLuckyCritChance = output.CritChance if env.mode_effective and skillModList:Flag(cfg, "CritChanceLucky") then output.CritChance = (1 - (1 - output.CritChance / 100) ^ 2) * 100 @@ -3179,11 +3182,6 @@ function calcs.offence(env, actor, activeSkill) if env.mode_effective and skillModList:Flag(cfg, "ForkCrit") then output.CritChance = (1 - (1 - output.CritChance / 100) ^ 2) * 100 end - output.PreHitCheckCritChance = output.CritChance - local preHitCheckCritChance = output.CritChance - if env.mode_effective then - output.CritChance = output.CritChance * output.AccuracyHitChance / 100 - end if breakdown and output.CritChance ~= baseCrit then breakdown.CritChance = { } local baseCritFromMainHandStr = baseCritFromMainHand and " from main weapon" or baseCritFromParentMainHand and " from parent main weapon" or "" @@ -3203,6 +3201,13 @@ function calcs.offence(env, actor, activeSkill) local overCap = preCapCritChance - 100 t_insert(breakdown.CritChance, s_format("Crit is overcapped by %.2f%% (%d%% increased Critical Hit Chance)", overCap, overCap / more / (baseCrit + base) * 100)) end + if env.mode_effective and output.AccuracyHitChance < 100 then + t_insert(breakdown.CritChance, "") + t_insert(breakdown.CritChance, "Effective Crit Chance:") + t_insert(breakdown.CritChance, s_format("%.2f%%", preHitCheckCritChance)) + t_insert(breakdown.CritChance, s_format("x %.2f ^8(chance to hit)", output.AccuracyHitChance / 100)) + t_insert(breakdown.CritChance, s_format("= %.2f%%", preLuckyCritChance)) + end if env.mode_effective and skillModList:Flag(cfg, "CritChanceLucky") then t_insert(breakdown.CritChance, "Crit Chance is Lucky:") t_insert(breakdown.CritChance, s_format("1 - (1 - %.4f) x (1 - %.4f)", preLuckyCritChance / 100, preLuckyCritChance / 100)) @@ -3211,12 +3216,6 @@ function calcs.offence(env, actor, activeSkill) if env.mode_effective and skillModList:Flag(cfg, "ForkCrit") then t_insert(breakdown.CritChance, "Critical Strike Forks:") t_insert(breakdown.CritChance, s_format("1 - (1 - %.4f) x (1 - %.4f)", preForkCritChance / 100, preForkCritChance / 100)) - t_insert(breakdown.CritChance, s_format("= %.2f%%", preHitCheckCritChance)) - end - if env.mode_effective and output.AccuracyHitChance < 100 then - t_insert(breakdown.CritChance, "Crit confirmation roll:") - t_insert(breakdown.CritChance, s_format("%.2f%%", preHitCheckCritChance)) - t_insert(breakdown.CritChance, s_format("x %.2f ^8(chance to hit)", output.AccuracyHitChance / 100)) t_insert(breakdown.CritChance, s_format("= %.2f%%", output.CritChance)) end end From 7550bd16a35e4e518c1e0eed1a4d73c20b3baeef Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Wed, 7 May 2025 05:38:59 +1000 Subject: [PATCH 5/6] Add breakdown for forked crit chance --- src/Modules/BuildDisplayStats.lua | 2 +- src/Modules/CalcOffence.lua | 9 +++++++++ src/Modules/CalcSections.lua | 8 ++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Modules/BuildDisplayStats.lua b/src/Modules/BuildDisplayStats.lua index ff4b7e0910..56214a3465 100644 --- a/src/Modules/BuildDisplayStats.lua +++ b/src/Modules/BuildDisplayStats.lua @@ -32,8 +32,8 @@ local displayStats = { { stat = "ReloadTime", label = "Reload Time", fmt = ".2fs", compPercent = true, lowerIsBetter = true, condFunc = function(v,o) return o.ReloadTime end }, { stat = "PreEffectiveCritChance", label = "Crit Chance", fmt = ".2f%%", flag = "hit" }, { stat = "CritChance", label = "Effective Crit Chance", fmt = ".2f%%", flag = "hit", condFunc = function(v,o) return v ~= o.PreEffectiveCritChance end }, + { stat = "CritForks", label = "Crit Forks Chance", fmt = ".2f%%", flag = "hit", condFunc = function(v,o) return (o.CritForks or 0) > 0 end }, { stat = "CritMultiplier", label = "Crit Multiplier", fmt = "d%%", pc = true, condFunc = function(v,o) return (o.CritChance or 0) > 0 end }, - --{ stat = "CritMultiplier", label = "Effective Crit Multiplier", fmt = "d%%", pc = true, condFunc = function(v,o) return (o.CritChance or 0) > 0 and v ~= o.PreEffectiveCritMultiplier end }, { stat = "HitChance", label = "Hit Chance", fmt = ".0f%%", flag = "attack" }, { stat = "HitChance", label = "Hit Chance", fmt = ".0f%%", condFunc = function(v,o) return o.enemyHasSpellBlock end }, { stat = "AccuracyHitChanceUncapped", label = "Uncap. Hit Chance", fmt = ".0f%%", flag = "attack", condFunc = function(v,o) if o.AccuracyHitChanceUncapped then return o.AccuracyHitChanceUncapped > 100 end end }, diff --git a/src/Modules/CalcOffence.lua b/src/Modules/CalcOffence.lua index d2ed475233..3b21504180 100644 --- a/src/Modules/CalcOffence.lua +++ b/src/Modules/CalcOffence.lua @@ -3240,8 +3240,16 @@ function calcs.offence(env, actor, activeSkill) -- get crit chance and calculate odds of critting twice local critChancePercentage = output.PreForkCritChance local forkMultiChance = (critChancePercentage ^ 2) / 100 + output.CritForks = forkMultiChance local damageBonus = extraDamage local forkedBonus = forkMultiChance * extraDamage / 100 + if breakdown and enemyInc ~= 1 then + breakdown.CritForks = { + s_format("%.2f%% ^8(effective crit chance)", critChancePercentage), + s_format("x %.2f%%", critChancePercentage), + s_format("= %d%% ^8(crit forks chance)", forkMultiChance), + } + end extraDamage = damageBonus + forkedBonus skillModList:NewMod("CritMultiplier", "MORE", floor(forkMultiChance, 2), "Forked Crit Damage Bonus") end @@ -3923,6 +3931,7 @@ function calcs.offence(env, actor, activeSkill) combineStat("CritChance", "AVERAGE") combineStat("PreEffectiveCritMultiplier", "AVERAGE") combineStat("CritMultiplier", "AVERAGE") + combineStat("CritForks", "AVERAGE") combineStat("AverageDamage", "DPS") combineStat("PvpAverageDamage", "DPS") combineStat("TotalDPS", "DPS") diff --git a/src/Modules/CalcSections.lua b/src/Modules/CalcSections.lua index a057b7d9b6..0e577bbab1 100644 --- a/src/Modules/CalcSections.lua +++ b/src/Modules/CalcSections.lua @@ -546,6 +546,10 @@ return { { label = "Player modifiers", modName = { "CritChance", "WeaponBaseCritChance", "MainHandCritIsEqualToParent", "MainHandCritIsEqualToPartyMember", "AttackCritIsEqualToParentMainHand" }, cfg = "weapon1" }, { label = "Enemy modifiers", modName = "SelfCritChance", enemy = true }, }, }, + { label = "MH Crit Forks", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack", { format = "{2:output:MainHand.CritForks}%", + { breakdown = "MainHand.CritForks" }, + { label = "Player modifiers", modName = "ForkCrit", cfg = "weapon1" }, + }, }, { label = "MH Crit Multiplier", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack", { format = "x {2:output:MainHand.CritMultiplier}", { breakdown = "MainHand.CritMultiplier" }, { label = "Player modifiers", modName = "CritMultiplier", cfg = "weapon1" }, @@ -562,6 +566,10 @@ return { { label = "Player modifiers", modName = { "CritChance", "WeaponBaseCritChance", "AttackCritIsEqualToParentMainHand" }, cfg = "weapon2" }, { label = "Enemy modifiers", modName = "SelfCritChance", enemy = true }, }, }, + { label = "OH Crit Forks", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack", { format = "{2:output:OffHand.CritForks}%", + { breakdown = "OffHand.CritForks" }, + { label = "Player modifiers", modName = "ForkCrit", cfg = "weapon2" }, + }, }, { label = "OH Crit Multiplier", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack", { format = "x {2:output:OffHand.CritMultiplier}", { breakdown = "OffHand.CritMultiplier" }, { label = "Player modifiers", modName = "CritMultiplier", cfg = "weapon2" }, From 3325230d55c13a65258a53ab01984493326d1d10 Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Wed, 7 May 2025 05:45:08 +1000 Subject: [PATCH 6/6] Breakdown Formatting --- src/Modules/CalcOffence.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Modules/CalcOffence.lua b/src/Modules/CalcOffence.lua index 3b21504180..7e2aba5408 100644 --- a/src/Modules/CalcOffence.lua +++ b/src/Modules/CalcOffence.lua @@ -3247,7 +3247,7 @@ function calcs.offence(env, actor, activeSkill) breakdown.CritForks = { s_format("%.2f%% ^8(effective crit chance)", critChancePercentage), s_format("x %.2f%%", critChancePercentage), - s_format("= %d%% ^8(crit forks chance)", forkMultiChance), + s_format("= %.2f%% ^8(crit forks chance)", forkMultiChance), } end extraDamage = damageBonus + forkedBonus