Skip to content

Commit f4c10fa

Browse files
authored
Fix effective inherited cooldown not being affected by correct conditional icdr in hybrid triggers (#8717)
* FIX: inherited cooldown not being affected by icdr in hybrid triggers * MISC: cleanup whitespace, improve comment
1 parent b205f24 commit f4c10fa

4 files changed

Lines changed: 86 additions & 51 deletions

File tree

src/Modules/Build.lua

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,38 @@ local function InsertIfNew(t, val)
2323
table.insert(t, val)
2424
end
2525

26+
---matchFlags
27+
--- Compares the skill flags table against the line flag settings
28+
--- Required enabling flags check takes precedence over disabling flags check
29+
---@param reqFlags table containing the required flags
30+
---@param notFlags table containing the disabling flags
31+
---@param flags table containing the flags to match against
32+
local function matchFlags(reqFlags, notFlags, flags)
33+
if type(reqFlags) == "string" then
34+
reqFlags = { reqFlags }
35+
end
36+
if reqFlags then
37+
for _, flag in ipairs(reqFlags) do
38+
if not flags[flag] then
39+
return
40+
end
41+
end
42+
end
43+
44+
if type(notFlags) == "string" then
45+
notFlags = { notFlags }
46+
end
47+
if notFlags then
48+
for _, flag in ipairs(notFlags) do
49+
if flags[flag] then
50+
return
51+
end
52+
end
53+
end
54+
-- Both flag checks passed, default true
55+
return true
56+
end
57+
2658
function buildMode:Init(dbFileName, buildName, buildXML, convertBuild, importLink)
2759
self.dbFileName = dbFileName
2860
self.buildName = buildName
@@ -934,7 +966,7 @@ function buildMode:Save(xml)
934966
end
935967
local addedStatNames = { }
936968
for index, statData in ipairs(self.displayStats) do
937-
if not statData.flag or self.calcsTab.mainEnv.player.mainSkill.skillFlags[statData.flag] then
969+
if matchFlags(statData.flag, statData.notFlag, self.calcsTab.mainEnv.player.mainSkill.skillFlags) then
938970
local statName = statData.stat and statData.stat..(statData.childStat or "")
939971
if statName and not addedStatNames[statName] then
940972
if statData.stat == "SkillDPS" then
@@ -1479,7 +1511,7 @@ end
14791511
function buildMode:AddDisplayStatList(statList, actor)
14801512
local statBoxList = self.controls.statBox.list
14811513
for index, statData in ipairs(statList) do
1482-
if not statData.flag or actor.mainSkill.skillFlags[statData.flag] then
1514+
if matchFlags(statData.flag, statData.notFlag, actor.mainSkill.skillFlags) then
14831515
local labelColor = "^7"
14841516
if statData.color then
14851517
labelColor = statData.color
@@ -1650,7 +1682,7 @@ end
16501682
function buildMode:CompareStatList(tooltip, statList, actor, baseOutput, compareOutput, header, nodeCount)
16511683
local count = 0
16521684
for _, statData in ipairs(statList) do
1653-
if statData.stat and (not statData.flag or actor.mainSkill.skillFlags[statData.flag]) and not statData.childStat and statData.stat ~= "SkillDPS" then
1685+
if statData.stat and matchFlags(statData.flag, statData.notFlag, actor.mainSkill.skillFlags) and not statData.childStat and statData.stat ~= "SkillDPS" then
16541686
local statVal1 = compareOutput[statData.stat] or 0
16551687
local statVal2 = baseOutput[statData.stat] or 0
16561688
local diff = statVal1 - statVal2

src/Modules/BuildDisplayStats.lua

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ local displayStats = {
1717
{ stat = "PvpAverageDamage", label = "PvP Average Damage", fmt = ".1f", compPercent = true, flag = "attackPvP" },
1818
{ stat = "Speed", label = "Attack Rate", fmt = ".2f", compPercent = true, flag = "attack", condFunc = function(v,o) return v > 0 and (o.TriggerTime or 0) == 0 end },
1919
{ stat = "Speed", label = "Cast Rate", fmt = ".2f", compPercent = true, flag = "spell", condFunc = function(v,o) return v > 0 and (o.TriggerTime or 0) == 0 end },
20-
{ stat = "Speed", label = "Effective Trigger Rate", fmt = ".2f", compPercent = true, condFunc = function(v,o) return (o.TriggerTime or 0) ~= 0 end },
20+
{ stat = "Speed", label = "Effective Trigger Rate", fmt = ".2f", compPercent = true, notFlag = "skipEffectiveRate", condFunc = function(v,o) return (o.TriggerTime or 0) ~= 0 end },
2121
{ stat = "WarcryCastTime", label = "Cast Time", fmt = ".2fs", compPercent = true, lowerIsBetter = true, flag = "warcry" },
2222
{ stat = "HitSpeed", label = "Hit Rate", fmt = ".2f", compPercent = true, condFunc = function(v,o) return not o.TriggerTime end },
2323
{ stat = "HitTime", label = "Channel Time", fmt = ".2fs", compPercent = true, flag = "channelRelease", lowerIsBetter = true, condFunc = function(v,o) return not o.TriggerTime end },
@@ -67,7 +67,7 @@ local displayStats = {
6767
{ stat = "CombinedAvg", label = "Combined Total Damage", fmt = ".1f", compPercent = true, flag = "showAverage", condFunc = function(v,o) return (v ~= o.AverageDamage and (o.TotalDot or 0) == 0) and (v ~= o.WithPoisonDPS or v ~= o.WithIgniteDPS or v ~= o.WithBleedDPS) end },
6868
{ stat = "ExplodeChance", label = "Total Explode Chance", fmt = ".0f%%" },
6969
{ stat = "CombinedAvgToMonsterLife", label = "Enemy Life Equivalent", fmt = ".1f%%" },
70-
{ stat = "Cooldown", label = "Skill Cooldown", fmt = ".3fs", lowerIsBetter = true },
70+
{ stat = "Cooldown", label = "Skill Cooldown", fmt = ".3fs", notFlag = "skipEffectiveRate", lowerIsBetter = true },
7171
{ stat = "SealCooldown", label = "Seal Gain Frequency", fmt = ".2fs", lowerIsBetter = true },
7272
{ stat = "SealMax", label = "Max Number of Seals", fmt = "d" },
7373
{ stat = "TimeMaxSeals", label = "Time to Gain Max Seals", fmt = ".2fs", lowerIsBetter = true },
@@ -207,7 +207,7 @@ local minionDisplayStats = {
207207
{ stat = "AverageDamage", label = "Average Damage", fmt = ".1f", compPercent = true },
208208
{ stat = "Speed", label = "Attack/Cast Rate", fmt = ".2f", compPercent = true, condFunc = function(v,o) return v > 0 and (o.TriggerTime or 0) == 0 end },
209209
{ stat = "HitSpeed", label = "Hit Rate", fmt = ".2f" },
210-
{ stat = "Speed", label = "Effective Trigger Rate", fmt = ".2f", compPercent = true, condFunc = function(v,o) return (o.TriggerTime or 0) ~= 0 end },
210+
{ stat = "Speed", label = "Effective Trigger Rate", fmt = ".2f", compPercent = true, notFlag = "skipEffectiveRate", condFunc = function(v,o) return (o.TriggerTime or 0) ~= 0 end },
211211
{ stat = "TotalDPS", label = "Hit DPS", fmt = ".1f", compPercent = true },
212212
{ stat = "TotalDot", label = "DoT DPS", fmt = ".1f", compPercent = true },
213213
{ stat = "WithDotDPS", label = "Total DPS inc. DoT", fmt = ".1f", compPercent = true, condFunc = function(v,o) return v ~= o.TotalDPS and (o.PoisonDPS or 0) == 0 and (o.IgniteDPS or 0) == 0 and (o.ImpaleDPS or 0) == 0 and (o.BleedDPS or 0) == 0 end },
@@ -225,7 +225,7 @@ local minionDisplayStats = {
225225
{ stat = "CullingDPS", label = "Culling DPS", fmt = ".1f", compPercent = true, condFunc = function(v,o) return (o.CullingDPS or 0) > 0 end },
226226
{ stat = "ReservationDPS", label = "Reservation DPS", fmt = ".1f", compPercent = true, condFunc = function(v,o) return (o.ReservationDPS or 0) > 0 end },
227227
{ stat = "CombinedDPS", label = "Combined DPS", fmt = ".1f", compPercent = true, condFunc = function(v,o) return v ~= ((o.TotalDPS or 0) + (o.TotalDot or 0)) and v ~= o.WithImpaleDPS and v ~= o.WithPoisonDPS and v ~= o.WithIgniteDPS and v ~= o.WithBleedDPS end},
228-
{ stat = "Cooldown", label = "Skill Cooldown", fmt = ".3fs", lowerIsBetter = true },
228+
{ stat = "Cooldown", label = "Skill Cooldown", fmt = ".3fs", notFlag = "skipEffectiveRate", lowerIsBetter = true },
229229
{ stat = "Life", label = "Total Life", fmt = ".1f", color = colorCodes.LIFE, compPercent = true },
230230
{ stat = "LifeRegenRecovery", label = "Life Recovery", fmt = ".1f", color = colorCodes.LIFE },
231231
{ stat = "LifeLeechGainRate", label = "Life Leech/On Hit Rate", fmt = ".1f", color = colorCodes.LIFE, compPercent = true },

src/Modules/CalcSections.lua

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -493,12 +493,12 @@ return {
493493
{ label = "More Cast Speed", flag = "spell", notFlag = "triggered", { format = "{0:mod:1}%", { modName = "Speed", modType = "MORE", cfg = "skill", }, }, },
494494
{ label = "Casts per second", flag = "spell", notFlag = "triggered", { format = "{2:output:Speed}", { breakdown = "Speed" }, }, },
495495
{ label = "Cast Time", flag = "addsCastTime", { format = "{2:output:addsCastTime}", { breakdown = "AddedCastTime" }, }, },
496-
{ label = "Trigger Rate Cap", flag = "triggered", notFlagList = {"focused", "hasOverride"}, { format = "{2:output:TriggerRateCap}", { breakdown = "TriggerRateCap" }, { modName = "CooldownRecovery", modType = "INC", cfg = "skill", }, }, },
497-
{ label = "Trigger Rate Cap", flagList = {"triggered", "hasOverride"}, notFlag = "focused", { format = "{2:output:TriggerRateCap}", { breakdown = "TriggerRateCap" }, { modName = "CooldownRecovery", modType = "OVERRIDE", cfg = "skill", }, }, },
498-
{ label = "Trigger Rate Cap", flagList = {"triggered", "focused"}, { format = "{2:output:TriggerRateCap}", { breakdown = "TriggerRateCap" }, { modName = "FocusCooldownRecovery", modType = "INC", cfg = "skill", }, }, },
499-
{ label = "Eff. Source Rate", flag = "triggered", notFlag = "focused", notFlag = "globalTrigger", { format = "{2:output:EffectiveSourceRate}", { breakdown = "EffectiveSourceRate" } }, },
500-
{ label = "Skill Trigger Rate", flag = "triggered", notFlag = "focused", { format = "{2:output:SkillTriggerRate}", { breakdown = "SkillTriggerRate" }, { breakdown = "SimData" }, }, },
501-
{ label = "Skill Trigger Rate", flagList = {"triggered", "focused"}, { format = "{2:output:SkillTriggerRate}", { breakdown = "SkillTriggerRate" }, { breakdown = "SimData" }, { modName = "FocusCooldownRecovery", modType = "INC", cfg = "skill", }, }, },
496+
{ label = "Trigger Rate Cap", flag = "triggered", notFlagList = {"focused", "hasOverride", "skipEffectiveRate"}, { format = "{2:output:TriggerRateCap}", { breakdown = "TriggerRateCap" }, { modName = "CooldownRecovery", modType = "INC", cfg = "skill", }, }, },
497+
{ label = "Trigger Rate Cap", flagList = {"triggered", "hasOverride"}, notFlagList = { "focused", "skipEffectiveRate" }, { format = "{2:output:TriggerRateCap}", { breakdown = "TriggerRateCap" }, { modName = "CooldownRecovery", modType = "OVERRIDE", cfg = "skill", }, }, },
498+
{ label = "Trigger Rate Cap", flagList = {"triggered", "focused"}, notFlag = "skipEffectiveRate", { format = "{2:output:TriggerRateCap}", { breakdown = "TriggerRateCap" }, { modName = "FocusCooldownRecovery", modType = "INC", cfg = "skill", }, }, },
499+
{ label = "Eff. Source Rate", flag = "triggered", notFlagList = { "focused", "globalTrigger", "skipEffectiveRate" }, { format = "{2:output:EffectiveSourceRate}", { breakdown = "EffectiveSourceRate" } }, },
500+
{ label = "Skill Trigger Rate", flag = "triggered", notFlagList = { "focused", "skipEffectiveRate" }, { format = "{2:output:SkillTriggerRate}", { breakdown = "SkillTriggerRate" }, { breakdown = "SimData" }, }, },
501+
{ label = "Skill Trigger Rate", flagList = {"triggered", "focused"}, notFlag = "skipEffectiveRate", { format = "{2:output:SkillTriggerRate}", { breakdown = "SkillTriggerRate" }, { breakdown = "SimData" }, { modName = "FocusCooldownRecovery", modType = "INC", cfg = "skill", }, }, },
502502
{ label = "Cast time", flag = "spell", notFlag = "triggered", { format = "{2:output:Time}s", }, },
503503
{ label = "CWDT Threshold", haveOutput = "CWDTThreshold", flag = "triggered", { format = "{2:output:CWDTThreshold}", { breakdown = "CWDTThreshold" }, }, },
504504
{ label = "Channel time", flag = "channelRelease", haveOutput = "HitTime", { format = "{2:output:HitTime}s", { breakdown = "HitTime" } }, },
@@ -621,7 +621,7 @@ return {
621621
{ breakdown = "QuantityMultiplier" },
622622
{ modName = { "QuantityMultiplier" }, cfg = "skill" },
623623
}, },
624-
{ label = "Skill Cooldown", haveOutput = "Cooldown", { format = "{3:output:Cooldown}s",
624+
{ label = "Skill Cooldown", haveOutput = "Cooldown", notFlag = "skipEffectiveRate", { format = "{3:output:Cooldown}s",
625625
{ breakdown = "Cooldown" },
626626
{ modName = "CooldownRecovery", cfg = "skill" },
627627
}, },

src/Modules/CalcTriggers.lua

Lines changed: 40 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -719,7 +719,7 @@ local function defaultTriggerHandler(env, config)
719719

720720
local skillName = (source and source.activeEffect.grantedEffect.name) or (actor.mainSkill.triggeredBy and actor.mainSkill.triggeredBy.grantedEffect.name) or actor.mainSkill.activeEffect.grantedEffect.name
721721

722-
if output.EffectiveSourceRate ~= 0 then
722+
if output.EffectiveSourceRate ~= 0 and not env.player.mainSkill.skillFlags.skipEffectiveRate then
723723
local triggerChance = 100
724724
local triggerChanceBreakdown = {}
725725

@@ -1138,67 +1138,70 @@ local configTable = {
11381138
return {triggerChance = env.player.mainSkill.skillData.chanceToTriggerOnStun,
11391139
source = env.player.mainSkill}
11401140
end,
1141-
["automation"] = function(env)
1142-
if env.player.mainSkill.activeEffect.grantedEffect.name == "Automation" then
1143-
-- This calculated the trigger rate of the Automation gem it self
1144-
env.player.mainSkill.skillFlags.globalTrigger = true
1145-
return {source = env.player.mainSkill}
1146-
end
1147-
env.player.mainSkill.skillData.sourceRateIsFinal = true
1148-
1149-
-- Trigger rate of the triggered skill is capped by the cooldown of Automation
1150-
-- which will likely be different from the cooldown of the triggered skill
1151-
-- and is affected by different cooldown modifiers
1152-
env.player.mainSkill.skillData.ignoresTickRate = true
1153-
1154-
-- This basically does min(trigger rate of steelskin assuming no trigger cooldown, trigger rate of Automation)
1155-
return {triggerOnUse = true,
1156-
useCastRate = true,
1157-
triggerSkillCond = function(env, skill)
1158-
return skill.activeEffect.grantedEffect.name == "Automation"
1159-
end}
1160-
end,
11611141
["spellslinger"] = function(env)
11621142
if env.player.mainSkill.activeEffect.grantedEffect.name == "Spellslinger" then
1163-
return {triggerName = "Spellslinger",
1143+
env.player.mainSkill.skillFlags.skipEffectiveRate = true
1144+
end
1145+
-- Spell slinger adds a cooldown with its support part
1146+
env.player.mainSkill.skillData.sourceRateIsFinal = true
1147+
return {triggerName = "Spellslinger",
11641148
triggerOnUse = true,
11651149
triggerSkillCond = function(env, skill)
11661150
local isWandAttack = (not skill.weaponTypes or (skill.weaponTypes and skill.weaponTypes["Wand"])) and skill.skillTypes[SkillType.Attack]
11671151
return isWandAttack and not skill.skillData.triggeredBySpellSlinger
11681152
end}
1169-
end
1170-
env.player.mainSkill.skillData.sourceRateIsFinal = true
1171-
return {triggerOnUse = true,
1172-
useCastRate = true,
1173-
triggerSkillCond = function(env, skill)
1174-
return skill.activeEffect.grantedEffect.name == "Spellslinger"
1175-
end}
11761153
end,
11771154
["call to arms"] = function(env)
11781155
if env.player.mainSkill.activeEffect.grantedEffect.name == "Call to Arms" then
11791156
env.player.mainSkill.skillFlags.globalTrigger = true
1180-
return {source = env.player.mainSkill}
1157+
env.player.mainSkill.skillFlags.skipEffectiveRate = true
1158+
else -- Needed to get the cooldown form the active part
1159+
for _, skill in ipairs(env.player.activeSkillList) do
1160+
if skill.activeEffect.grantedEffect.name == "Call to Arms" then
1161+
env.player.mainSkill.triggeredBy.grantedEffect = skill.activeEffect.grantedEffect
1162+
break
1163+
end
1164+
end
11811165
end
11821166
env.player.mainSkill.skillData.sourceRateIsFinal = true
11831167
env.player.mainSkill.skillData.ignoresTickRate = true
11841168
return {triggerOnUse = true,
11851169
useCastRate = true,
1186-
triggerSkillCond = function(env, skill)
1187-
return skill.activeEffect.grantedEffect.name == "Call to Arms"
1188-
end}
1170+
source = env.player.mainSkill}
1171+
end,
1172+
["automation"] = function(env)
1173+
if env.player.mainSkill.activeEffect.grantedEffect.name == "Automation" then
1174+
env.player.mainSkill.skillFlags.globalTrigger = true
1175+
env.player.mainSkill.skillFlags.skipEffectiveRate = true
1176+
else -- Needed to get the cooldown form the active part
1177+
for _, skill in ipairs(env.player.activeSkillList) do
1178+
if skill.activeEffect.grantedEffect.name == "Automation" then
1179+
env.player.mainSkill.triggeredBy.grantedEffect = skill.activeEffect.grantedEffect
1180+
break
1181+
end
1182+
end
1183+
end
1184+
return {triggerOnUse = true,
1185+
useCastRate = true,
1186+
source = env.player.mainSkill}
11891187
end,
11901188
["autoexertion"] = function(env)
11911189
if env.player.mainSkill.activeEffect.grantedEffect.name == "Autoexertion" then
11921190
env.player.mainSkill.skillFlags.globalTrigger = true
1193-
return {source = env.player.mainSkill}
1191+
env.player.mainSkill.skillFlags.skipEffectiveRate = true
1192+
else -- Needed to get the cooldown form the active part
1193+
for _, skill in ipairs(env.player.activeSkillList) do
1194+
if skill.activeEffect.grantedEffect.name == "Automation" then
1195+
env.player.mainSkill.triggeredBy.grantedEffect = skill.activeEffect.grantedEffect
1196+
break
1197+
end
1198+
end
11941199
end
11951200
env.player.mainSkill.skillData.sourceRateIsFinal = true
11961201
env.player.mainSkill.skillData.ignoresTickRate = true
11971202
return {triggerOnUse = true,
11981203
useCastRate = true,
1199-
triggerSkillCond = function(env, skill)
1200-
return skill.activeEffect.grantedEffect.name == "Autoexertion"
1201-
end}
1204+
source = env.player.mainSkill}
12021205
end,
12031206
["mark on hit"] = function()
12041207
return {triggerSkillCond = function(env, skill) return skill.skillTypes[SkillType.Attack] end}

0 commit comments

Comments
 (0)