Skip to content

Add support for Forking Critical Strikes #1100

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/Data/ModCache.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3815,8 +3815,7 @@ c["Fire Exposure you inflict lowers Total Fire Resistance by an extra 5%"]={{[1]
c["Fire Resistance is unaffected by Area Penalties"]={nil,"Fire Resistance is unaffected by Area Penalties "}
c["Fire Resistance is unaffected by Area Penalties Lightning Resistance is unaffected by Area Penalties"]={nil,"Fire Resistance is unaffected by Area Penalties Lightning Resistance is unaffected by Area Penalties "}
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 "}
Expand Down
1 change: 1 addition & 0 deletions src/Modules/BuildDisplayStats.lua
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ 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 = "HitChance", label = "Hit Chance", fmt = ".0f%%", flag = "attack" },
{ stat = "HitChance", label = "Hit Chance", fmt = ".0f%%", condFunc = function(v,o) return o.enemyHasSpellBlock end },
Expand Down
51 changes: 43 additions & 8 deletions src/Modules/CalcOffence.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3151,6 +3151,7 @@ function calcs.offence(env, actor, activeSkill)

if critOverride == 100 then
output.PreEffectiveCritChance = 100
output.PreForkCritChance = 100
output.CritChance = 100
else
local base = 0
Expand All @@ -3168,13 +3169,18 @@ 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
end
local preHitCheckCritChance = output.CritChance
if env.mode_effective then
output.CritChance = output.CritChance * output.AccuracyHitChance / 100
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
if breakdown and output.CritChance ~= baseCrit then
breakdown.CritChance = { }
Expand All @@ -3195,15 +3201,21 @@ 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))
t_insert(breakdown.CritChance, s_format("= %.2f%%", preHitCheckCritChance))
t_insert(breakdown.CritChance, s_format("= %.2f%%", preForkCritChance))
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))
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%%", output.CritChance))
end
end
Expand All @@ -3221,6 +3233,27 @@ 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 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("= %.2f%% ^8(crit forks chance)", forkMultiChance),
}
end
extraDamage = damageBonus + forkedBonus
skillModList:NewMod("CritMultiplier", "MORE", floor(forkMultiChance, 2), "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
Expand Down Expand Up @@ -3896,7 +3929,9 @@ 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("CritForks", "AVERAGE")
combineStat("AverageDamage", "DPS")
combineStat("PvpAverageDamage", "DPS")
combineStat("TotalDPS", "DPS")
Expand Down
8 changes: 8 additions & 0 deletions src/Modules/CalcSections.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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" },
Expand All @@ -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" },
Expand Down
1 change: 1 addition & 0 deletions src/Modules/ModParser.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3330,6 +3330,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") },
Expand Down
Loading