From 17e08efbc6e86bad5a7f697fb16a72b3e665d250 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Sat, 7 Sep 2024 19:22:54 -0700 Subject: [PATCH] feat: #2144 add support for modRate/timeMod --- CHANGELOG.md | 1 + Components/Core/Common/Cooldowns.lua | 1 + .../Conditions/Categories/BuffsDebuffs.lua | 31 ++++++++---- .../Core/Conditions/Categories/Spells.lua | 12 +++-- Components/Core/Icon.lua | 47 ++++++++++--------- Components/Core/IconDataProcessor.lua | 26 ++++++++-- Components/Core/IconDataProcessors.lua | 29 +++++++----- Components/Core/IconType.lua | 9 ++-- .../Alpha_DurationReq.lua | 4 +- .../CooldownSweep.lua | 12 +++-- Components/IconTypes/IconType_buff/buff.lua | 8 ++-- .../IconType_buffcheck/buffcheck.lua | 8 ++-- .../IconTypes/IconType_cooldown/cooldown.lua | 8 ++-- Components/IconTypes/IconType_meta/meta.lua | 2 +- .../IconTypes/IconType_reactive/reactive.lua | 4 +- Options/CHANGELOG.lua | 1 + TellMeWhen.lua | 8 +--- 17 files changed, 127 insertions(+), 84 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35fedba5..cbab4177 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## v11.0.7 * #1857 - Add LibCustomGlow animations. +* #2144 - TMW now accounts for adjusted-rate cooldowns and auras (e.g. timer freezes caused by talents or boss mechanics). * Fix: #2191 - Icon Shown condition ignoring Shown/Hidden checkboxes for disabled icons/groups. * Fix: #2193 - Icon overlay and border animations starting in the wrong state. * Fix: #2215 - Spell Charges condition not updating for countable spells without true charges. diff --git a/Components/Core/Common/Cooldowns.lua b/Components/Core/Common/Cooldowns.lua index b1b270ed..1322da63 100644 --- a/Components/Core/Common/Cooldowns.lua +++ b/Components/Core/Common/Cooldowns.lua @@ -99,6 +99,7 @@ else maxCharges = maxCharges, cooldownStartTime = cooldownStartTime, cooldownDuration = cooldownDuration, + chargeModRate = 1 } or false CachedCharges[spell] = cached return cached diff --git a/Components/Core/Conditions/Categories/BuffsDebuffs.lua b/Components/Core/Conditions/Categories/BuffsDebuffs.lua index 8a4d8c62..fb80ea9e 100644 --- a/Components/Core/Conditions/Categories/BuffsDebuffs.lua +++ b/Components/Core/Conditions/Categories/BuffsDebuffs.lua @@ -112,7 +112,13 @@ function Env.AuraDur(unit, name, filter) if not data then return 0, 0, 0 else - return data.expirationTime == 0 and huge or data.expirationTime - TMW.time, data.duration, data.expirationTime + local expirationTime = instance.expirationTime + local timeMod = instance.timeMod + return + expirationTime == 0 and huge or ((expirationTime - TMW.time) / timeMod), + data.duration, + expirationTime, + timeMod end end @@ -125,7 +131,12 @@ function Env.AuraDurPacked(unit, name, kindKey, onlyMine) local instance = instances[auraInstanceID] if instance[kindKey] then local expirationTime = instance.expirationTime - return expirationTime == 0 and huge or expirationTime - TMW.time, instance.duration, expirationTime + local timeMod = instance.timeMod + return + expirationTime == 0 and huge or ((expirationTime - TMW.time) / timeMod), + instance.duration, + expirationTime, + timeMod end end end @@ -273,10 +284,10 @@ ConditionCategory:RegisterCondition(1, "BUFFDUR", { and [[AuraDurPacked(c.Unit, c.Spells.First, "isHelpful", ]] .. (tostring(c.Checked)) .. ")" or [[AuraDur(c.Unit, c.Spells.First, "HELPFUL]] .. (c.Checked and " PLAYER" or "") .. [[")]] - return [[local dur, duration, expirationTime = ]] .. getAura .. [[ + return [[local dur, duration, expirationTime, timeMod = ]] .. getAura .. [[ local VALUE if dur and dur > 0 then - VALUE = expirationTime and expirationTime - c.Level or 0 + VALUE = expirationTime and expirationTime - (c.Level * timeMod) or 0 else VALUE = 0 end]] @@ -333,10 +344,10 @@ ConditionCategory:RegisterCondition(2.5, "BUFFPERC", { and [[AuraDurPacked(c.Unit, c.Spells.First, "isHelpful", ]] .. (tostring(c.Checked)) .. ")" or [[AuraDur(c.Unit, c.Spells.First, "HELPFUL]] .. (c.Checked and " PLAYER" or "") .. [[")]] - return [[local dur, duration, expirationTime = ]] .. getAura .. [[ + return [[local dur, duration, expirationTime, timeMod = ]] .. getAura .. [[ local VALUE if dur and dur > 0 then - VALUE = expirationTime and (expirationTime - c.Level*duration) or 0 + VALUE = expirationTime and (expirationTime - c.Level*duration*timeMod) or 0 else VALUE = 0 end]] @@ -538,10 +549,10 @@ ConditionCategory:RegisterCondition(11, "DEBUFFDUR", { and [[AuraDurPacked(c.Unit, c.Spells.First, "isHarmful", ]] .. (tostring(c.Checked)) .. ")" or [[AuraDur(c.Unit, c.Spells.First, "HARMFUL]] .. (c.Checked and " PLAYER" or "") .. [[")]] - return [[local dur, duration, expirationTime = ]] .. getAura .. [[ + return [[local dur, duration, expirationTime, timeMod = ]] .. getAura .. [[ local VALUE if dur and dur > 0 then - VALUE = expirationTime and expirationTime - c.Level or 0 + VALUE = expirationTime and expirationTime - (c.Level*timeMod) or 0 else VALUE = 0 end]] @@ -598,10 +609,10 @@ ConditionCategory:RegisterCondition(12.5,"DEBUFFPERC", { and [[AuraDurPacked(c.Unit, c.Spells.First, "isHarmful", ]] .. (tostring(c.Checked)) .. ")" or [[AuraDur(c.Unit, c.Spells.First, "HARMFUL]] .. (c.Checked and " PLAYER" or "") .. [[")]] - return [[local dur, duration, expirationTime = ]] .. getAura .. [[ + return [[local dur, duration, expirationTime, timeMod = ]] .. getAura .. [[ local VALUE if dur and dur > 0 then - VALUE = expirationTime and (expirationTime - c.Level*duration) or 0 + VALUE = expirationTime and (expirationTime - c.Level*duration*timeMod) or 0 else VALUE = 0 end]] diff --git a/Components/Core/Conditions/Categories/Spells.lua b/Components/Core/Conditions/Categories/Spells.lua index 16ac644d..ab244d7e 100644 --- a/Components/Core/Conditions/Categories/Spells.lua +++ b/Components/Core/Conditions/Categories/Spells.lua @@ -50,13 +50,15 @@ function Env.CooldownDuration(spell, gcdAsUnusable) if spell == "gcd" then local cooldown = GetSpellCooldown(TMW.GCDSpell) local duration = cooldown.duration - return duration == 0 and 0 or (duration - (TMW.time - cooldown.startTime)) + return duration == 0 and 0 or ((duration - (TMW.time - cooldown.startTime)) / cooldown.modRate) end local cooldown = GetSpellCooldown(spell) if cooldown then local duration = cooldown.duration - return ((duration == 0 or (not gcdAsUnusable and OnGCD(duration))) and 0) or (duration - (TMW.time - cooldown.startTime)) + return + ((duration == 0 or (not gcdAsUnusable and OnGCD(duration))) and 0) or + ((duration - (TMW.time - cooldown.startTime)) / cooldown.modRate) end return 0 end @@ -65,7 +67,7 @@ function Env.RechargeDuration(spell) local charges = GetSpellCharges(spell) if charges and charges.currentCharges ~= charges.maxCharges then local duration = charges.cooldownDuration - return (duration == 0 and 0) or (duration - (TMW.time - charges.cooldownStartTime)) + return (duration == 0 and 0) or ((duration - (TMW.time - charges.cooldownStartTime)) / charges.chargeModRate) end return 0 end @@ -113,7 +115,7 @@ ConditionCategory:RegisterCondition(1, "SPELLCD", { anticipate = function(c) local str = [[ local cooldown = GetSpellCooldown(c.OwnSpells.First) - local VALUE = cooldown and cooldown.startTime + (cooldown.duration - c.Level) or huge + local VALUE = cooldown and cooldown.startTime + (cooldown.duration - (c.Level*cooldown.modRate)) or huge ]] if TMW:GetSpells(c.Name).First == "gcd" then str = str:gsub("c.OwnSpells.First", TMW.GCDSpell) @@ -229,7 +231,7 @@ if TMW.isRetail then end, anticipate = [[ local data = GetSpellCharges(c.OwnSpells.First) - local VALUE = data and data.cooldownDuration and data.cooldownStartTime + (data.cooldownDuration - c.Level) or huge + local VALUE = data and data.cooldownDuration and data.cooldownStartTime + (data.cooldownDuration - (c.Level*data.chargeModRate)) or huge ]], }) end diff --git a/Components/Core/Icon.lua b/Components/Core/Icon.lua index 27593532..03f1f25b 100644 --- a/Components/Core/Icon.lua +++ b/Components/Core/Icon.lua @@ -1343,28 +1343,33 @@ local function SetInfo_GenerateFunction(signature, isInternal) local match for _, Processor in ipairs(TMW.Classes.IconDataProcessor.instances) do - match = signature:match("^(" .. Processor.attributesStringNoSpaces .. ")$") -- The attribute string is the only one in the signature - or signature:match("^(" .. Processor.attributesStringNoSpaces .. ";)") -- The attribute string is the first one in the signature - or signature:match("(;" .. Processor.attributesStringNoSpaces .. ")$") -- The attribute string is the last one in the signature - or signature:match(";(" .. Processor.attributesStringNoSpaces .. ";)") -- The attribute string is in the middle of the signature - + for _, attributeString in pairs(Processor.allAttributesStringNoSpaces) do + match = signature:match("^(" .. attributeString .. ")$") -- The attribute string is the only one in the signature + or signature:match("^(" .. attributeString .. ";)") -- The attribute string is the first one in the signature + or signature:match("(;" .. attributeString .. ")$") -- The attribute string is the last one in the signature + or signature:match(";(" .. attributeString .. ";)") -- The attribute string is in the middle of the signature + + if match then + t[#t+1] = "local Processor = " + t[#t+1] = Processor.name + t[#t+1] = "\n" + + -- Process any hooks that should go before the main function segment + Processor:CompileFunctionHooks(t, "pre") + + Processor:CompileFunctionSegment(t) + + -- Process any hooks that should go after the main function segment + Processor:CompileFunctionHooks(t, "post") + + t[#t+1] = "\n\n" + + signature = signature:gsub(match, "", 1) + + break + end + end if match then - t[#t+1] = "local Processor = " - t[#t+1] = Processor.name - t[#t+1] = "\n" - - -- Process any hooks that should go before the main function segment - Processor:CompileFunctionHooks(t, "pre") - - Processor:CompileFunctionSegment(t) - - -- Process any hooks that should go after the main function segment - Processor:CompileFunctionHooks(t, "post") - - t[#t+1] = "\n\n" - - signature = signature:gsub(match, "", 1) - break end end diff --git a/Components/Core/IconDataProcessor.lua b/Components/Core/IconDataProcessor.lua index d7742a74..a07af0ec 100644 --- a/Components/Core/IconDataProcessor.lua +++ b/Components/Core/IconDataProcessor.lua @@ -47,9 +47,10 @@ IconDataProcessor.ProcessorsByName = {} -- local Processor = TMW.Classes.IconDataProcessor:New("STATE", "state") -- -- local Processor = TMW.Classes.IconDataProcessor:New("DURATION", "start, duration") -function IconDataProcessor:OnNewInstance(name, attributes) - TMW:ValidateType("2 (name)", "IconDataProcessor:New(name, attributes)", name, "string") - TMW:ValidateType("3 (attributes)", "IconDataProcessor:New(name, attributes)", attributes, "string") +function IconDataProcessor:OnNewInstance(name, attributes, altAttributes) + TMW:ValidateType("2 (name)", "IconDataProcessor:New(name, attributes, altAttributes?)", name, "string") + TMW:ValidateType("3 (attributes)", "IconDataProcessor:New(name, attributes, altAttributes?)", attributes, "string") + TMW:ValidateType("4 (altAttributes)", "IconDataProcessor:New(name, attributes, altAttributes?)", altAttributes, "table;nil") self.hooks = {} @@ -64,19 +65,36 @@ function IconDataProcessor:OnNewInstance(name, attributes) self.name = name self.attributesString = attributes self.attributesStringNoSpaces = attributes:gsub(" ", "") + self.allAttributesStringNoSpaces = { self.attributesStringNoSpaces } for _, attribute in TMW:Vararg(strsplit(",", self.attributesStringNoSpaces)) do if self.UsedTokens[attribute] then error(("Attribute token %q is already in use by %q!"):format(attribute, self.UsedTokens[attribute].name)) else self.UsedTokens[attribute] = self + self:DeclareUpValue(attribute) -- do this to prevent accidental leaked global accessing self.NumAttributes = self.NumAttributes + 1 end end + + for _, altAttribute in pairs(altAttributes or {}) do + altAttribute = altAttribute:gsub(" ", "") + tinsert(self.allAttributesStringNoSpaces, altAttribute) + for i, attribute in TMW:Vararg(strsplit(",", altAttribute)) do + if self.UsedTokens[attribute] == self then + -- Already declared by the default attributes + elseif self.UsedTokens[attribute] then + error(("Attribute token %q is already in use by %q!"):format(attribute, self.UsedTokens[attribute].name)) + else + self.UsedTokens[attribute] = self + self:DeclareUpValue(attribute) -- do this to prevent accidental leaked global accessing + end + self.NumAttributes = max(self.NumAttributes, i) + end + end self.ProcessorsByName[self.name] = self self:DeclareUpValue(name, self) - self:DeclareUpValue(attributes) -- do this to prevent accidental leaked global accessing self.changedEvent = "TMW_ICON_DATA_CHANGED_" .. name diff --git a/Components/Core/IconDataProcessors.lua b/Components/Core/IconDataProcessors.lua index d417d803..1a412b66 100755 --- a/Components/Core/IconDataProcessors.lua +++ b/Components/Core/IconDataProcessors.lua @@ -316,11 +316,12 @@ end -- DURATION: "start, duration" do - local Processor = TMW.Classes.IconDataProcessor:New("DURATION", "start, duration") + local Processor = TMW.Classes.IconDataProcessor:New("DURATION", "start, duration, modRate", {"start, duration"}) Processor:DeclareUpValue("OnGCD", TMW.OnGCD) TMW.Classes.Icon.attributes.start = 0 TMW.Classes.Icon.attributes.duration = 0 + TMW.Classes.Icon.attributes.modRate = 1 TMW.Classes.Icon.__realDuration = 0 Processor:RegisterIconEvent(21, "OnStart", { @@ -358,7 +359,7 @@ do valueName = L["DURATION"], conditionChecker = function(icon, eventSettings) local attributes = icon.attributes - local d = attributes.duration - (TMW.time - attributes.start) + local d = (attributes.duration - (TMW.time - attributes.start)) / attributes.modRate d = d > 0 and d or 0 return TMW.CompareFuncs[eventSettings.Operator](d, eventSettings.Value) @@ -370,15 +371,16 @@ do }) function Processor:CompileFunctionSegment(t) - -- GLOBALS: start, duration + -- GLOBALS: start, duration, modRate t[#t+1] = [[ duration = duration or 0 start = start or 0 + modRate = modRate or 1 if duration == 0.001 then duration = 0 end -- hardcode fix for tricks of the trade. nice hardcoding on your part too, blizzard if EventHandlersSet.OnDuration then - local d = duration - (TMW.time - start) + local d = (duration - (TMW.time - start)) / modRate d = d > 0 and d or 0 if d ~= icon.__lastDur then @@ -387,7 +389,7 @@ do end end - if attributes.start ~= start or attributes.duration ~= duration then + if attributes.start ~= start or attributes.duration ~= duration or attributes.modRate ~= modRate then local realDuration = icon:OnGCD(duration) and 0 or duration -- the duration of the cooldown, ignoring the GCD if icon.__realDuration ~= realDuration then @@ -406,8 +408,9 @@ do attributes.start = start attributes.duration = duration + attributes.modRate = modRate - TMW:Fire(DURATION.changedEvent, icon, start, duration) + TMW:Fire(DURATION.changedEvent, icon, start, duration, modRate) doFireIconUpdated = true end --]] @@ -458,7 +461,7 @@ do if #durations > 0 then local lastCheckedDuration = durations.last or 0 - local currentIconDuration = icon.attributes.duration - (time - icon.attributes.start) + local currentIconDuration = (icon.attributes.duration - (time - icon.attributes.start)) / icon.attributes.modRate if currentIconDuration < 0 then currentIconDuration = 0 end -- If the duration didn't change (i.e. it is 0) then don't even try. @@ -502,8 +505,8 @@ do local durationA = attributesA.duration local durationB = attributesB.duration - durationA = iconA:OnGCD(durationA) and 0 or durationA - (time - attributesA.start) - durationB = iconB:OnGCD(durationB) and 0 or durationB - (time - attributesB.start) + durationA = iconA:OnGCD(durationA) and 0 or ((durationA - (time - attributesA.start)) / attributesA.modRate) + durationB = iconB:OnGCD(durationB) and 0 or ((durationB - (time - attributesB.start)) / attributesA.modRate) if durationA ~= durationB then return durationA*order < durationB*order @@ -539,7 +542,7 @@ do TMW:RegisterCallback("TMW_ICON_SETUP_POST", function(event, icon) if not TMW.Locked then - icon:SetInfo("start, duration", 0, 0) + icon:SetInfo("start, duration, modRate", 0, 0, 1) end end) end @@ -702,19 +705,19 @@ do if icon then local attributes = icon.attributes + local modRate = attributes.modRate local chargeDur = attributes.chargeDur if not ignoreCharges and chargeDur and chargeDur > 0 then - local remaining = chargeDur - (TMW.time - attributes.chargeStart) + local remaining = (chargeDur - (TMW.time - attributes.chargeStart)) / modRate if remaining > 0 then return isNumber[format("%.1f", remaining)] or 0 end end local duration = attributes.duration - - local remaining = duration - (TMW.time - attributes.start) + local remaining = (duration - (TMW.time - attributes.start)) / modRate if remaining <= 0 or (not gcd and icon:OnGCD(duration)) then return 0 end diff --git a/Components/Core/IconType.lua b/Components/Core/IconType.lua index 22f582a1..e8672106 100644 --- a/Components/Core/IconType.lua +++ b/Components/Core/IconType.lua @@ -316,6 +316,7 @@ function IconType:UsesAttributes(attributesString, uses) TMW:ValidateType("3 (uses)", "IconView:Register(attributesString, uses)", uses, "boolean;nil") + attributesString = attributesString:gsub(" ", "") if uses == false then self.UsedAttributes[attributesString] = nil else @@ -328,9 +329,11 @@ function IconType:UpdateUsedProcessors() self:AssertSelfIsInstance() for _, Processor in ipairs(TMW.Classes.IconDataProcessor.instances) do - if self.UsedAttributes[Processor.attributesString] then - self.UsedAttributes[Processor.attributesString] = nil - self.UsedProcessors[Processor] = true + for _, attributeString in pairs(Processor.allAttributesStringNoSpaces) do + if self.UsedAttributes[attributeString] then + self.UsedAttributes[attributeString] = nil + self.UsedProcessors[Processor] = true + end end end end diff --git a/Components/IconDataProcessorHooks/IconDataProcessorHook_Alpha_DurationReq/Alpha_DurationReq.lua b/Components/IconDataProcessorHooks/IconDataProcessorHook_Alpha_DurationReq/Alpha_DurationReq.lua index f334caf3..71802c9b 100644 --- a/Components/IconDataProcessorHooks/IconDataProcessorHook_Alpha_DurationReq/Alpha_DurationReq.lua +++ b/Components/IconDataProcessorHooks/IconDataProcessorHook_Alpha_DurationReq/Alpha_DurationReq.lua @@ -76,12 +76,12 @@ end) Hook:DeclareUpValue("STATE_DEFAULT_DURATIONFAILED", STATE) Hook:RegisterCompileFunctionSegmentHook("post", function(Processor, t) - -- GLOBALS: start, duration + -- GLOBALS: start, duration, modRate t[#t+1] = [[ if duration > 0 or doFireIconUpdated then - local d = duration - (TMW.time - start) + local d = (duration - (TMW.time - start)) / (modRate or 1) local state_durationFailed = nil if diff --git a/Components/IconModules/IconModule_CooldownSweep/CooldownSweep.lua b/Components/IconModules/IconModule_CooldownSweep/CooldownSweep.lua index 3744e1e2..042825b9 100644 --- a/Components/IconModules/IconModule_CooldownSweep/CooldownSweep.lua +++ b/Components/IconModules/IconModule_CooldownSweep/CooldownSweep.lua @@ -223,6 +223,7 @@ local NeedsUpdate = {} function CooldownSweep:OnDisable() self.start = 0 self.duration = 0 + self.modRate = 1 self.charges = 0 self.maxCharges = 0 self.chargeStart = 0 @@ -293,7 +294,7 @@ function CooldownSweep:SetupForIcon(icon) local attributes = icon.attributes - self:DURATION(icon, attributes.start, attributes.duration) + self:DURATION(icon, attributes.start, attributes.duration, attributes.modRate) self:SPELLCHARGES(icon, attributes.charges, attributes.maxCharges, attributes.chargeStart, attributes.chargeDur) self:REVERSE(icon, attributes.reverse) end @@ -326,7 +327,7 @@ function CooldownSweep:UpdateCooldown() cd:SetDrawSwipe(false) end - cd:SetCooldown(mainStart, mainDuration) + cd:SetCooldown(mainStart, mainDuration, self.modRate) cd:Show() else cd:SetCooldown(0, 0) @@ -335,21 +336,22 @@ function CooldownSweep:UpdateCooldown() -- Handle charges of spells that aren't completely depleted. local cd2 = self.cooldown2 if otherDuration > 0 then - cd2:SetCooldown(otherStart, otherDuration) + cd2:SetCooldown(otherStart, otherDuration, self.modRate) cd2:Show() else cd2:SetCooldown(0, 0) end end -function CooldownSweep:DURATION(icon, start, duration) +function CooldownSweep:DURATION(icon, start, duration, modRate) if (not self.ClockGCD and OnGCD(duration)) or (duration - (TMW.time - start)) <= 0 or duration <= 0 then start, duration = 0, 0 end - if self.start ~= start or self.duration ~= duration then + if self.start ~= start or self.duration ~= duration or self.modRate ~= modRate then self.start = start self.duration = duration + self.modRate = modRate NeedsUpdate[self] = true end diff --git a/Components/IconTypes/IconType_buff/buff.lua b/Components/IconTypes/IconType_buff/buff.lua index 2c98496e..476e9b59 100644 --- a/Components/IconTypes/IconType_buff/buff.lua +++ b/Components/IconTypes/IconType_buff/buff.lua @@ -359,7 +359,7 @@ local function Buff_OnUpdate(icon, time) and (NotStealable or (instance.isStealable and not NOT_ACTUALLY_SPELLSTEALABLE[instance.spellId])) then if DurationSort then - local remaining = (instance.expirationTime == 0 and huge) or instance.expirationTime - time + local remaining = (instance.expirationTime == 0 and huge) or ((instance.expirationTime - time) / instance.timeMod) if not foundInstance or curSortDur*DurationSort < remaining*DurationSort then -- DurationSort is either 1 or -1, so multiply by it to get the correct ordering. (multiplying by a negative flips inequalities) @@ -439,7 +439,7 @@ local function Buff_OnUpdate_Packed(icon, time) and (NotStealable or (instance.isStealable and not NOT_ACTUALLY_SPELLSTEALABLE[instance.spellId])) then if DurationSort then - local remaining = (instance.expirationTime == 0 and huge) or instance.expirationTime - time + local remaining = (instance.expirationTime == 0 and huge) or ((instance.expirationTime - time) / instance.timeMod) -- If we haven't found anything yet, or if this aura beats the previous by sort order, then use it. if not foundInstance or curSortDur*DurationSort < remaining*DurationSort then @@ -642,10 +642,10 @@ function Type:HandleYieldedInfo(icon, iconToSet, unit, instance) end end - iconToSet:SetInfo("state; texture; start, duration; stack, stackText; spell; unit, GUID; auraSourceUnit, auraSourceGUID", + iconToSet:SetInfo("state; texture; start, duration, modRate; stack, stackText; spell; unit, GUID; auraSourceUnit, auraSourceGUID", STATE_PRESENT, instance.icon, - instance.expirationTime - instance.duration, instance.duration, + instance.expirationTime - instance.duration, instance.duration, instance.timeMod, count, count, instance.spellId, unit, nil, diff --git a/Components/IconTypes/IconType_buffcheck/buffcheck.lua b/Components/IconTypes/IconType_buffcheck/buffcheck.lua index c1d7de2b..6bfff67c 100644 --- a/Components/IconTypes/IconType_buffcheck/buffcheck.lua +++ b/Components/IconTypes/IconType_buffcheck/buffcheck.lua @@ -158,7 +158,7 @@ local function BuffCheck_OnUpdate(icon, time) break elseif Hash[instance.spellId] or Hash[strlowerCache[instance.name]] then foundOnUnit = true - local remaining = (instance.expirationTime == 0 and huge) or instance.expirationTime - time + local remaining = (instance.expirationTime == 0 and huge) or ((instance.expirationTime - time) / instance.timeMod) -- This icon type automatically sorts by lowest duration. if not foundInstance or remaining < curSortDur then @@ -227,7 +227,7 @@ local function BuffCheck_OnUpdate_Packed(icon, time) and (NotOnlyMine or isMine) then foundOnUnit = true - local remaining = (instance.expirationTime == 0 and huge) or instance.expirationTime - time + local remaining = (instance.expirationTime == 0 and huge) or ((instance.expirationTime - time) / instance.timeMod) -- If we haven't found anything yet, or if this aura beats the previous by sort order, then use it. if not foundInstance or remaining < curSortDur then @@ -295,10 +295,10 @@ function Type:HandleYieldedInfo(icon, iconToSet, unit, instance) elseif instance then -- ID is defined if we didn't find any units that are missing all the auras being checked for. -- In this case, the data is for the first matching aura found on the first unit checked. - iconToSet:SetInfo("state; texture; start, duration; stack, stackText; spell; unit, GUID; auraSourceUnit, auraSourceGUID", + iconToSet:SetInfo("state; texture; start, duration, modRate; stack, stackText; spell; unit, GUID; auraSourceUnit, auraSourceGUID", STATE_PRESENT, instance.icon, - instance.expirationTime - instance.duration, instance.duration, + instance.expirationTime - instance.duration, instance.duration, instance.timeMod, instance.applications, instance.applications, instance.spellId, unit, nil, diff --git a/Components/IconTypes/IconType_cooldown/cooldown.lua b/Components/IconTypes/IconType_cooldown/cooldown.lua index d810292d..5d517745 100644 --- a/Components/IconTypes/IconType_cooldown/cooldown.lua +++ b/Components/IconTypes/IconType_cooldown/cooldown.lua @@ -292,12 +292,14 @@ local function SpellCooldown_OnUpdate(icon, time) end if dataToUse then + local cooldown = dataToUse.cooldown + local charges = dataToUse.charges icon:SetInfo( - "state; texture; start, duration; charges, maxCharges, chargeStart, chargeDur; stack, stackText; spell", + "state; texture; start, duration, modRate; charges, maxCharges, chargeStart, chargeDur; stack, stackText; spell", dataToUse.state, spellTextureCache[dataToUse.iName], - dataToUse.cooldown.startTime, dataToUse.cooldown.duration, - dataToUse.charges.currentCharges, dataToUse.charges.maxCharges, dataToUse.charges.cooldownStartTime, dataToUse.charges.cooldownDuration, + cooldown.startTime, cooldown.duration, cooldown.modRate, + charges.currentCharges, charges.maxCharges, charges.cooldownStartTime, charges.cooldownDuration, dataToUse.stack, dataToUse.stack, dataToUse.iName ) diff --git a/Components/IconTypes/IconType_meta/meta.lua b/Components/IconTypes/IconType_meta/meta.lua index c9fd87a4..142a16d3 100644 --- a/Components/IconTypes/IconType_meta/meta.lua +++ b/Components/IconTypes/IconType_meta/meta.lua @@ -238,7 +238,7 @@ local function Meta_OnUpdate(icon, time) -- This icon is OK to be shown. if Sort then -- See if we can use this icon due to sorting. - local dur = attributes.duration - (time - attributes.start) + local dur = (attributes.duration - (time - attributes.start)) / attributes.timeMod if dur < 0 then dur = 0 end diff --git a/Components/IconTypes/IconType_reactive/reactive.lua b/Components/IconTypes/IconType_reactive/reactive.lua index 7e97c423..235808d5 100644 --- a/Components/IconTypes/IconType_reactive/reactive.lua +++ b/Components/IconTypes/IconType_reactive/reactive.lua @@ -231,10 +231,10 @@ local function Reactive_OnUpdate(icon, time) usable = activationOverlayActive or usable end if usable and not CD and not noMana and inrange then --usable - icon:SetInfo("state; texture; start, duration; charges, maxCharges, chargeStart, chargeDur; stack, stackText; spell", + icon:SetInfo("state; texture; start, duration, modRate; charges, maxCharges, chargeStart, chargeDur; stack, stackText; spell", STATE_USABLE, spellTextureCache[iName], - cooldown.startTime, cooldown.duration, + cooldown.startTime, cooldown.duration, cooldown.modRate, charges.currentCharges, charges.maxCharges, charges.cooldownStartTime, charges.cooldownDuration, stack, stack, iName diff --git a/Options/CHANGELOG.lua b/Options/CHANGELOG.lua index 241d58d1..e9e95bde 100644 --- a/Options/CHANGELOG.lua +++ b/Options/CHANGELOG.lua @@ -5,6 +5,7 @@ TMW.CHANGELOG_LASTVER="10.0.0" TMW.CHANGELOG = [==[ ## v11.0.7 * #1857 - Add LibCustomGlow animations. +* #2144 - TMW now accounts for adjusted-rate cooldowns and auras (e.g. timer freezes caused by talents or boss mechanics). * Fix: #2191 - Icon Shown condition ignoring Shown/Hidden checkboxes for disabled icons/groups. * Fix: #2193 - Icon overlay and border animations starting in the wrong state. * Fix: #2215 - Spell Charges condition not updating for countable spells without true charges. diff --git a/TellMeWhen.lua b/TellMeWhen.lua index 21cd8d95..73ba64f0 100644 --- a/TellMeWhen.lua +++ b/TellMeWhen.lua @@ -437,14 +437,8 @@ local avengingWrathName = GetSpellName(31884) function TMW.GetSpellTexture(spell) if not spell then return end - local spellTex = GetSpellTexture(spell) - if spellTex and (spellTex ~= 135875 or GetSpellName(spell) == avengingWrathName) then - -- Workaround https://github.com/ascott18/TellMeWhen/issues/2114 - - -- don't return avenging wrath texture if the input wasn't the avenging wrath spell. - return spellTex - end - return + GetSpellTexture(spell) or SpellTexturesMetaIndex[spell] or rawget(SpellTexturesMetaIndex, strlowerCache[spell]) end