From 8e07a6495c8d013f9902f0dddaec7508e95f48a4 Mon Sep 17 00:00:00 2001 From: NoM0Re Date: Mon, 27 Jan 2025 03:28:33 +0100 Subject: [PATCH] from retail --- WeakAuras/BaseRegions/Texture.lua | 1 - WeakAuras/Bindings.xml | 6 +- WeakAuras/BossMods.lua | 1268 +++++++++-------- WeakAuras/BuffTrigger2.lua | 41 +- WeakAuras/CHANGELOG.md | 16 - WeakAuras/GenericTrigger.lua | 36 +- WeakAuras/Init.lua | 4 +- .../Media/Creative Commons - CC0 1.0.txt | 121 ++ WeakAuras/Media/Provided by.txt | 3 +- WeakAuras/Media/Sounds/HeartbeatSingle.ogg | Bin 0 -> 36315 bytes WeakAuras/Modernize.lua | 36 + WeakAuras/Prototypes.lua | 96 ++ WeakAuras/RegionTypes/Empty.lua | 50 + WeakAuras/RegionTypes/RegionPrototype.lua | 30 +- WeakAuras/RegionTypes/Texture.lua | 4 + WeakAuras/SubRegionTypes/Border.lua | 7 +- WeakAuras/SubRegionTypes/Glow.lua | 3 +- WeakAuras/SubRegionTypes/Model.lua | 1 + WeakAuras/SubRegionTypes/StopMotion.lua | 9 +- WeakAuras/SubRegionTypes/SubText.lua | 1 + WeakAuras/SubRegionTypes/Texture.lua | 5 +- WeakAuras/Types.lua | 79 +- WeakAuras/WeakAuras.lua | 2 +- WeakAuras/WeakAuras.toc | 3 +- WeakAurasOptions/AuthorOptions.lua | 9 - WeakAurasOptions/Changelog.lua | 89 ++ WeakAurasOptions/CommonOptions.lua | 185 ++- .../OptionsFrames/OptionsFrame.lua | 18 +- WeakAurasOptions/RegionOptions/Empty.lua | 54 + .../SubRegionOptions/StopMotion.lua | 44 +- WeakAurasOptions/SubRegionOptions/Texture.lua | 1 - WeakAurasOptions/WeakAurasOptions.toc | 5 +- 32 files changed, 1450 insertions(+), 777 deletions(-) delete mode 100644 WeakAuras/CHANGELOG.md create mode 100644 WeakAuras/Media/Creative Commons - CC0 1.0.txt create mode 100644 WeakAuras/Media/Sounds/HeartbeatSingle.ogg create mode 100644 WeakAuras/RegionTypes/Empty.lua create mode 100644 WeakAurasOptions/Changelog.lua create mode 100644 WeakAurasOptions/RegionOptions/Empty.lua diff --git a/WeakAuras/BaseRegions/Texture.lua b/WeakAuras/BaseRegions/Texture.lua index e5502bc..8f4010f 100644 --- a/WeakAuras/BaseRegions/Texture.lua +++ b/WeakAuras/BaseRegions/Texture.lua @@ -112,7 +112,6 @@ function Private.TextureBase.create(frame) return base end --- TODO better type for options function Private.TextureBase.modify(base, options) base.canRotate = options.canRotate base.mirror = options.mirror diff --git a/WeakAuras/Bindings.xml b/WeakAuras/Bindings.xml index 66ea3e6..eb4d252 100644 --- a/WeakAuras/Bindings.xml +++ b/WeakAuras/Bindings.xml @@ -1,11 +1,11 @@ - + WeakAuras.OpenOptions() - + WeakAuras.RealTimeProfilingWindow:Toggle() - + WeakAuras.PrintProfile() diff --git a/WeakAuras/BossMods.lua b/WeakAuras/BossMods.lua index 32fe029..00ab746 100644 --- a/WeakAuras/BossMods.lua +++ b/WeakAuras/BossMods.lua @@ -1,5 +1,4 @@ if not WeakAuras.IsLibsOK() then return end ---- @type string, Private local AddonName, Private = ... local timer = WeakAuras.timer; @@ -7,6 +6,11 @@ local L = WeakAuras.L Private.ExecEnv.BossMods = {} +-- TODO! remove this +WeakAuras.IsTWW = function() + return false +end + -- DBM Private.ExecEnv.BossMods.DBM = { registeredEvents = {}, @@ -137,9 +141,9 @@ Private.ExecEnv.BossMods.DBM = { end if not bar.expired then bar.expired = true - WeakAuras.ScanEvents("DBM_TimerStop", timerId) + Private.ScanEvents("DBM_TimerStop", timerId) if self.isGeneric then - WeakAuras.ScanEvents("BossMod_TimerStop", timerId) + Private.ScanEvents("BossMod_TimerStop", timerId) end end elseif self.nextExpire == nil then @@ -157,10 +161,10 @@ Private.ExecEnv.BossMods.DBM = { EventCallback = function(self, event, ...) if event == "DBM_Announce" then local message, icon, _, spellId, _, count = ... - WeakAuras.ScanEvents("DBM_Announce", spellId, message, icon) + Private.ScanEvents("DBM_Announce", spellId, message, icon) if self.isGeneric then count = count and tostring(count) or "0" - WeakAuras.ScanEvents("BossMod_Announce", spellId, message, icon, count) + Private.ScanEvents("BossMod_Announce", spellId, message, icon, count) end elseif event == "DBM_TimerStart" then local timerId, msg, duration, icon, timerType, spellId, dbmType, _, _, _, _, _, timerCount = ... @@ -209,9 +213,9 @@ Private.ExecEnv.BossMods.DBM = { end bar.dbmColor = {r, g, b} - WeakAuras.ScanEvents("DBM_TimerStart", timerId) + Private.ScanEvents("DBM_TimerStart", timerId) if self.isGeneric then - WeakAuras.ScanEvents("BossMod_TimerStart", timerId) + Private.ScanEvents("BossMod_TimerStart", timerId) end if self.nextExpire == nil then self.recheckTimer = timer:ScheduleTimerFixed(self.RecheckTimers, expirationTime - now, self) @@ -229,9 +233,9 @@ Private.ExecEnv.BossMods.DBM = { self.bars[timerId] = nil end bar.expired = true - WeakAuras.ScanEvents("DBM_TimerStop", timerId) + Private.ScanEvents("DBM_TimerStop", timerId) if self.isGeneric then - WeakAuras.ScanEvents("BossMod_TimerStop", timerId) + Private.ScanEvents("BossMod_TimerStop", timerId) end end elseif event == "DBM_TimerPause" then @@ -240,9 +244,9 @@ Private.ExecEnv.BossMods.DBM = { if bar then bar.paused = true bar.remaining = bar.expirationTime - GetTime() - WeakAuras.ScanEvents("DBM_TimerPause", timerId) + Private.ScanEvents("DBM_TimerPause", timerId) if self.isGeneric then - WeakAuras.ScanEvents("BossMod_TimerPause", timerId) + Private.ScanEvents("BossMod_TimerPause", timerId) end if self.recheckTimer then timer:CancelTimer(self.recheckTimer) @@ -256,9 +260,9 @@ Private.ExecEnv.BossMods.DBM = { bar.paused = nil bar.expirationTime = GetTime() + (bar.remaining or 0) bar.remaining = nil - WeakAuras.ScanEvents("DBM_TimerResume", timerId) + Private.ScanEvents("DBM_TimerResume", timerId) if self.isGeneric then - WeakAuras.ScanEvents("BossMod_TimerResume", timerId) + Private.ScanEvents("BossMod_TimerResume", timerId) end if self.nextExpire == nil then self.recheckTimer = timer:ScheduleTimerFixed(self.RecheckTimers, bar.expirationTime - GetTime(), self) @@ -286,9 +290,9 @@ Private.ExecEnv.BossMods.DBM = { self.nextExpire = expirationTime end end - WeakAuras.ScanEvents("DBM_TimerUpdate", timerId) + Private.ScanEvents("DBM_TimerUpdate", timerId) if self.isGeneric then - WeakAuras.ScanEvents("BossMod_TimerUpdate", timerId) + Private.ScanEvents("BossMod_TimerUpdate", timerId) end elseif event == "DBM_TimerUpdateIcon" then local timerId, icon = ... @@ -296,14 +300,14 @@ Private.ExecEnv.BossMods.DBM = { if bar then bar.icon = icon end - WeakAuras.ScanEvents("DBM_TimerUpdateIcon", timerId) + Private.ScanEvents("DBM_TimerUpdateIcon", timerId) if self.isGeneric then - WeakAuras.ScanEvents("BossMod_TimerUpdateIcon", timerId) + Private.ScanEvents("BossMod_TimerUpdateIcon", timerId) end elseif event == "DBM_SetStage" or event == "DBM_Pull" or event == "DBM_Wipe" or event == "DBM_Kill" then - WeakAuras.ScanEvents("DBM_SetStage") + Private.ScanEvents("DBM_SetStage") if self.isGeneric then - WeakAuras.ScanEvents("BossMod_SetStage") + Private.ScanEvents("BossMod_SetStage") end end end, @@ -341,9 +345,9 @@ Private.ExecEnv.BossMods.DBM = { DoScan = function(self, fireTime) self.scheduled_scans[fireTime] = nil - WeakAuras.ScanEvents("DBM_TimerUpdate") + Private.ScanEvents("DBM_TimerUpdate") if self.isGeneric then - WeakAuras.ScanEvents("BossMod_TimerUpdate") + Private.ScanEvents("BossMod_TimerUpdate") end end, @@ -354,329 +358,331 @@ Private.ExecEnv.BossMods.DBM = { end } -Private.event_prototypes["DBM Stage"] = { - type = "addons", - events = {}, - internal_events = { - "DBM_SetStage" - }, - force_events = "DBM_SetStage", - name = L["DBM Stage"], - init = function(trigger) - Private.ExecEnv.BossMods.DBM:RegisterStage() - return "" - end, - args = { - { - name = "stage", - init = "Private.ExecEnv.BossMods.DBM:GetStage()", - display = L["Journal Stage"], - desc = L["Matches stage number of encounter journal.\nIntermissions are .5\nE.g. 1;2;1;2;2.5;3"], - type = "number", - conditionType = "number", - store = true, +if not WeakAuras.IsTWW() then + Private.event_prototypes["DBM Stage"] = { + type = "addons", + events = {}, + internal_events = { + "DBM_SetStage" }, - { - name = "stageTotal", - init = "select(2, Private.ExecEnv.BossMods.DBM:GetStage())", - display = L["Stage Counter"], - desc = L["Increases by one per stage or intermission."], - type = "number", - conditionType = "number", - store = true, + force_events = "DBM_SetStage", + name = L["DBM Stage"], + init = function(trigger) + Private.ExecEnv.BossMods.DBM:RegisterStage() + return "" + end, + args = { + { + name = "stage", + init = "Private.ExecEnv.BossMods.DBM:GetStage()", + display = L["Journal Stage"], + desc = L["Matches stage number of encounter journal.\nIntermissions are .5\nE.g. 1;2;1;2;2.5;3"], + type = "number", + conditionType = "number", + store = true, + }, + { + name = "stageTotal", + init = "select(2, Private.ExecEnv.BossMods.DBM:GetStage())", + display = L["Stage Counter"], + desc = L["Increases by one per stage or intermission."], + type = "number", + conditionType = "number", + store = true, + }, }, - }, - automaticrequired = true, - statesParameter = "one", - progressType = "none" -} -Private.category_event_prototype.addons["DBM Stage"] = L["DBM Stage"] + automaticrequired = true, + statesParameter = "one", + progressType = "none" + } + Private.category_event_prototype.addons["DBM Stage"] = L["DBM Stage"] -Private.event_prototypes["DBM Announce"] = { - type = "addons", - events = {}, - internal_events = { - "DBM_Announce" - }, - name = L["DBM Announce"], - init = function(trigger) - Private.ExecEnv.BossMods.DBM:RegisterMessage(); - local ret = "local use_cloneId = %s;" - return ret:format(trigger.use_cloneId and "true" or "false"); - end, - statesParameter = "all", - args = { - { - name = "spellId", - init = "arg", - display = L["Spell Id"], - type = "spell", - noValidation = true, - showExactOption = false, - negativeIsEJ = true + Private.event_prototypes["DBM Announce"] = { + type = "addons", + events = {}, + internal_events = { + "DBM_Announce" }, - { - name = "message", - init = "arg", - display = L["Message"], - type = "longstring", - store = true, - conditionType = "string" + name = L["DBM Announce"], + init = function(trigger) + Private.ExecEnv.BossMods.DBM:RegisterMessage(); + local ret = "local use_cloneId = %s;" + return ret:format(trigger.use_cloneId and "true" or "false"); + end, + statesParameter = "all", + args = { + { + name = "spellId", + init = "arg", + display = L["Spell Id"], + type = "spell", + noValidation = true, + showExactOption = false, + negativeIsEJ = true + }, + { + name = "message", + init = "arg", + display = L["Message"], + type = "longstring", + store = true, + conditionType = "string" + }, + { + name = "name", + init = "message", + hidden = true, + test = "true", + store = true, + }, + { + name = "icon", + init = "arg", + store = true, + hidden = true, + test = "true" + }, + { + name = "cloneId", + display = L["Clone per Event"], + type = "toggle", + test = "true", + init = "use_cloneId and WeakAuras.GetUniqueCloneId() or ''" + }, }, - { - name = "name", - init = "message", - hidden = true, - test = "true", - store = true, - }, - { - name = "icon", - init = "arg", - store = true, - hidden = true, - test = "true" - }, - { - name = "cloneId", - display = L["Clone per Event"], - type = "toggle", - test = "true", - init = "use_cloneId and WeakAuras.GetUniqueCloneId() or ''" - }, - }, - timedrequired = true, - progressType = "timed" -} -Private.category_event_prototype.addons["DBM Announce"] = L["DBM Announce"] + timedrequired = true, + progressType = "timed" + } + Private.category_event_prototype.addons["DBM Announce"] = L["DBM Announce"] -Private.event_prototypes["DBM Timer"] = { - type = "addons", - events = {}, - internal_events = { - "DBM_TimerStart", "DBM_TimerStop", "DBM_TimerUpdate", "DBM_TimerForce", "DBM_TimerResume", "DBM_TimerPause", - "DBM_TimerUpdateIcon" - }, - force_events = "DBM_TimerForce", - name = L["DBM Timer"], - progressType = "timed", - triggerFunction = function(trigger) - Private.ExecEnv.BossMods.DBM:RegisterTimer() - local ret = [=[ - local triggerCounter = %q - local counter - if triggerCounter and triggerCounter ~= "" then - counter = Private.ExecEnv.CreateTriggerCounter(triggerCounter) - else - counter = Private.ExecEnv.CreateTriggerCounter() - end - return function (states, event, timerId) - local triggerId = %q - local triggerSpellId = %q - local triggerText = %q - local triggerTextOperator = %q - local useClone = %s - local extendTimer = %s - local triggerUseRemaining = %s - local triggerRemaining = %s - local triggerDbmType = %s - local cloneId = useClone and timerId or "" - local state = states[cloneId] - local counter = counter + Private.event_prototypes["DBM Timer"] = { + type = "addons", + events = {}, + internal_events = { + "DBM_TimerStart", "DBM_TimerStop", "DBM_TimerUpdate", "DBM_TimerForce", "DBM_TimerResume", "DBM_TimerPause", + "DBM_TimerUpdateIcon" + }, + force_events = "DBM_TimerForce", + name = L["DBM Timer"], + progressType = "timed", + triggerFunction = function(trigger) + Private.ExecEnv.BossMods.DBM:RegisterTimer() + local ret = [=[ + local triggerCounter = %q + local counter + if triggerCounter and triggerCounter ~= "" then + counter = Private.ExecEnv.CreateTriggerCounter(triggerCounter) + else + counter = Private.ExecEnv.CreateTriggerCounter() + end + return function (states, event, timerId) + local triggerId = %q + local triggerSpellId = %q + local triggerText = %q + local triggerTextOperator = %q + local useClone = %s + local extendTimer = %s + local triggerUseRemaining = %s + local triggerRemaining = %s + local triggerDbmType = %s + local cloneId = useClone and timerId or "" + local state = states[cloneId] + local counter = counter - function copyOrSchedule(bar, cloneId) - local remainingTime - local changed - if bar.paused then - remainingTime = bar.remaining + extendTimer - else - remainingTime = bar.expirationTime - GetTime() + extendTimer - end - if triggerUseRemaining then - if remainingTime > 0 and remainingTime %s triggerRemaining then - Private.ExecEnv.BossMods.DBM:CopyBarToState(bar, states, cloneId, extendTimer) - changed = true + function copyOrSchedule(bar, cloneId) + local remainingTime + local changed + if bar.paused then + remainingTime = bar.remaining + extendTimer else - local state = states[cloneId] - if state and state.show then - state.show = false - state.changed = true + remainingTime = bar.expirationTime - GetTime() + extendTimer + end + if triggerUseRemaining then + if remainingTime > 0 and remainingTime %s triggerRemaining then + Private.ExecEnv.BossMods.DBM:CopyBarToState(bar, states, cloneId, extendTimer) + changed = true + else + local state = states[cloneId] + if state and state.show then + state.show = false + state.changed = true + changed = true + end + end + if not bar.paused then + if extendTimer > 0 then + bar.scheduledScanExpireAt = math.max(bar.scheduledScanExpireAt or 0, bar.expirationTime + extendTimer) + end + if remainingTime >= triggerRemaining then + Private.ExecEnv.BossMods.DBM:ScheduleCheck(bar.expirationTime - triggerRemaining + extendTimer) + end + end + else + if not bar.paused and extendTimer > 0 then + bar.scheduledScanExpireAt = math.max(bar.scheduledScanExpireAt or 0, bar.expirationTime + extendTimer) + end + if remainingTime > 0 then + Private.ExecEnv.BossMods.DBM:CopyBarToState(bar, states, cloneId, extendTimer) changed = true end end - if not bar.paused then - if extendTimer > 0 then - bar.scheduledScanExpireAt = math.max(bar.scheduledScanExpireAt or 0, bar.expirationTime + extendTimer) - end - if remainingTime >= triggerRemaining then - Private.ExecEnv.BossMods.DBM:ScheduleCheck(bar.expirationTime - triggerRemaining + extendTimer) - end - end - else - if not bar.paused and extendTimer > 0 then - bar.scheduledScanExpireAt = math.max(bar.scheduledScanExpireAt or 0, bar.expirationTime + extendTimer) - end - if remainingTime > 0 then - Private.ExecEnv.BossMods.DBM:CopyBarToState(bar, states, cloneId, extendTimer) - changed = true - end + return changed end - return changed - end - if useClone then - if event == "DBM_TimerStart" - or event == "DBM_TimerPause" - or event == "DBM_TimerResume" - then - if Private.ExecEnv.BossMods.DBM:TimerMatches(timerId, triggerText, triggerTextOperator, triggerSpellId, counter, triggerId, triggerDbmType) then - local bar = Private.ExecEnv.BossMods.DBM:GetTimerById(timerId) - if bar then - return copyOrSchedule(bar, cloneId) - end - end - elseif event == "DBM_TimerStop" and state then - local bar_remainingTime = state.expirationTime - GetTime() + (state.extend or 0) - if state.extend == 0 or bar_remainingTime <= 0 then - state.show = false - state.changed = true - return true - end - elseif event == "DBM_TimerUpdate" or event == "DBM_TimerUpdateIcon" then - local changed - for timerId, bar in pairs(Private.ExecEnv.BossMods.DBM:GetAllTimers()) do - if Private.ExecEnv.BossMods.DBM:TimerMatches(timerId, triggerText, triggerTextOperator, triggerSpellId, counter, triggerId, triggerDbmType) then - changed = copyOrSchedule(bar, timerId) or changed - else - local state = states[timerId] - if state then - local bar_remainingTime = state.expirationTime - GetTime() + (state.extend or 0) - if state.extend == 0 or bar_remainingTime <= 0 then - state.show = false - state.changed = true - changed = true - end - end - end - end - return changed - elseif event == "DBM_TimerForce" then - local changed - for _, state in pairs(states) do - state.show = false - state.changed = true - changed = true - end - for timerId, bar in pairs(Private.ExecEnv.BossMods.DBM:GetAllTimers()) do - if Private.ExecEnv.BossMods.DBM:TimerMatches(timerId, triggerText, triggerTextOperator, triggerSpellId, counter, triggerId, triggerDbmType) then - changed = copyOrSchedule(bar, timerId) or changed - end - end - return changed - end - else - if event == "DBM_TimerStart" or event == "DBM_TimerUpdate" then - if extendTimer ~= 0 then + if useClone then + if event == "DBM_TimerStart" + or event == "DBM_TimerPause" + or event == "DBM_TimerResume" + then if Private.ExecEnv.BossMods.DBM:TimerMatches(timerId, triggerText, triggerTextOperator, triggerSpellId, counter, triggerId, triggerDbmType) then local bar = Private.ExecEnv.BossMods.DBM:GetTimerById(timerId) - Private.ExecEnv.BossMods.DBM:ScheduleCheck(bar.expirationTime + extendTimer) + if bar then + return copyOrSchedule(bar, cloneId) + end end - end - end - local bar = Private.ExecEnv.BossMods.DBM:GetTimer(triggerText, triggerTextOperator, triggerSpellId, extendTimer, counter, triggerId, triggerDbmType) - if bar then - if extendTimer == 0 - or not (state and state.show) - or (state and state.show and state.expirationTime > (bar.expirationTime + extendTimer)) - then - return copyOrSchedule(bar, cloneId) - end - else - if state and state.show then + elseif event == "DBM_TimerStop" and state then local bar_remainingTime = state.expirationTime - GetTime() + (state.extend or 0) if state.extend == 0 or bar_remainingTime <= 0 then state.show = false state.changed = true return true end + elseif event == "DBM_TimerUpdate" or event == "DBM_TimerUpdateIcon" then + local changed + for timerId, bar in pairs(Private.ExecEnv.BossMods.DBM:GetAllTimers()) do + if Private.ExecEnv.BossMods.DBM:TimerMatches(timerId, triggerText, triggerTextOperator, triggerSpellId, counter, triggerId, triggerDbmType) then + changed = copyOrSchedule(bar, timerId) or changed + else + local state = states[timerId] + if state then + local bar_remainingTime = state.expirationTime - GetTime() + (state.extend or 0) + if state.extend == 0 or bar_remainingTime <= 0 then + state.show = false + state.changed = true + changed = true + end + end + end + end + return changed + elseif event == "DBM_TimerForce" then + local changed + for _, state in pairs(states) do + state.show = false + state.changed = true + changed = true + end + for timerId, bar in pairs(Private.ExecEnv.BossMods.DBM:GetAllTimers()) do + if Private.ExecEnv.BossMods.DBM:TimerMatches(timerId, triggerText, triggerTextOperator, triggerSpellId, counter, triggerId, triggerDbmType) then + changed = copyOrSchedule(bar, timerId) or changed + end + end + return changed + end + else + if event == "DBM_TimerStart" or event == "DBM_TimerUpdate" then + if extendTimer ~= 0 then + if Private.ExecEnv.BossMods.DBM:TimerMatches(timerId, triggerText, triggerTextOperator, triggerSpellId, counter, triggerId, triggerDbmType) then + local bar = Private.ExecEnv.BossMods.DBM:GetTimerById(timerId) + Private.ExecEnv.BossMods.DBM:ScheduleCheck(bar.expirationTime + extendTimer) + end + end + end + local bar = Private.ExecEnv.BossMods.DBM:GetTimer(triggerText, triggerTextOperator, triggerSpellId, extendTimer, counter, triggerId, triggerDbmType) + if bar then + if extendTimer == 0 + or not (state and state.show) + or (state and state.show and state.expirationTime > (bar.expirationTime + extendTimer)) + then + return copyOrSchedule(bar, cloneId) + end + else + if state and state.show then + local bar_remainingTime = state.expirationTime - GetTime() + (state.extend or 0) + if state.extend == 0 or bar_remainingTime <= 0 then + state.show = false + state.changed = true + return true + end + end end end end - end - ]=] + ]=] - return ret:format( - trigger.use_count and trigger.count or "", - trigger.use_id and trigger.id or "", - trigger.use_spellId and tostring(trigger.spellId) or "", - trigger.use_message and trigger.message or "", - trigger.use_message and trigger.message_operator or "", - trigger.use_cloneId and "true" or "false", - trigger.use_extend and tonumber(trigger.extend or 0) or 0, - trigger.use_remaining and "true" or "false", - trigger.remaining and tonumber(trigger.remaining or 0) or 0, - trigger.use_dbmType and trigger.dbmType or "nil", - trigger.remaining_operator or "<" - ) - end, - statesParameter = "full", - args = { - { - name = "id", - display = L["Timer Id"], - type = "string", + return ret:format( + trigger.use_count and trigger.count or "", + trigger.use_id and trigger.id or "", + trigger.use_spellId and tostring(trigger.spellId) or "", + trigger.use_message and trigger.message or "", + trigger.use_message and trigger.message_operator or "", + trigger.use_cloneId and "true" or "false", + trigger.use_extend and tonumber(trigger.extend or 0) or 0, + trigger.use_remaining and "true" or "false", + trigger.remaining and tonumber(trigger.remaining or 0) or 0, + trigger.use_dbmType and trigger.dbmType or "nil", + trigger.remaining_operator or "<" + ) + end, + statesParameter = "full", + args = { + { + name = "id", + display = L["Timer Id"], + type = "string", + }, + { + name = "spellId", + display = L["Spell Id"], + store = true, + type = "spell", + conditionType = "string", + noValidation = true, + showExactOption = false, + negativeIsEJ = true + }, + { + name = "message", + display = L["Message"], + type = "longstring", + store = true, + conditionType = "string" + }, + { + name = "remaining", + display = L["Remaining Time"], + type = "number", + }, + { + name = "extend", + display = L["Offset Timer"], + type = "string", + }, + { + name = "count", + display = L["Count"], + desc = L["Occurrence of the event, reset when aura is unloaded\nCan be a range of values\nCan have multiple values separated by a comma or a space\n\nExamples:\n2nd 5th and 6th events: 2, 5, 6\n2nd to 6th: 2-6\nevery 2 events: /2\nevery 3 events starting from 2nd: 2/3\nevery 3 events starting from 2nd and ending at 11th: 2-11/3\n\nOnly if DBM shows it on it's bar"], + type = "string", + conditionType = "string", + }, + { + name = "dbmType", + display = L["Type"], + type = "select", + values = "dbm_types", + conditionType = "select", + test = "true" + }, + { + name = "cloneId", + display = L["Clone per Event"], + type = "toggle" + } }, - { - name = "spellId", - display = L["Spell Id"], - store = true, - type = "spell", - conditionType = "string", - noValidation = true, - showExactOption = false, - negativeIsEJ = true - }, - { - name = "message", - display = L["Message"], - type = "longstring", - store = true, - conditionType = "string" - }, - { - name = "remaining", - display = L["Remaining Time"], - type = "number", - }, - { - name = "extend", - display = L["Offset Timer"], - type = "string", - }, - { - name = "count", - display = L["Count"], - desc = L["Occurrence of the event, reset when aura is unloaded\nCan be a range of values\nCan have multiple values separated by a comma or a space\n\nExamples:\n2nd 5th and 6th events: 2, 5, 6\n2nd to 6th: 2-6\nevery 2 events: /2\nevery 3 events starting from 2nd: 2/3\nevery 3 events starting from 2nd and ending at 11th: 2-11/3\n\nOnly if DBM shows it on it's bar"], - type = "string", - conditionType = "string", - }, - { - name = "dbmType", - display = L["Type"], - type = "select", - values = "dbm_types", - conditionType = "select", - test = "true" - }, - { - name = "cloneId", - display = L["Clone per Event"], - type = "toggle" - } - }, - automaticrequired = true, -} -Private.category_event_prototype.addons["DBM Timer"] = L["DBM Timer"] + automaticrequired = true, + } + Private.category_event_prototype.addons["DBM Timer"] = L["DBM Timer"] +end -- BigWigs Private.ExecEnv.BossMods.BigWigs = { @@ -804,9 +810,9 @@ Private.ExecEnv.BossMods.BigWigs = { end if not bar.expired then bar.expired = true - WeakAuras.ScanEvents("BigWigs_StopBar", timerId) + Private.ScanEvents("BigWigs_StopBar", timerId) if self.isGeneric then - WeakAuras.ScanEvents("BossMod_TimerStop", timerId) + Private.ScanEvents("BossMod_TimerStop", timerId) end end elseif self.nextExpire == nil then @@ -824,11 +830,11 @@ Private.ExecEnv.BossMods.BigWigs = { EventCallback = function(self, event, ...) if event == "BigWigs_Message" then - WeakAuras.ScanEvents("BigWigs_Message", ...) + Private.ScanEvents("BigWigs_Message", ...) if self.isGeneric then local _, spellId, text, _, icon = ... local count = text and text:match("%((%d+)%)") or text:match("((%d+))") or "0" - WeakAuras.ScanEvents("BossMod_Announce", spellId, text, icon, count) + Private.ScanEvents("BossMod_Announce", spellId, text, icon, count) end elseif event == "BigWigs_StartBar" then local addon, spellId, text, duration, icon, isCD = ... @@ -853,9 +859,9 @@ Private.ExecEnv.BossMods.BigWigs = { bar.count = text:match("%((%d+)%)") or text:match("((%d+))") or "0" bar.cast = not(text:match("^[^<]") and true) - WeakAuras.ScanEvents("BigWigs_StartBar", text) + Private.ScanEvents("BigWigs_StartBar", text) if self.isGeneric then - WeakAuras.ScanEvents("BossMod_TimerStart", text) + Private.ScanEvents("BossMod_TimerStart", text) end if self.nextExpire == nil then self.recheckTimer = timer:ScheduleTimerFixed(self.RecheckTimers, expirationTime - now, self) @@ -872,9 +878,9 @@ Private.ExecEnv.BossMods.BigWigs = { if bar.scheduledScanExpireAt == nil or bar.scheduledScanExpireAt <= GetTime() then self.bars[text] = nil end - WeakAuras.ScanEvents("BigWigs_StopBar", text) + Private.ScanEvents("BigWigs_StopBar", text) if self.isGeneric then - WeakAuras.ScanEvents("BossMod_TimerStop", text) + Private.ScanEvents("BossMod_TimerStop", text) end end elseif event == "BigWigs_PauseBar" then @@ -883,9 +889,9 @@ Private.ExecEnv.BossMods.BigWigs = { if bar and not bar.paused then bar.paused = true bar.remaining = bar.expirationTime - GetTime() - WeakAuras.ScanEvents("BigWigs_PauseBar", text) + Private.ScanEvents("BigWigs_PauseBar", text) if self.isGeneric then - WeakAuras.ScanEvents("BossMod_TimerPause", text) + Private.ScanEvents("BossMod_TimerPause", text) end if self.recheckTimer then timer:CancelTimer(self.recheckTimer) @@ -899,9 +905,9 @@ Private.ExecEnv.BossMods.BigWigs = { bar.paused = nil bar.expirationTime = GetTime() + (bar.remaining or 0) bar.remaining = nil - WeakAuras.ScanEvents("BigWigs_ResumeBar", text) + Private.ScanEvents("BigWigs_ResumeBar", text) if self.isGeneric then - WeakAuras.ScanEvents("BossMod_TimerResume", text) + Private.ScanEvents("BossMod_TimerResume", text) end if self.nextExpire == nil then self.recheckTimer = timer:ScheduleTimerFixed(self.RecheckTimers, bar.expirationTime - GetTime(), self) @@ -919,24 +925,24 @@ Private.ExecEnv.BossMods.BigWigs = { for timerId, bar in pairs(self.bars) do if bar.addon == addon then self.bars[timerId] = nil - WeakAuras.ScanEvents("BigWigs_StopBar", timerId) + Private.ScanEvents("BigWigs_StopBar", timerId) if self.isGeneric then - WeakAuras.ScanEvents("BossMod_TimerStop", timerId) + Private.ScanEvents("BossMod_TimerStop", timerId) end end end elseif event == "BigWigs_SetStage" then local addon, stage = ... self.currentStage = stage - WeakAuras.ScanEvents("BigWigs_SetStage") + Private.ScanEvents("BigWigs_SetStage") if self.isGeneric then - WeakAuras.ScanEvents("BossMod_SetStage") + Private.ScanEvents("BossMod_SetStage") end elseif event == "BigWigs_OnBossWipe" or event == "BigWigs_OnBossWin" then self.currentStage = 0 - WeakAuras.ScanEvents("BigWigs_SetStage") + Private.ScanEvents("BigWigs_SetStage") if self.isGeneric then - WeakAuras.ScanEvents("BossMod_SetStage") + Private.ScanEvents("BossMod_SetStage") end end end, @@ -986,9 +992,9 @@ Private.ExecEnv.BossMods.BigWigs = { DoScan = function(self, fireTime) self.scheduled_scans[fireTime] = nil - WeakAuras.ScanEvents("BigWigs_Timer_Update") + Private.ScanEvents("BigWigs_Timer_Update") if self.isGeneric then - WeakAuras.ScanEvents("BossMod_TimerUpdate") + Private.ScanEvents("BossMod_TimerUpdate") end end, @@ -999,323 +1005,325 @@ Private.ExecEnv.BossMods.BigWigs = { end } -Private.event_prototypes["BigWigs Stage"] = { - type = "addons", - events = {}, - internal_events = { - "BigWigs_SetStage" - }, - name = L["BigWigs Stage"], - init = function(trigger) - Private.ExecEnv.BossMods.BigWigs:RegisterStage() - return "" - end, - args = { - { - name = "stage", - init = "Private.ExecEnv.BossMods.BigWigs:GetStage()", - display = L["Stage"], - type = "number", - conditionType = "number", - store = true, - } - }, - automaticrequired = true, - statesParameter = "one", - progressType = "none" -} -Private.category_event_prototype.addons["BigWigs Stage"] = L["BigWigs Stage"] +if not WeakAuras.IsTWW() then + Private.event_prototypes["BigWigs Stage"] = { + type = "addons", + events = {}, + internal_events = { + "BigWigs_SetStage" + }, + name = L["BigWigs Stage"], + init = function(trigger) + Private.ExecEnv.BossMods.BigWigs:RegisterStage() + return "" + end, + args = { + { + name = "stage", + init = "Private.ExecEnv.BossMods.BigWigs:GetStage()", + display = L["Stage"], + type = "number", + conditionType = "number", + store = true, + } + }, + automaticrequired = true, + statesParameter = "one", + progressType = "none" + } + Private.category_event_prototype.addons["BigWigs Stage"] = L["BigWigs Stage"] -Private.event_prototypes["BigWigs Message"] = { - type = "addons", - events = {}, - internal_events = { - "BigWigs_Message" - }, - name = L["BigWigs Message"], - init = function(trigger) - Private.ExecEnv.BossMods.BigWigs:RegisterMessage(); - local ret = "local use_cloneId = %s;" - return ret:format(trigger.use_cloneId and "true" or "false"); - end, - statesParameter = "all", - args = { - { - name = "addon", - init = "arg", - display = L["BigWigs Addon"], - type = "string" + Private.event_prototypes["BigWigs Message"] = { + type = "addons", + events = {}, + internal_events = { + "BigWigs_Message" }, - { - name = "spellId", - init = "arg", - display = L["ID"], - desc = L["The 'ID' value can be found in the BigWigs options of a specific spell"], - type = "spell", - conditionType = "string", - noValidation = true, - showExactOption = false, - negativeIsEJ = true + name = L["BigWigs Message"], + init = function(trigger) + Private.ExecEnv.BossMods.BigWigs:RegisterMessage(); + local ret = "local use_cloneId = %s;" + return ret:format(trigger.use_cloneId and "true" or "false"); + end, + statesParameter = "all", + args = { + { + name = "addon", + init = "arg", + display = L["BigWigs Addon"], + type = "string" + }, + { + name = "spellId", + init = "arg", + display = L["ID"], + desc = L["The 'ID' value can be found in the BigWigs options of a specific spell"], + type = "spell", + conditionType = "string", + noValidation = true, + showExactOption = false, + negativeIsEJ = true + }, + { + name = "text", + init = "arg", + display = L["Message"], + type = "longstring", + store = true, + conditionType = "string" + }, + { + name = "name", + init = "text", + hidden = true, + test = "true", + store = true + }, + {}, -- Importance, might be useful + { + name = "icon", + init = "arg", + hidden = true, + test = "true", + store = true + }, + { + name = "cloneId", + display = L["Clone per Event"], + type = "toggle", + test = "true", + init = "use_cloneId and WeakAuras.GetUniqueCloneId() or ''" + }, }, - { - name = "text", - init = "arg", - display = L["Message"], - type = "longstring", - store = true, - conditionType = "string" - }, - { - name = "name", - init = "text", - hidden = true, - test = "true", - store = true - }, - {}, -- Importance, might be useful - { - name = "icon", - init = "arg", - hidden = true, - test = "true", - store = true - }, - { - name = "cloneId", - display = L["Clone per Event"], - type = "toggle", - test = "true", - init = "use_cloneId and WeakAuras.GetUniqueCloneId() or ''" - }, - }, - timedrequired = true, - progressType = "timed" -} -Private.category_event_prototype.addons["BigWigs Message"] = L["BigWigs Message"] + timedrequired = true, + progressType = "timed" + } + Private.category_event_prototype.addons["BigWigs Message"] = L["BigWigs Message"] -Private.event_prototypes["BigWigs Timer"] = { - type = "addons", - events = {}, - internal_events = { - "BigWigs_StartBar", "BigWigs_StopBar", "BigWigs_Timer_Update", "BigWigs_PauseBar", "BigWigs_ResumeBar" - }, - force_events = "BigWigs_Timer_Force", - name = L["BigWigs Timer"], - progressType = "timed", - triggerFunction = function(trigger) - Private.ExecEnv.BossMods.BigWigs:RegisterTimer() - local ret = [=[ - local triggerCounter = %q - local counter - if triggerCounter and triggerCounter ~= "" then - counter = Private.ExecEnv.CreateTriggerCounter(triggerCounter) - else - counter = Private.ExecEnv.CreateTriggerCounter() - end - return function(states, event, timerId) - local triggerSpellId = %q - local triggerText = %q - local triggerTextOperator = %q - local useClone = %s - local extendTimer = %s - local triggerUseRemaining = %s - local triggerRemaining = %s - local triggerCast = %s - local triggerIsCooldown = %s - local cloneId = useClone and timerId or "" - local state = states[cloneId] - local counter = counter + Private.event_prototypes["BigWigs Timer"] = { + type = "addons", + events = {}, + internal_events = { + "BigWigs_StartBar", "BigWigs_StopBar", "BigWigs_Timer_Update", "BigWigs_PauseBar", "BigWigs_ResumeBar" + }, + force_events = "BigWigs_Timer_Force", + name = L["BigWigs Timer"], + progressType = "timed", + triggerFunction = function(trigger) + Private.ExecEnv.BossMods.BigWigs:RegisterTimer() + local ret = [=[ + local triggerCounter = %q + local counter + if triggerCounter and triggerCounter ~= "" then + counter = Private.ExecEnv.CreateTriggerCounter(triggerCounter) + else + counter = Private.ExecEnv.CreateTriggerCounter() + end + return function(states, event, timerId) + local triggerSpellId = %q + local triggerText = %q + local triggerTextOperator = %q + local useClone = %s + local extendTimer = %s + local triggerUseRemaining = %s + local triggerRemaining = %s + local triggerCast = %s + local triggerIsCooldown = %s + local cloneId = useClone and timerId or "" + local state = states[cloneId] + local counter = counter - function copyOrSchedule(bar, cloneId) - local remainingTime - local changed - if bar.paused then - remainingTime = bar.remaining + extendTimer - else - remainingTime = bar.expirationTime - GetTime() + extendTimer - end - if triggerUseRemaining then - if remainingTime > 0 and remainingTime %s triggerRemaining then - Private.ExecEnv.BossMods.BigWigs:CopyBarToState(bar, states, cloneId, extendTimer) - changed = true + function copyOrSchedule(bar, cloneId) + local remainingTime + local changed + if bar.paused then + remainingTime = bar.remaining + extendTimer else - local state = states[cloneId] - if state and state.show then - state.show = false - state.changed = true + remainingTime = bar.expirationTime - GetTime() + extendTimer + end + if triggerUseRemaining then + if remainingTime > 0 and remainingTime %s triggerRemaining then + Private.ExecEnv.BossMods.BigWigs:CopyBarToState(bar, states, cloneId, extendTimer) + changed = true + else + local state = states[cloneId] + if state and state.show then + state.show = false + state.changed = true + changed = true + end + end + if not bar.paused then + if extendTimer > 0 then + bar.scheduledScanExpireAt = math.max(bar.scheduledScanExpireAt or 0, bar.expirationTime + extendTimer) + end + if remainingTime >= triggerRemaining then + Private.ExecEnv.BossMods.BigWigs:ScheduleCheck(bar.expirationTime - triggerRemaining + extendTimer) + end + end + else + if not bar.paused and extendTimer > 0 then + bar.scheduledScanExpireAt = math.max(bar.scheduledScanExpireAt or 0, bar.expirationTime + extendTimer) + end + if remainingTime > 0 then + Private.ExecEnv.BossMods.BigWigs:CopyBarToState(bar, states, cloneId, extendTimer) changed = true end end - if not bar.paused then - if extendTimer > 0 then - bar.scheduledScanExpireAt = math.max(bar.scheduledScanExpireAt or 0, bar.expirationTime + extendTimer) - end - if remainingTime >= triggerRemaining then - Private.ExecEnv.BossMods.BigWigs:ScheduleCheck(bar.expirationTime - triggerRemaining + extendTimer) - end - end - else - if not bar.paused and extendTimer > 0 then - bar.scheduledScanExpireAt = math.max(bar.scheduledScanExpireAt or 0, bar.expirationTime + extendTimer) - end - if remainingTime > 0 then - Private.ExecEnv.BossMods.BigWigs:CopyBarToState(bar, states, cloneId, extendTimer) - changed = true - end + return changed end - return changed - end - if useClone then - if event == "BigWigs_StartBar" - or event == "BigWigs_PauseBar" - or event == "BigWigs_ResumeBar" - then - if Private.ExecEnv.BossMods.BigWigs:TimerMatches(timerId, triggerText, triggerTextOperator, triggerSpellId, counter, triggerCast, triggerIsCooldown) then - local bar = Private.ExecEnv.BossMods.BigWigs:GetTimerById(timerId) - if bar then - return copyOrSchedule(bar, cloneId) - end - end - elseif event == "BigWigs_StopBar" and state then - local bar_remainingTime = state.expirationTime - GetTime() + (state.extend or 0) - if state.extend == 0 or bar_remainingTime <= 0 then - state.show = false - state.changed = true - return true - end - elseif event == "BigWigs_Timer_Update" then - local changed - for timerId, bar in pairs(Private.ExecEnv.BossMods.BigWigs:GetAllTimers()) do - if Private.ExecEnv.BossMods.BigWigs:TimerMatches(timerId, triggerText, triggerTextOperator, triggerSpellId, counter, triggerCast, triggerIsCooldown) then - changed = copyOrSchedule(bar, timerId) or changed - end - end - return changed - elseif event == "BigWigs_Timer_Force" then - local changed - for _, state in pairs(states) do - state.show = false - state.changed = true - changed = true - end - for timerId, bar in pairs(Private.ExecEnv.BossMods.BigWigs:GetAllTimers()) do - if Private.ExecEnv.BossMods.BigWigs:TimerMatches(timerId, triggerText, triggerTextOperator, triggerSpellId, counter, triggerCast, triggerIsCooldown) then - changed = copyOrSchedule(bar, timerId) or changed - end - end - return changed - end - else - if event == "BigWigs_StartBar" then - if extendTimer ~= 0 then + if useClone then + if event == "BigWigs_StartBar" + or event == "BigWigs_PauseBar" + or event == "BigWigs_ResumeBar" + then if Private.ExecEnv.BossMods.BigWigs:TimerMatches(timerId, triggerText, triggerTextOperator, triggerSpellId, counter, triggerCast, triggerIsCooldown) then local bar = Private.ExecEnv.BossMods.BigWigs:GetTimerById(timerId) - Private.ExecEnv.BossMods.BigWigs:ScheduleCheck(bar.expirationTime + extendTimer) + if bar then + return copyOrSchedule(bar, cloneId) + end end - end - end - local bar = Private.ExecEnv.BossMods.BigWigs:GetTimer(triggerText, triggerTextOperator, triggerSpellId, extendTimer, counter, triggerCast, triggerIsCooldown) - if bar then - if extendTimer == 0 - or not (state and state.show) - or (state and state.show and state.expirationTime > (bar.expirationTime + extendTimer)) - then - return copyOrSchedule(bar, cloneId) - end - else - if state and state.show then + elseif event == "BigWigs_StopBar" and state then local bar_remainingTime = state.expirationTime - GetTime() + (state.extend or 0) if state.extend == 0 or bar_remainingTime <= 0 then state.show = false state.changed = true return true end + elseif event == "BigWigs_Timer_Update" then + local changed + for timerId, bar in pairs(Private.ExecEnv.BossMods.BigWigs:GetAllTimers()) do + if Private.ExecEnv.BossMods.BigWigs:TimerMatches(timerId, triggerText, triggerTextOperator, triggerSpellId, counter, triggerCast, triggerIsCooldown) then + changed = copyOrSchedule(bar, timerId) or changed + end + end + return changed + elseif event == "BigWigs_Timer_Force" then + local changed + for _, state in pairs(states) do + state.show = false + state.changed = true + changed = true + end + for timerId, bar in pairs(Private.ExecEnv.BossMods.BigWigs:GetAllTimers()) do + if Private.ExecEnv.BossMods.BigWigs:TimerMatches(timerId, triggerText, triggerTextOperator, triggerSpellId, counter, triggerCast, triggerIsCooldown) then + changed = copyOrSchedule(bar, timerId) or changed + end + end + return changed + end + else + if event == "BigWigs_StartBar" then + if extendTimer ~= 0 then + if Private.ExecEnv.BossMods.BigWigs:TimerMatches(timerId, triggerText, triggerTextOperator, triggerSpellId, counter, triggerCast, triggerIsCooldown) then + local bar = Private.ExecEnv.BossMods.BigWigs:GetTimerById(timerId) + Private.ExecEnv.BossMods.BigWigs:ScheduleCheck(bar.expirationTime + extendTimer) + end + end + end + local bar = Private.ExecEnv.BossMods.BigWigs:GetTimer(triggerText, triggerTextOperator, triggerSpellId, extendTimer, counter, triggerCast, triggerIsCooldown) + if bar then + if extendTimer == 0 + or not (state and state.show) + or (state and state.show and state.expirationTime > (bar.expirationTime + extendTimer)) + then + return copyOrSchedule(bar, cloneId) + end + else + if state and state.show then + local bar_remainingTime = state.expirationTime - GetTime() + (state.extend or 0) + if state.extend == 0 or bar_remainingTime <= 0 then + state.show = false + state.changed = true + return true + end + end end end end - end - ]=] - return ret:format( - trigger.use_count and trigger.count or "", - trigger.use_spellId and tostring(trigger.spellId) or "", - trigger.use_text and trigger.text or "", - trigger.use_text and trigger.text_operator or "", - trigger.use_cloneId and "true" or "false", - trigger.use_extend and tonumber(trigger.extend or 0) or 0, - trigger.use_remaining and "true" or "false", - trigger.remaining and tonumber(trigger.remaining or 0) or 0, - trigger.use_cast == nil and "nil" or trigger.use_cast and "true" or "false", - trigger.use_isCooldown == nil and "nil" or trigger.use_isCooldown and "true" or "false", - trigger.remaining_operator or "<" - ) - end, - statesParameter = "full", - args = { - { - name = "spellId", - display = L["ID"], - desc = L["The 'ID' value can be found in the BigWigs options of a specific spell"], - type = "spell", - conditionType = "string", - noValidation = true, - showExactOption = false, - negativeIsEJ = true + ]=] + return ret:format( + trigger.use_count and trigger.count or "", + trigger.use_spellId and tostring(trigger.spellId) or "", + trigger.use_text and trigger.text or "", + trigger.use_text and trigger.text_operator or "", + trigger.use_cloneId and "true" or "false", + trigger.use_extend and tonumber(trigger.extend or 0) or 0, + trigger.use_remaining and "true" or "false", + trigger.remaining and tonumber(trigger.remaining or 0) or 0, + trigger.use_cast == nil and "nil" or trigger.use_cast and "true" or "false", + trigger.use_isCooldown == nil and "nil" or trigger.use_isCooldown and "true" or "false", + trigger.remaining_operator or "<" + ) + end, + statesParameter = "full", + args = { + { + name = "spellId", + display = L["ID"], + desc = L["The 'ID' value can be found in the BigWigs options of a specific spell"], + type = "spell", + conditionType = "string", + noValidation = true, + showExactOption = false, + negativeIsEJ = true + }, + { + name = "text", + display = L["Message"], + type = "longstring", + store = true, + conditionType = "string" + }, + { + name = "remaining", + display = L["Remaining Time"], + type = "number", + }, + { + name = "extend", + display = L["Offset Timer"], + type = "string", + }, + { + name = "count", + display = L["Count"], + desc = L["Occurrence of the event, reset when aura is unloaded\nCan be a range of values\nCan have multiple values separated by a comma or a space\n\nExamples:\n2nd 5th and 6th events: 2, 5, 6\n2nd to 6th: 2-6\nevery 2 events: /2\nevery 3 events starting from 2nd: 2/3\nevery 3 events starting from 2nd and ending at 11th: 2-11/3\n\nOnly if BigWigs shows it on it's bar"], + type = "string", + store = true, + conditionType = "string", + }, + { + name = "cast", + display = L["Cast Bar"], + desc = L["Filter messages with format "], + type = "tristate", + test = "true", + init = "false", + conditionType = "bool" + }, + { + name = "isCooldown", + display = L["Cooldown"], + desc = L["Cooldown bars show time before an ability is ready to be use, BigWigs prefix them with '~'"], + type = "tristate", + test = "true", + init = "false", + conditionType = "bool" + }, + { + name = "cloneId", + display = L["Clone per Event"], + type = "toggle", + test = "true", + init = "false" + }, }, - { - name = "text", - display = L["Message"], - type = "longstring", - store = true, - conditionType = "string" - }, - { - name = "remaining", - display = L["Remaining Time"], - type = "number", - }, - { - name = "extend", - display = L["Offset Timer"], - type = "string", - }, - { - name = "count", - display = L["Count"], - desc = L["Occurrence of the event, reset when aura is unloaded\nCan be a range of values\nCan have multiple values separated by a comma or a space\n\nExamples:\n2nd 5th and 6th events: 2, 5, 6\n2nd to 6th: 2-6\nevery 2 events: /2\nevery 3 events starting from 2nd: 2/3\nevery 3 events starting from 2nd and ending at 11th: 2-11/3\n\nOnly if BigWigs shows it on it's bar"], - type = "string", - store = true, - conditionType = "string", - }, - { - name = "cast", - display = L["Cast Bar"], - desc = L["Filter messages with format "], - type = "tristate", - test = "true", - init = "false", - conditionType = "bool" - }, - { - name = "isCooldown", - display = L["Cooldown"], - desc = L["Cooldown bars show time before an ability is ready to be use, BigWigs prefix them with '~'"], - type = "tristate", - test = "true", - init = "false", - conditionType = "bool" - }, - { - name = "cloneId", - display = L["Clone per Event"], - type = "toggle", - test = "true", - init = "false" - }, - }, - automaticrequired = true, -} -Private.category_event_prototype.addons["BigWigs Timer"] = L["BigWigs Timer"] + automaticrequired = true, + } + Private.category_event_prototype.addons["BigWigs Timer"] = L["BigWigs Timer"] +end -- Unified if BigWigsLoader or not DBM then diff --git a/WeakAuras/BuffTrigger2.lua b/WeakAuras/BuffTrigger2.lua index cf628e4..d6bb578 100644 --- a/WeakAuras/BuffTrigger2.lua +++ b/WeakAuras/BuffTrigger2.lua @@ -2335,19 +2335,32 @@ local function createScanFunc(trigger) end end -local function highestExpirationTime(bestMatch, auraMatch) - if bestMatch.expirationTime and auraMatch.expirationTime then - return auraMatch.expirationTime > bestMatch.expirationTime - end - return true -end - -local function lowestExpirationTime(bestMatch, auraMatch) - if bestMatch.expirationTime and auraMatch.expirationTime then - return auraMatch.expirationTime < bestMatch.expirationTime - end - return false -end +local matchCombineFunctions = { + showHighest = function(bestMatch, auraMatch) + if bestMatch.expirationTime and auraMatch.expirationTime then + return auraMatch.expirationTime > bestMatch.expirationTime + end + return true + end, + showLowest = function(bestMatch, auraMatch) + if bestMatch.expirationTime and auraMatch.expirationTime then + return auraMatch.expirationTime < bestMatch.expirationTime + end + return false + end, + showLowestSpellId = function(bestMatch, auraMatch) + if bestMatch.spellId and auraMatch.spellId then + return auraMatch.spellId < bestMatch.spellId + end + return false + end, + showHighestSpellId = function(bestMatch, auraMatch) + if bestMatch.spellId and auraMatch.spellId then + return auraMatch.spellId > bestMatch.spellId + end + return false + end, +} local function GreaterEqualOne(x) return x >= 1 @@ -2503,7 +2516,7 @@ function BuffTrigger.Add(data) remainingCheck = trigger.unit ~= "multi" and CanHaveMatchCheck(trigger) and trigger.useRem and tonumber(trigger.rem) or 0, id = id, triggernum = triggernum, - compareFunc = trigger.combineMode == "showHighest" and highestExpirationTime or lowestExpirationTime, + compareFunc = matchCombineFunctions[trigger.combineMode] or matchCombineFunctions["showLowest"], unitExists = showIfInvalidUnit, fetchTooltip = not IsSingleMissing(trigger) and trigger.unit ~= "multi" and trigger.fetchTooltip, fetchRaidMark = trigger.unit ~= "multi" and trigger.fetchRaidMark, diff --git a/WeakAuras/CHANGELOG.md b/WeakAuras/CHANGELOG.md deleted file mode 100644 index ce88325..0000000 --- a/WeakAuras/CHANGELOG.md +++ /dev/null @@ -1,16 +0,0 @@ -# [2.17.4](https://github.com/WeakAuras/WeakAuras2/tree/2.17.4) (2020-04-22) - -[Full Changelog](https://github.com/WeakAuras/WeakAuras2/compare/2.17.3...2.17.4) - -## Highlights - - - fix a buff tracking and nameplates regression - -## Commits - -InfusOnWoW (3): - -- BT2 Fix Multi by adjusting it to recent changes (#2139) -- Clean up match data if a unit ceases to exists -- Fix nameplates auras sometimes not being applied if in a raid group - diff --git a/WeakAuras/GenericTrigger.lua b/WeakAuras/GenericTrigger.lua index ae87d32..5774757 100644 --- a/WeakAuras/GenericTrigger.lua +++ b/WeakAuras/GenericTrigger.lua @@ -896,13 +896,6 @@ function Private.ScanEvents(event, arg1, arg2, ...) return; end Private.ScanEventsInternal(event_list, event, arg1, arg2, ...); - - elseif (event == "COMBAT_LOG_EVENT_UNFILTERED_CUSTOM") then - -- This reverts the COMBAT_LOG_EVENT_UNFILTERED_CUSTOM workaround so that custom triggers that check the event argument will work as expected - if(event == "COMBAT_LOG_EVENT_UNFILTERED_CUSTOM") then - event = "COMBAT_LOG_EVENT_UNFILTERED"; - end - Private.ScanEventsInternal(event_list, event, arg1, arg2, ...); else Private.ScanEventsInternal(event_list, event, arg1, arg2, ...); end @@ -1148,9 +1141,6 @@ function GenericTrigger.ScanWithFakeEvent(id, fake) end elseif (type(event.force_events) == "boolean" and event.force_events) then for i, eventName in pairs(event.events) do - if eventName == "COMBAT_LOG_EVENT_UNFILTERED_CUSTOM" then - eventName = "COMBAT_LOG_EVENT_UNFILTERED" - end updateTriggerState = RunTriggerFunc(allStates, events[id][triggernum], id, triggernum, eventName) or updateTriggerState; end for unit, unitData in pairs(event.unit_events) do @@ -1179,9 +1169,6 @@ function HandleEvent(frame, event, arg1, arg2, ...) if not(WeakAuras.IsPaused()) then if(event == "COMBAT_LOG_EVENT_UNFILTERED") then Private.ScanEvents(event, arg1, arg2, ...); - -- This triggers the scanning of "hacked" COMBAT_LOG_EVENT_UNFILTERED events that were renamed in order to circumvent - -- the "proper" COMBAT_LOG_EVENT_UNFILTERED checks - Private.ScanEvents("COMBAT_LOG_EVENT_UNFILTERED_CUSTOM", arg1, arg2, ...); else Private.ScanEvents(event, arg1, arg2, ...); end @@ -1429,18 +1416,10 @@ function GenericTrigger.LoadDisplays(toLoad, loadEvent, ...) for triggernum, data in pairs(events[id]) do if data.events then for index, event in pairs(data.events) do - if (event == "COMBAT_LOG_EVENT_UNFILTERED_CUSTOM") then - if not genericTriggerRegisteredEvents["COMBAT_LOG_EVENT_UNFILTERED"] then - eventsToRegister["COMBAT_LOG_EVENT_UNFILTERED"] = true; - end - elseif (event == "FRAME_UPDATE") then + if (event == "FRAME_UPDATE") then register_for_frame_updates = true; - else - if (genericTriggerRegisteredEvents[event]) then - -- Already registered event - else - eventsToRegister[event] = true; - end + elseif not genericTriggerRegisteredEvents[event] then + eventsToRegister[event] = true; end end end @@ -1818,12 +1797,7 @@ function GenericTrigger.Add(data, region) if isCLEU then if hasParam then tinsert(trigger_events, "COMBAT_LOG_EVENT_UNFILTERED") - else - -- This is a dirty, lazy, dirty hack. "Proper" COMBAT_LOG_EVENT_UNFILTERED events are indexed by their sub-event types (e.g. SPELL_PERIODIC_DAMAGE), - -- but custom COMBAT_LOG_EVENT_UNFILTERED events are not guaranteed to have sub-event types. Thus, if the user specifies that they want to use - -- COMBAT_LOG_EVENT_UNFILTERED, this hack renames the event to COMBAT_LOG_EVENT_UNFILTERED_CUSTOM to circumvent the COMBAT_LOG_EVENT_UNFILTERED checks - -- that are already in place. Replacing all those checks would be a pain in the ass. - tinsert(trigger_events, "COMBAT_LOG_EVENT_UNFILTERED_CUSTOM") + -- We don't register CLEU events without parameters anymore end elseif isUnitEvent then -- not added to trigger_events @@ -1884,7 +1858,7 @@ function GenericTrigger.Add(data, region) if warnAboutCLEUEvents then Private.AuraWarnings.UpdateWarning(data.uid, "spammy_event_warning", "error", - L["COMBAT_LOG_EVENT_UNFILTERED with no filter can trigger frame drops in raid environment. Find more information:\nhttps://github.com/WeakAuras/WeakAuras2/wiki/Deprecated-CLEU"]) + L["|cFFFF0000Support for unfiltered COMBAT_LOG_EVENT_UNFILTERED is deprecated|r\nCOMBAT_LOG_EVENT_UNFILTERED without a filter are disabled as it’s very performance costly.\nFind more information:\nhttps://github.com/WeakAuras/WeakAuras2/wiki/Custom-Triggers#events"]) else Private.AuraWarnings.UpdateWarning(data.uid, "spammy_event_warning") end diff --git a/WeakAuras/Init.lua b/WeakAuras/Init.lua index 17a5ab6..c40f157 100644 --- a/WeakAuras/Init.lua +++ b/WeakAuras/Init.lua @@ -8,8 +8,8 @@ WeakAuras.halfWidth = WeakAuras.normalWidth / 2 WeakAuras.doubleWidth = WeakAuras.normalWidth * 2 local versionStringFromToc = GetAddOnMetadata("WeakAuras", "Version") -local versionString = "5.13.2" -local buildTime = "20240701180000" +local versionString = "5.19.0" +local buildTime = "20250127040000" local isAwesomeEnabled = C_NamePlate and C_NamePlate.GetNamePlateForUnit or false WeakAuras.versionString = versionStringFromToc diff --git a/WeakAuras/Media/Creative Commons - CC0 1.0.txt b/WeakAuras/Media/Creative Commons - CC0 1.0.txt new file mode 100644 index 0000000..0e259d4 --- /dev/null +++ b/WeakAuras/Media/Creative Commons - CC0 1.0.txt @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/WeakAuras/Media/Provided by.txt b/WeakAuras/Media/Provided by.txt index fbf2182..61a8c04 100644 --- a/WeakAuras/Media/Provided by.txt +++ b/WeakAuras/Media/Provided by.txt @@ -9,6 +9,7 @@ Creative Commons - Sampling Plus 1.0 Overview: +HeartbeatSingle - Creative Commons CC0 1.0 - original by: bart (https://opengameart.org/content/heartbeat-sounds), modified by: Jieiku (https://github.com/Jieiku) BatmanPunch - Sampling Plus 1.0 - CGEffex (http://www.freesound.org/people/CGEffex/) BikeHorn - Sampling Plus 1.0 - StickInTheMud BoxingArenaSound - Attribution 3.0 - Samantha Enrico @@ -31,4 +32,4 @@ Shotgun - Attribution 3.0 - Mike Koenig SquishFart - Sampling Plus 1.0 - timtube TempleBellHuge - Attribution 3.0 - Mike Koenig Torch - Attribution 3.0 - Mike Koenig -WarningSiren - Attribution 3.0 - snottyboy \ No newline at end of file +WarningSiren - Attribution 3.0 - snottyboy diff --git a/WeakAuras/Media/Sounds/HeartbeatSingle.ogg b/WeakAuras/Media/Sounds/HeartbeatSingle.ogg new file mode 100644 index 0000000000000000000000000000000000000000..48ea5112022efa23780be5f7a848bdcf82ef96ad GIT binary patch literal 36315 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 A9RL6T literal 0 HcmV?d00001 diff --git a/WeakAuras/Modernize.lua b/WeakAuras/Modernize.lua index 54a6b91..b82405e 100644 --- a/WeakAuras/Modernize.lua +++ b/WeakAuras/Modernize.lua @@ -1751,6 +1751,42 @@ function Private.Modernize(data, oldSnapshot) end end + if data.internalVersion < 81 then + -- Rename 'progressSources' to 'progressSource' for Linear/CircularProgressTexture/StopMotion sub elements + local conversions = { + sublineartexture = { + progressSources = "progressSource", + }, + subcirculartexture = { + progressSources = "progressSource", + }, + substopmotion = { + progressSources = "progressSource", + } + } + if data.subRegions then + for index, subRegionData in ipairs(data.subRegions) do + if conversions[subRegionData.type] then + for oldKey, newKey in pairs(conversions[subRegionData.type]) do + subRegionData[newKey] = subRegionData[oldKey] + subRegionData[oldKey] = nil + end + end + end + end + end + + if data.internalVersion < 82 then + -- noMerge for separator custom option doesn't make sense, + -- and groups achieve the desired effect better, + -- so drop the feature + for _, optionData in ipairs(data.authorOptions) do + if optionData.type == "header" then + optionData.noMerge = nil + end + end + end + data.internalVersion = max(data.internalVersion or 0, WeakAuras.InternalVersion()) end diff --git a/WeakAuras/Prototypes.lua b/WeakAuras/Prototypes.lua index 0d3e96e..c9b8649 100644 --- a/WeakAuras/Prototypes.lua +++ b/WeakAuras/Prototypes.lua @@ -13,6 +13,7 @@ local UnitClass = UnitClass local GetSpellInfo, GetItemInfo, GetItemCount, GetItemIcon = GetSpellInfo, GetItemInfo, GetItemCount, GetItemIcon local GetShapeshiftFormInfo, GetShapeshiftForm = GetShapeshiftFormInfo, GetShapeshiftForm local UnitDetailedThreatSituation = UnitDetailedThreatSituation +local MONEY = MONEY local WeakAuras = WeakAuras local L = WeakAuras.L @@ -5362,6 +5363,32 @@ Private.event_prototypes = { store = true, conditionType = "string", }, + { + -- flags + }, + { + -- zone Channel id + }, + { + -- channel index + }, + { + -- channel base name + }, + { + -- language id + }, + { + -- line id + }, + { + name = "sourceGUID", + display = L["Source GUID"], + init = "arg", + store = true, + hidden = true, + test = "true", + }, { name = "cloneId", display = L["Clone per Event"], @@ -7530,6 +7557,75 @@ Private.event_prototypes = { automaticrequired = true, progressType = "none" }, + ["Money"] = { + type = "unit", + statesParameter = "one", + progressType = "none", + automaticrequired = true, + events = { + ["events"] = {"PLAYER_MONEY"} + }, + internal_events = {"WA_DELAYED_PLAYER_ENTERING_WORLD"}, + force_events = "WA_DELAYED_PLAYER_ENTERING_WORLD", + name = WeakAuras.newFeatureString..L["Player Money"], + init = function() + return [=[ + local money = GetMoney() + local gold = floor(money / 1e4) + local silver = floor(money / 100 % 100) + local copper = money % 100 + + local icon = "interface/moneyframe/ui-goldicon" + ]=] + end, + args = { + { + name = "money", + init = "money", + type = "number", + display = L["Money"], + store = true, + hidden = true, + test = "true", + }, + { + name = "gold", + init = "gold", + type = "number", + display = Private.coin_icons.gold .. L["Gold"], + store = true, + conditionType = "number", + }, + { + name = "silver", + init = "silver", + type = "number", + display = Private.coin_icons.silver .. L["Silver"], + store = true, + hidden = true, + test = "true", + }, + { + name = "copper", + init = "copper", + type = "number", + display = Private.coin_icons.copper .. L["Copper"], + store = true, + hidden = true, + test = "true", + }, + { + name = "icon", + init = "icon", + store = true, + hidden = true, + test = "true", + }, + }, + GetNameAndIcon = function(trigger) + return MONEY, "interface/moneyframe/ui-goldicon" + end, + }, --[[ Some day i will finish this, soonTM ["Currency"] = { type = "unit", diff --git a/WeakAuras/RegionTypes/Empty.lua b/WeakAuras/RegionTypes/Empty.lua new file mode 100644 index 0000000..0676805 --- /dev/null +++ b/WeakAuras/RegionTypes/Empty.lua @@ -0,0 +1,50 @@ +if not WeakAuras.IsLibsOK() then return end + +local AddonName, Private = ... + +local L = WeakAuras.L + +local default = { + width = 200, + height = 200, + selfPoint = "CENTER", + anchorPoint = "CENTER", + anchorFrameType = "SCREEN", + xOffset = 0, + yOffset = 0, + frameStrata = 1 +} + +Private.regionPrototype.AddAlphaToDefault(default) + +local properties = { +} + + +Private.regionPrototype.AddProperties(properties, default); + +local function create(parent) + local region = CreateFrame("Frame", nil, UIParent) + region.regionType = "empty" + region:SetMovable(true) + region:SetResizable(true) + region:SetMinResize(1, 1) + + region.Update = function() end + + Private.regionPrototype.create(region) + return region +end + +local function modify(parent, region, data) + Private.regionPrototype.modify(parent, region, data) + region:SetWidth(data.width) + region:SetHeight(data.height) + region.width = data.width + region.height = data.height + region.scalex = 1 + region.scaley = 1 + Private.regionPrototype.modifyFinish(parent, region, data) +end + +Private.RegisterRegionType("empty", create, modify, default, properties) diff --git a/WeakAuras/RegionTypes/RegionPrototype.lua b/WeakAuras/RegionTypes/RegionPrototype.lua index df9741c..ade62f1 100644 --- a/WeakAuras/RegionTypes/RegionPrototype.lua +++ b/WeakAuras/RegionTypes/RegionPrototype.lua @@ -713,20 +713,7 @@ function Private.regionPrototype.create(region) region:SetPoint("CENTER", UIParent, "CENTER") end -function Private.regionPrototype.modify(parent, region, data) - region.state = nil - region.states = nil - region.subRegionEvents:ClearSubscribers() - region.subRegionEvents:ClearCallbacks() - Private.FrameTick:RemoveSubscriber("Tick", region) - - local defaultsForRegion = Private.regionTypes[data.regionType] and Private.regionTypes[data.regionType].default; - - if region.SetRegionAlpha then - region:SetRegionAlpha(data.alpha) - end - - local hasProgressSource = defaultsForRegion and defaultsForRegion.progressSource +function Private.regionPrototype.AddMinMaxProgressSource(hasProgressSource, region, parentData, data) local hasAdjustedMin = hasProgressSource and data.useAdjustededMin and data.adjustedMin local hasAdjustedMax = hasProgressSource and data.useAdjustededMax and data.adjustedMax @@ -737,7 +724,7 @@ function Private.regionPrototype.modify(parent, region, data) region.adjustedMaxRelPercent = nil if hasProgressSource then - region.progressSource = Private.AddProgressSourceMetaData(data, data.progressSource) + region.progressSource = Private.AddProgressSourceMetaData(parentData, data.progressSource) end if (hasAdjustedMin) then @@ -756,6 +743,19 @@ function Private.regionPrototype.modify(parent, region, data) region.adjustedMax = tonumber(data.adjustedMax) end end +end +function Private.regionPrototype.modify(parent, region, data) + region.state = nil + region.states = nil + region.subRegionEvents:ClearSubscribers() + region.subRegionEvents:ClearCallbacks() + Private.FrameTick:RemoveSubscriber("Tick", region) + local defaultsForRegion = Private.regionTypes[data.regionType] and Private.regionTypes[data.regionType].default; + if region.SetRegionAlpha then + region:SetRegionAlpha(data.alpha) + end + local hasProgressSource = defaultsForRegion and defaultsForRegion.progressSource + Private.regionPrototype.AddMinMaxProgressSource(hasProgressSource, region, data, data) region:SetOffset(data.xOffset or 0, data.yOffset or 0); region:SetOffsetRelative(0, 0) diff --git a/WeakAuras/RegionTypes/Texture.lua b/WeakAuras/RegionTypes/Texture.lua index 7fd015e..2a20e3f 100644 --- a/WeakAuras/RegionTypes/Texture.lua +++ b/WeakAuras/RegionTypes/Texture.lua @@ -150,6 +150,10 @@ local function modify(parent, region, data) self:UpdateProgress() end + function region:SetTexture(texture) + self.texture:SetTexture(texture) + end + region.texture:SetTexture(data.texture) function region:Color(r, g, b, a) diff --git a/WeakAuras/SubRegionTypes/Border.lua b/WeakAuras/SubRegionTypes/Border.lua index 0cb487a..dab509f 100644 --- a/WeakAuras/SubRegionTypes/Border.lua +++ b/WeakAuras/SubRegionTypes/Border.lua @@ -83,9 +83,10 @@ end local function supports(regionType) return regionType == "texture" - or regionType == "progresstexture" - or regionType == "icon" - or regionType == "aurabar" + or regionType == "progresstexture" + or regionType == "icon" + or regionType == "aurabar" + or regionType == "empty" end WeakAuras.RegisterSubRegionType("subborder", L["Border"], supports, create, modify, onAcquire, onRelease, default, nil, properties); diff --git a/WeakAuras/SubRegionTypes/Glow.lua b/WeakAuras/SubRegionTypes/Glow.lua index de447d1..be08c7b 100644 --- a/WeakAuras/SubRegionTypes/Glow.lua +++ b/WeakAuras/SubRegionTypes/Glow.lua @@ -384,7 +384,8 @@ local supportedRegion = { icon = true, aurabar = true, texture = true, - progresstexture = true + progresstexture = true, + empty = true, } local function supports(regionType) return supportedRegion[regionType] diff --git a/WeakAuras/SubRegionTypes/Model.lua b/WeakAuras/SubRegionTypes/Model.lua index b8d0bc4..37454fa 100644 --- a/WeakAuras/SubRegionTypes/Model.lua +++ b/WeakAuras/SubRegionTypes/Model.lua @@ -221,6 +221,7 @@ local function supports(regionType) or regionType == "icon" or regionType == "aurabar" or regionType == "text" + or regionType == "empty" end WeakAuras.RegisterSubRegionType("submodel", L["Model"], supports, create, modify, onAcquire, onRelease, default, nil, properties); diff --git a/WeakAuras/SubRegionTypes/StopMotion.lua b/WeakAuras/SubRegionTypes/StopMotion.lua index 0a8e332..121f68c 100644 --- a/WeakAuras/SubRegionTypes/StopMotion.lua +++ b/WeakAuras/SubRegionTypes/StopMotion.lua @@ -34,7 +34,7 @@ local default = function(parentType) height = 32, scale = 1, - progressSources = {-2, ""}, + progressSource = {-2, ""}, } if IsAddOnLoaded("WeakAurasStopMotion") then @@ -153,7 +153,7 @@ local ProgressFuncs = { end end, Update = function(self, state, states) - Private.UpdateProgressFrom(self.progressData, self.progressSource, {}, state, states, self.parent) + Private.UpdateProgressFrom(self.progressData, self.progressSource, self, state, states, self.parent) self:UpdateFrame() self:UpdateFrameTick() end, @@ -243,7 +243,9 @@ local function modify(parent, region, parentData, data, first) customFrameHeight = data.customFrameHeight, }) - region.progressSource = Private.AddProgressSourceMetaData(parentData, data.progressSources or {-2, ""}) + region.stopMotion:SetColor(unpack(data.stopmotionColor)) + + Private.regionPrototype.AddMinMaxProgressSource(true, region, parentData, data) region.FrameTick = nil if data.animationType == "loop" or data.animationType == "bounce" or data.animationType == "once" then @@ -272,6 +274,7 @@ local function supports(regionType) or regionType == "icon" or regionType == "aurabar" or regionType == "text" + or regionType == "empty" end WeakAuras.RegisterSubRegionType("substopmotion", L["Stop Motion"], supports, create, modify, onAcquire, onRelease, default, nil, properties) diff --git a/WeakAuras/SubRegionTypes/SubText.lua b/WeakAuras/SubRegionTypes/SubText.lua index 0814510..ac30e5f 100644 --- a/WeakAuras/SubRegionTypes/SubText.lua +++ b/WeakAuras/SubRegionTypes/SubText.lua @@ -486,6 +486,7 @@ local function supports(regionType) or regionType == "progresstexture" or regionType == "icon" or regionType == "aurabar" + or regionType == "empty" end WeakAuras.RegisterSubRegionType("subtext", L["Text"], supports, create, modify, onAcquire, onRelease, diff --git a/WeakAuras/SubRegionTypes/Texture.lua b/WeakAuras/SubRegionTypes/Texture.lua index 530f4af..d08837d 100644 --- a/WeakAuras/SubRegionTypes/Texture.lua +++ b/WeakAuras/SubRegionTypes/Texture.lua @@ -66,10 +66,6 @@ local properties = { bigStep = 1, default = 0 } - - -- TODO width? - -- TODO height? - -- } local funcs = { @@ -156,6 +152,7 @@ local function supports(regionType) or regionType == "icon" or regionType == "aurabar" or regionType == "text" + or regionType == "empty" end WeakAuras.RegisterSubRegionType("subtexture", L["Texture"], supports, create, modify, onAcquire, onRelease, default, nil, properties) diff --git a/WeakAuras/Types.lua b/WeakAuras/Types.lua index e47a568..b9c159b 100644 --- a/WeakAuras/Types.lua +++ b/WeakAuras/Types.lua @@ -101,6 +101,9 @@ Private.big_number_types = { ["AbbreviateLargeNumbers"] = L["AbbreviateLargeNumbers (Blizzard)"] } +Private.big_number_types_with_disable = CopyTable(Private.big_number_types) +Private.big_number_types_with_disable["disable"] = L["Disabled"] + Private.round_types = { floor = L["Floor"], ceil = L["Ceil"], @@ -282,6 +285,58 @@ Private.format_types = { end }, --[[ + Money = { + display = L["Money"], + AddOptions = function(symbol, hidden, addOption) + addOption(symbol .. "_money_format", { + type = "select", + name = L["Format Gold"], + width = WeakAuras.normalWidth, + values = Private.big_number_types_with_disable, + hidden = hidden + }) + addOption(symbol .. "_money_precision", { + type = "select", + name = L["Coin Precision"], + width = WeakAuras.normalWidth, + values = Private.money_precision_types, + hidden = hidden + }) + end, + CreateFormatter = function(symbol, get) + local format = get(symbol .. "_money_format", "AbbreviateNumbers") + local precision = get(symbol .. "_money_precision", 3) + + return function(value) + local gold = floor(value / 1e4) + local silver = floor(value / 100 % 100) + local copper = value % 100 + + if (format == "AbbreviateNumbers") then + gold = simpleFormatters.AbbreviateNumbers(gold) + elseif (format == "BreakUpLargeNumbers") then + gold = simpleFormatters.BreakUpLargeNumbers(gold) + elseif (format == "AbbreviateLargeNumbers") then + gold = simpleFormatters.AbbreviateLargeNumbers(gold) + end + + local formatCode + if precision == 1 then + formatCode = "%s%s" + elseif precision == 2 then + formatCode = "%s%s %d%s" + else + formatCode = "%s%s %d%s %d%s" + end + + return string.format(formatCode, + tostring(gold), Private.coin_icons.gold, + silver, Private.coin_icons.silver, + copper, Private.coin_icons.copper + ) + end + end + }, BigNumber = { display = L["Big Number"], AddOptions = function(symbol, hidden, addOption) @@ -1170,6 +1225,24 @@ for id, str in pairs(Private.combatlog_spell_school_types) do Private.combatlog_spell_school_types_for_ui[id] = ("%.3d - %s"):format(id, str) end +Private.money_icons = { + ["gold"] = "interface/moneyframe/ui-goldicon", + ["silver"] = "interface/moneyframe/ui-silvericon", + ["copper"] = "interface/moneyframe/ui-coppericon" +} + +Private.coin_icons = { + ["gold"] = "|Tinterface/moneyframe/ui-goldicon:0|t", + ["silver"] = "|Tinterface/moneyframe/ui-silvericon:0|t", + ["copper"] = "|Tinterface/moneyframe/ui-coppericon:0|t" +} + +Private.money_precision_types = { + [1] = "123 " .. Private.coin_icons.gold, + [2] = "123 " .. Private.coin_icons.gold .. " 45 " .. Private.coin_icons.silver, + [3] = "123 " .. Private.coin_icons.gold .. " 45 " .. Private.coin_icons.silver .. " 67 " .. Private.coin_icons.copper +} + Private.item_quality_types = { [0] = ITEM_QUALITY0_DESC, [1] = ITEM_QUALITY1_DESC, @@ -2222,6 +2295,7 @@ Private.cast_types = { } -- register sounds +LSM:Register("sound", "Heartbeat Single", "Interface\\AddOns\\WeakAuras\\Media\\Sounds\\HeartbeatSingle.ogg") LSM:Register("sound", "Batman Punch", "Interface\\AddOns\\WeakAuras\\Media\\Sounds\\BatmanPunch.ogg") LSM:Register("sound", "Bike Horn", "Interface\\AddOns\\WeakAuras\\Media\\Sounds\\BikeHorn.ogg") LSM:Register("sound", "Boxing Arena Gong", "Interface\\AddOns\\WeakAuras\\Media\\Sounds\\BoxingArenaSound.ogg") @@ -2441,7 +2515,9 @@ Private.bufftrigger_2_progress_behavior_types = { Private.bufftrigger_2_preferred_match_types = { showLowest = L["Least remaining time"], - showHighest = L["Most remaining time"] + showHighest = L["Most remaining time"], + showLowestSpellId = L["Lowest Spell Id"], + showHighestSpellId = L["Highest Spell Id"], } Private.bufftrigger_2_per_unit_mode = { @@ -2800,7 +2876,6 @@ Private.author_option_fields = { header = { useName = false, text = "", - noMerge = false }, group = { groupType = "simple", diff --git a/WeakAuras/WeakAuras.lua b/WeakAuras/WeakAuras.lua index dcbaf88..7f9db36 100644 --- a/WeakAuras/WeakAuras.lua +++ b/WeakAuras/WeakAuras.lua @@ -1,6 +1,6 @@ local AddonName, Private = ... -local internalVersion = 80 +local internalVersion = 82 -- Lua APIs local insert = table.insert diff --git a/WeakAuras/WeakAuras.toc b/WeakAuras/WeakAuras.toc index d9c8ef0..3ecc97f 100644 --- a/WeakAuras/WeakAuras.toc +++ b/WeakAuras/WeakAuras.toc @@ -1,7 +1,7 @@ ## Interface: 30300 ## Title: WeakAuras ## Author: The WeakAuras Team -## Version: 5.13.2 +## Version: 5.19.0 ## Notes: A powerful, comprehensive utility for displaying graphics and information based on buffs, debuffs, and other triggers. ## Notes-esES: Potente y completa aplicación que te permitirá mostrar por pantalla múltiples diseños, basados en beneficios, perjuicios y otros activadores. ## Notes-deDE: Ein leistungsfähiges, umfassendes Addon zur grafischen Darstellung von Informationen von Auren, Cooldowns, Timern und vielem mehr. @@ -55,6 +55,7 @@ SubscribableObject.lua # Region support RegionTypes\SmoothStatusBarMixin.lua RegionTypes\RegionPrototype.lua +RegionTypes\Empty.lua RegionTypes\ProgressTexture.lua BaseRegions\Texture.lua RegionTypes\Texture.lua diff --git a/WeakAurasOptions/AuthorOptions.lua b/WeakAurasOptions/AuthorOptions.lua index b2ce0be..0c512a3 100644 --- a/WeakAurasOptions/AuthorOptions.lua +++ b/WeakAurasOptions/AuthorOptions.lua @@ -1190,15 +1190,6 @@ typeControlAdders = { return not option.useName end } - args[prefix .. "noMerge"] = { - type = "toggle", - name = name(option, "noMerge", L["Prevent Merging"]), - desc = desc(option, "noMerge", L["If checked, then this separator will not merge with other separators when selecting multiple auras."]), - order = order(), - width = WeakAuras.doubleWidth, - get = get(option, "noMerge"), - set = set(data, option, "noMerge"), - } end, group = function(options, args, data, order, prefix, i) local option = options[i] diff --git a/WeakAurasOptions/Changelog.lua b/WeakAurasOptions/Changelog.lua new file mode 100644 index 0000000..5ddfdea --- /dev/null +++ b/WeakAurasOptions/Changelog.lua @@ -0,0 +1,89 @@ +if not WeakAuras.IsLibsOK() then return end +---@type string +local AddonName = ... +---@class OptionsPrivate +local OptionsPrivate = select(2, ...) + +if not WeakAuras.IsLibsOK() then return end +---@type string +local AddonName = ... +---@class OptionsPrivate +local OptionsPrivate = select(2, ...) +OptionsPrivate.changelog = { + versionString = '5.19.0', + dateString = '2025-01-20', + fullChangeLogUrl = 'https://github.com/WeakAuras/WeakAuras2/compare/5.18.1...5.19.0', + highlightText = [==[ +- Some Important changes: + - 🚨🚨 WeakAuras no longer dispatches COMBAT_LOG_EVENT_UNFILTERED to custom triggers, unless the event list specifies at least one subevent. 🚨🚨 You will need to make changes to these auras to get them to function properly. + - Actions: For performance reasons, "On Init" custom code for auras with an encounter ID load option is not as eager to load as previous. We don't anticipate this should cause any problems, but if it does please get in touch! +- Many new features: + - This changelog is now viewable in game, via the changelog button (if you're reading this from the in game changelog, hi!) + - New Region Type: Empty region is now available, for "displays" which either don't need a visual component (e.g. 'play a sound when i get 5 buffs from roll the bones'), and for wacky custom designs. + - New Trigger: Player Money can now be tracked in the builtins, & a coin string formatting option is avaialable for text patterns. (thanks Boneshock!) + - New sub element: Stop Motion is now available as a subregion to add to texture, progress texture, icon, progress bar, text, & empty retion types. + - Linear/Circular Progress elements now support min/max progress properties. + - Classic (Cataclysm): Spell Power is now tracked in the Character Stats trigger. + - Chat Message Events: sourceGUID is now exposed in Other Events - Chat Message triggers. Note that some message types don't have a source & thuse don't provide sourceGUID either. + - New Media: "Heartbeat Single" (try playing it on a loop) is now provided in the builtin media. (thanks Jake!) + - Aura Trigger: new match selectors based on spell ID are now avaialble.]==], commitText = [==[Boneshock (1): + +- Add Money Formatting Option and Add Player Money to Currency Trigger (#5586) + +InfusOnWoW (23): + +- Update Atlas File List from wago.tools +- Update Discord List +- Templates Classic: Fix Paladin templates +- Classic: Enable UnitGroupRoleAssigned options +- Bufftrigger 2: Add match selectors that work on spell ids +- Make StopMotion sub elements's color work +- Update Discord List +- Texture Sub Element: Fix ordering of input and browse button +- Stop Motion: Properly fix GetColor function +- Fix description of Stop Motion sub element +- Add Thank you Role to allowed roles +- Add a Changelog button +- Add an Empty RegionType +- Fix lua error for color animation on Stop Motion +- Add min/max progress for Linear/CircularProgress and StopMotion sub elements +- Remove left over TODOs that are actually done +- Fix regressions in Textures refactor +- Cata: Add Spell Power to Character Stats +- Introduce sub elements for circular/linear Textures +- Texture Sub Element +- StopMotion: Introduce a StopMotion sub element +- Update Atlas File List from wago.tools +- Update Discord List + +Jake G (1): + +- Add Sound Heartbeat Single (#5600) + +Stanzilla (4): + +- Update WeakAurasModelPaths from wago.tools +- Update bug_report.yml +- Update WeakAurasModelPaths from wago.tools +- Update WeakAurasModelPaths from wago.tools + +emptyrivers (2): + +- drop nomerge headers +- add sourceGUID to chat msg trigger state + +github-actions[bot] (1): + +- Update Atlas File List from wago.tools (#5618) + +mrbuds (2): + +- Don't pre-load in raid init scripts for auras with an encounterId load option +- Disable CLEU triggers without filters + +nullKomplex (1): + +- Default discord-update to not run on forks + +]==] +} \ No newline at end of file diff --git a/WeakAurasOptions/CommonOptions.lua b/WeakAurasOptions/CommonOptions.lua index 668fff0..9695981 100644 --- a/WeakAurasOptions/CommonOptions.lua +++ b/WeakAurasOptions/CommonOptions.lua @@ -1566,9 +1566,191 @@ local function PositionOptionsForSubElement(data, options, startOrder, areaAncho softMax = 200, step = 1, } - end +local function ProgressOptionsForSubElement(parentData, data, options, startOrder, progressSourceHidden) + options.progress_source = { + type = "select", + width = WeakAuras.doubleWidth, + name = L["Progress Source"], + order = startOrder, + control = "WeakAurasTwoColumnDropdown", + values = OptionsPrivate.Private.GetProgressSourcesForUi(parentData, true), + get = function(info) + return OptionsPrivate.Private.GetProgressValueConstant(data.progressSource or {-2, ""}) + end, + set = function(info, value) + if value then + data.progressSource = data.progressSource or {} + -- Copy only trigger + property + data.progressSource[1] = value[1] + data.progressSource[2] = value[2] + else + data.progressSource = nil + end + WeakAuras.Add(parentData) + end, + hidden = progressSourceHidden + } + + options.progressSourceWarning = { + type = "description", + width = WeakAuras.doubleWidth, + name = L["Note: This progress source does not provide a total value/duration. A total value/duration must be set via \"Set Maximum Progress\""], + order = startOrder + 0.1, + hidden = function() + if type(progressSourceHidden) == "function" and progressSourceHidden() then + return true + end + local progressSource = OptionsPrivate.Private.AddProgressSourceMetaData(parentData, data.progressSource) + -- Auto progress, Manual Progress or the progress source has a total property + if not progressSource or progressSource[2] == "auto" or progressSource[1] == 0 or progressSource[4] ~= nil then + return true + end + return false + end, + } + + local function hiddenManual() + if type(progressSourceHidden) == "function" and progressSourceHidden() then + return true + end + if data.progressSource and data.progressSource[1] == 0 then + return false + end + return true + end + + options.progressSourceManualValue = { + type = "range", + control = "WeakAurasSpinBox", + width = WeakAuras.normalWidth, + name = L["Value"], + order = startOrder + 0.2, + min = 0, + softMax = 100, + bigStep = 1, + hidden = hiddenManual, + get = function(info) + return data.progressSource and data.progressSource[3] or 0 + end, + set = function(info, value) + data.progressSource = data.progressSource or {} + data.progressSource[3] = value + WeakAuras.Add(parentData) + end + } + + options.progressSourceManualTotal = { + type = "range", + control = "WeakAurasSpinBox", + width = WeakAuras.normalWidth, + name = L["Total"], + order = startOrder + 0.3, + min = 0, + softMax = 100, + bigStep = 1, + hidden = hiddenManual, + get = function(info) + return data.progressSource and data.progressSource[4] or 100 + end, + set = function(info, value) + data.progressSource = data.progressSource or {} + data.progressSource[4] = value + WeakAuras.Add(parentData) + end + } + + options.useAdjustededMin = { + type = "toggle", + width = WeakAuras.normalWidth, + name = L["Set Minimum Progress"], + desc = L["Values/Remaining Time below this value are displayed as zero progress."], + order = startOrder + 0.4, + set = function(info, value) + data.useAdjustededMin = value + if not value then + data.adjustedMin = "" + end + WeakAuras.Add(parentData) + end, + hidden = progressSourceHidden + }; + + options.adjustedMin = { + type = "input", + validate = WeakAuras.ValidateNumericOrPercent, + width = WeakAuras.normalWidth, + order = startOrder + 0.5, + name = L["Minimum"], + hidden = function() + if type(progressSourceHidden) == "function" and progressSourceHidden() then + return true + end + return not data.useAdjustededMin + end, + desc = L["Enter static or relative values with %"] + }; + + options.useAdjustedMinSpacer = { + type = "description", + width = WeakAuras.normalWidth, + name = "", + order = startOrder + 0.6, + hidden = function() + if type(progressSourceHidden) == "function" and progressSourceHidden() then + return true + end + return not (not data.useAdjustededMin and data.useAdjustededMax) + end, + } + + options.useAdjustededMax = { + type = "toggle", + width = WeakAuras.normalWidth, + name = L["Set Maximum Progress"], + desc = L["Values/Remaining Time above this value are displayed as full progress."], + order = startOrder + 0.7, + set = function(info, value) + data.useAdjustededMax = value + if not value then + data.adjustedMax = "" + end + WeakAuras.Add(parentData) + end, + hidden = progressSourceHidden + } + + options.adjustedMax = { + type = "input", + width = WeakAuras.normalWidth, + validate = WeakAuras.ValidateNumericOrPercent, + order = startOrder + 0.8, + name = L["Maximum"], + hidden = function() + if type(progressSourceHidden) == "function" and progressSourceHidden() then + return true + end + return not data.useAdjustededMax + end, + desc = L["Enter static or relative values with %"] + } + + options.useAdjustedMaxSpacer = { + type = "description", + width = WeakAuras.normalWidth, + name = "", + order = startOrder + 0.9, + hidden = function() + if type(progressSourceHidden) == "function" and progressSourceHidden() then + return true + end + return not (data.useAdjustededMin and not data.useAdjustededMax) + end, + } +end + + local function BorderOptions(id, data, showBackDropOptions, hiddenFunc, order) local borderOptions = { borderHeader = { @@ -1902,6 +2084,7 @@ OptionsPrivate.commonOptions.CreateExecuteAll = CreateExecuteAll OptionsPrivate.commonOptions.PositionOptions = PositionOptions OptionsPrivate.commonOptions.PositionOptionsForSubElement = PositionOptionsForSubElement OptionsPrivate.commonOptions.ProgressOptions = ProgressOptions +OptionsPrivate.commonOptions.ProgressOptionsForSubElement = ProgressOptionsForSubElement OptionsPrivate.commonOptions.BorderOptions = BorderOptions OptionsPrivate.commonOptions.AddCodeOption = AddCodeOption diff --git a/WeakAurasOptions/OptionsFrames/OptionsFrame.lua b/WeakAurasOptions/OptionsFrames/OptionsFrame.lua index 3866924..207bf58 100644 --- a/WeakAurasOptions/OptionsFrames/OptionsFrame.lua +++ b/WeakAurasOptions/OptionsFrames/OptionsFrame.lua @@ -483,12 +483,28 @@ function OptionsPrivate.CreateFrame() thanksButton:SetParent(tipFrame) thanksButton:SetPoint("LEFT", documentationButton, "RIGHT", 10, 0) + local changelogButton + if OptionsPrivate.changelog then + local changelog + if OptionsPrivate.changelog.highlightText then + changelog = L["Highlights"] .. "\n" .. OptionsPrivate.changelog.highlightText .. "\n" + .. L["Commits"] .. "\n" ..OptionsPrivate.changelog.commitText + else + changelog = OptionsPrivate.changelog.commitText + end + + changelogButton = addFooter(L["Changelog"], "", OptionsPrivate.changelog.fullChangeLogUrl, + changelog, nil, nil, false, 800) + changelogButton:SetParent(tipFrame) + changelogButton:SetPoint("LEFT", thanksButton, "RIGHT", 10, 0) + end + local awesomeWotlkButton if not WeakAuras.isAwesomeEnabled() then awesomeWotlkButton = addFooter("Awesome WotLK", [[Interface\AddOns\WeakAuras\Media\Textures\GitHub.tga]], "https://github.com/FrostAtom/awesome_wotlk/releases", L["Unlock nameplate anchoring & units in WeakAuras with the awesome_wotlk client patch"]) awesomeWotlkButton:SetParent(tipFrame) - awesomeWotlkButton:SetPoint("LEFT", thanksButton, "RIGHT", 10, 0) + awesomeWotlkButton:SetPoint("LEFT", changelogButton or thanksButton, "RIGHT", 10, 0) end local reportbugButton = addFooter(L["Found a Bug?"], [[Interface\AddOns\WeakAuras\Media\Textures\bug_report.tga]], "https://github.com/NoM0Re/WeakAuras-WotLK/issues", diff --git a/WeakAurasOptions/RegionOptions/Empty.lua b/WeakAurasOptions/RegionOptions/Empty.lua new file mode 100644 index 0000000..71508af --- /dev/null +++ b/WeakAurasOptions/RegionOptions/Empty.lua @@ -0,0 +1,54 @@ +if not WeakAuras.IsLibsOK() then return end + +local AddonName, OptionsPrivate = ... + +local L = WeakAuras.L + +local function createOptions(id, data) + local options = { + __title = L["Settings"], + __order = 1, + alpha = { + type = "range", + control = "WeakAurasSpinBox", + width = WeakAuras.normalWidth, + name = L["Alpha"], + order = 1, + min = 0, + max = 1, + bigStep = 0.01, + isPercent = true + }, + } + + return { + empty = options, + position = OptionsPrivate.commonOptions.PositionOptions(id, data), + } + +end + +local function createThumbnail() + local frame = CreateFrame("Frame", nil, UIParent) + frame:SetWidth(32) + frame:SetHeight(32) + + local border = frame:CreateTexture(nil, "OVERLAY") + border:SetAllPoints(frame) + border:SetTexture("Interface\\BUTTONS\\UI-Quickslot2.blp") + border:SetTexCoord(0.2, 0.8, 0.2, 0.8) + + return frame +end + +local function modifyThumbnail(parent, frame, data) + +end + +-- Register new region type options with WeakAuras +OptionsPrivate.registerRegions = OptionsPrivate.registerRegions or {} +table.insert(OptionsPrivate.registerRegions, function() + OptionsPrivate.Private.RegisterRegionOptions("empty", createOptions, createThumbnail, L["Empty Base Region"], + createThumbnail, modifyThumbnail, + L["Shows nothing, except sub elements"]); +end) diff --git a/WeakAurasOptions/SubRegionOptions/StopMotion.lua b/WeakAurasOptions/SubRegionOptions/StopMotion.lua index a6d7382..27eb295 100644 --- a/WeakAurasOptions/SubRegionOptions/StopMotion.lua +++ b/WeakAurasOptions/SubRegionOptions/StopMotion.lua @@ -226,41 +226,7 @@ local function createOptions(parentData, data, index, subIndex) values = animation_types }, - progress_source = { - type = "select", - width = WeakAuras.normalWidth, - name = L["Progress Source"], - order = 15, - control = "WeakAurasTwoColumnDropdown", - values = OptionsPrivate.Private.GetProgressSourcesForUi(parentData, true), - get = function(info) - return OptionsPrivate.Private.GetProgressValueConstant(data.progressSources or {-2, ""}) - end, - set = function(info, value) - if value then - data.progressSources = data.progressSources or {} - -- Copy only trigger + property - data.progressSources[1] = value[1] - data.progressSources[2] = value[2] - else - data.progressSources = nil - end - WeakAuras.Add(parentData) - end, - hidden = function() - return not(data.animationType == "progress") - end - }, - - progress_source_space = { - type = "description", - name = "", - order = 16, - width = WeakAuras.normalWidth, - hidden = function() - return not(data.animationType == "progress") - end - }, + -- progress source added below startPercent = { type = "range", @@ -341,10 +307,16 @@ local function createOptions(parentData, data, index, subIndex) }, } + local progressSourceHiden = function() + return not(data.animationType == "progress") + end + + OptionsPrivate.commonOptions.ProgressOptionsForSubElement(parentData, data, options, 15, progressSourceHiden) OptionsPrivate.commonOptions.PositionOptionsForSubElement(data, options, 21, areaAnchors, pointAnchors) OptionsPrivate.AddUpDownDeleteDuplicate(options, parentData, index, "substopmotion") return options end - WeakAuras.RegisterSubRegionOptions("substopmotion", createOptions, L["Shows a Stop Moption"]); +WeakAuras.RegisterSubRegionOptions("substopmotion", createOptions, L["Shows a Stop Motion"]); + diff --git a/WeakAurasOptions/SubRegionOptions/Texture.lua b/WeakAurasOptions/SubRegionOptions/Texture.lua index 2849ab3..8233f06 100644 --- a/WeakAurasOptions/SubRegionOptions/Texture.lua +++ b/WeakAurasOptions/SubRegionOptions/Texture.lua @@ -12,7 +12,6 @@ local function createOptions(parentData, data, index, subIndex) WeakAuras.Mixin(areaAnchors, OptionsPrivate.Private.GetAnchorsForData(child, "area")) end - -- TODO verfiy order being ordered local options = { __title = L["Texture %s"]:format(subIndex), __order = 1, diff --git a/WeakAurasOptions/WeakAurasOptions.toc b/WeakAurasOptions/WeakAurasOptions.toc index 5ad377e..2dc0f98 100644 --- a/WeakAurasOptions/WeakAurasOptions.toc +++ b/WeakAurasOptions/WeakAurasOptions.toc @@ -1,7 +1,7 @@ ## Interface: 30300 ## Title: WeakAuras Options ## Author: The WeakAuras Team -## Version: 5.13.2 +## Version: 5.19.0 ## Notes: Options for WeakAuras ## Notes-esES: Opciones para WeakAuras ## Notes-deDE: Optionen für WeakAuras @@ -20,6 +20,7 @@ locales.xml VersionCheck.lua ForAllIndentsAndPurposes.lua +RegionOptions\Empty.lua RegionOptions\AuraBar.lua RegionOptions\Texture.lua RegionOptions\Icon.lua @@ -58,6 +59,8 @@ WeakAurasOptions.lua ConditionOptions.lua AuthorOptions.lua +Changelog.lua + OptionsFrames\OptionsFrame.lua # Groups