diff --git a/WeakAuras/Animations.lua b/WeakAuras/Animations.lua new file mode 100644 index 0000000..f8899c5 --- /dev/null +++ b/WeakAuras/Animations.lua @@ -0,0 +1,470 @@ +if not WeakAuras.IsCorrectVersion() then return end +local AddonName, Private = ... + +-- Animations +local animations = {} +local pending_controls = {} +local anim_presets = WeakAuras.anim_presets; +local anim_function_strings = WeakAuras.anim_function_strings; + +local function noopErrorHandler() end + +local frame = WeakAuras.frames["WeakAuras Main Frame"] + +local updatingAnimations; +local last_update = GetTime(); +local function UpdateAnimations() + WeakAuras.StartProfileSystem("animations"); + local errorHandler = WeakAuras.IsOptionsOpen() and noopErrorHandler or geterrorhandler() + for groupId, groupRegion in pairs(pending_controls) do + pending_controls[groupId] = nil; + groupRegion:DoPositionChildren(); + end + local time = GetTime(); + local elapsed = time - last_update; + last_update = time; + local num = 0; + for id, anim in pairs(animations) do + WeakAuras.StartProfileAura(anim.name); + num = num + 1; + local finished = false; + if(anim.duration_type == "seconds") then + if anim.duration > 0 then + anim.progress = anim.progress + (elapsed / anim.duration); + else + anim.progress = anim.progress + (elapsed / 1); + end + if(anim.progress >= 1) then + anim.progress = 1; + finished = true; + end + elseif(anim.duration_type == "relative") then + local state = anim.region.state; + if (not state + or (state.progressType == "timed" and state.duration < 0.01) + or (state.progressType == "static" and state.value < 0.01)) then + anim.progress = 0; + if(anim.type == "start" or anim.type == "finish") then + finished = true; + end + else + local relativeProgress = 0; + if(state.progressType == "static") then + relativeProgress = state.value / state.total; + elseif (state.progressType == "timed") then + relativeProgress = 1 - ((state.expirationTime - time) / state.duration); + end + relativeProgress = state.inverse and (1 - relativeProgress) or relativeProgress; + anim.progress = relativeProgress / anim.duration + local iteration = math.floor(anim.progress); + --anim.progress = anim.progress - iteration; + if not(anim.iteration) then + anim.iteration = iteration; + elseif(anim.iteration ~= iteration) then + anim.iteration = nil; + finished = true; + end + end + else + anim.progress = 1; + end + local progress = anim.inverse and (1 - anim.progress) or anim.progress; + progress = anim.easeFunc(progress, anim.easeStrength or 3) + WeakAuras.ActivateAuraEnvironment(anim.name, anim.cloneId, anim.region.state, anim.region.states); + if(anim.translateFunc) then + if (anim.region.SetOffsetAnim) then + local ok, x, y = pcall(anim.translateFunc, progress, 0, 0, anim.dX, anim.dY); + if not ok then + errorHandler(x) + else + anim.region:SetOffsetAnim(x, y); + end + else + anim.region:ClearAllPoints(); + local ok, x, y = xpcall(anim.translateFunc, progress, anim.startX, anim.startY, anim.dX, anim.dY); + if not ok then + errorHandler(x) + else + anim.region:SetPoint(anim.selfPoint, anim.anchor, anim.anchorPoint, x, y); + end + end + end + if(anim.alphaFunc) then + local ok, alpha = pcall(anim.alphaFunc, progress, anim.startAlpha, anim.dAlpha); + if not ok then + errorHandler(alpha) + else + if (anim.region.SetAnimAlpha) then + anim.region:SetAnimAlpha(alpha); + else + anim.region:SetAlpha(alpha); + end + end + end + if(anim.scaleFunc) then + local ok, scaleX, scaleY = pcall(anim.scaleFunc, progress, 1, 1, anim.scaleX, anim.scaleY); + if not ok then + errorHandler(scaleX) + else + if(anim.region.Scale) then + anim.region:Scale(scaleX, scaleY); + else + anim.region:SetWidth(anim.startWidth * scaleX); + anim.region:SetHeight(anim.startHeight * scaleY); + end + end + end + if(anim.rotateFunc and anim.region.Rotate) then + local ok, rotate = pcall(anim.rotateFunc, progress, anim.startRotation, anim.rotate); + if not ok then + errorHandler(rotate) + else + anim.region:Rotate(rotate); + end + end + if(anim.colorFunc and anim.region.ColorAnim) then + local startR, startG, startB, startA = anim.region:GetColor(); + startR, startG, startB, startA = startR or 1, startG or 1, startB or 1, startA or 1; + local ok, r, g, b, a = pcall(anim.colorFunc, progress, startR, startG, startB, startA, anim.colorR, anim.colorG, anim.colorB, anim.colorA); + if not ok then + errorHandler(r) + else + anim.region:ColorAnim(r, g, b, a); + end + end + WeakAuras.ActivateAuraEnvironment(nil); + if(finished) then + if not(anim.loop) then + if (anim.region.SetOffsetAnim) then + anim.region:SetOffsetAnim(0, 0); + else + if(anim.startX) then + anim.region:SetPoint(anim.selfPoint, anim.anchor, anim.anchorPoint, anim.startX, anim.startY); + end + end + if (anim.region.SetAnimAlpha) then + anim.region:SetAnimAlpha(nil); + elseif(anim.startAlpha) then + anim.region:SetAlpha(anim.startAlpha); + end + if(anim.startWidth) then + if(anim.region.Scale) then + anim.region:Scale(1, 1); + else + anim.region:SetWidth(anim.startWidth); + anim.region:SetHeight(anim.startHeight); + end + end + if(anim.startRotation) then + if(anim.region.Rotate) then + anim.region:Rotate(anim.startRotation); + end + end + if(anim.region.ColorAnim) then + anim.region:ColorAnim(nil); + end + animations[id] = nil; + end + + if(anim.loop) then + WeakAuras.Animate(anim.namespace, anim.data, anim.type, anim.anim, anim.region, anim.inverse, anim.onFinished, anim.loop, anim.cloneId); + elseif(anim.onFinished) then + anim.onFinished(); + end + end + WeakAuras.StopProfileAura(anim.name); + end + -- XXX: I tried to have animations only update if there are actually animation data to animate upon. + -- This caused all start animations to break, and I couldn't figure out why. + -- May revisit at a later time. + --[[ + if(num == 0) then + WeakAuras.debug("Animation stopped", 3); + frame:SetScript("OnUpdate", nil); + updatingAnimations = nil; + updatingAnimations = nil; + end + ]]-- + + WeakAuras.StopProfileSystem("animations"); +end + +function Private.RegisterGroupForPositioning(id, region) + pending_controls[id] = region + updatingAnimations = true + frame:SetScript("OnUpdate", UpdateAnimations) +end + +function WeakAuras.Animate(namespace, data, type, anim, region, inverse, onFinished, loop, cloneId) + local id = data.id; + local key = tostring(region); + local valid; + if(anim and anim.type == "custom" and (anim.use_translate or anim.use_alpha or (anim.use_scale and region.Scale) or (anim.use_rotate and region.Rotate) or (anim.use_color and region.Color))) then + valid = true; + elseif(anim and anim.type == "preset" and anim.preset and anim_presets[anim.preset]) then + anim = anim_presets[anim.preset]; + valid = true; + end + if(valid) then + local progress, duration, selfPoint, anchor, anchorPoint, startX, startY, startAlpha, startWidth, startHeight, startRotation, easeType, easeStrength; + local translateFunc, alphaFunc, scaleFunc, rotateFunc, colorFunc, easeFunc; + if(animations[key]) then + if(animations[key].type == type and not loop) then + return "no replace"; + end + anim.x = anim.x or 0; + anim.y = anim.y or 0; + selfPoint, anchor, anchorPoint, startX, startY = animations[key].selfPoint, animations[key].anchor, animations[key].anchorPoint, animations[key].startX, animations[key].startY; + anim.alpha = anim.alpha or 0; + startAlpha = animations[key].startAlpha; + anim.scalex = anim.scalex or 1; + anim.scaley = anim.scaley or 1; + startWidth, startHeight = animations[key].startWidth, animations[key].startHeight; + anim.rotate = anim.rotate or 0; + startRotation = animations[key].startRotation; + anim.colorR = anim.colorR or 1; + anim.colorG = anim.colorG or 1; + anim.colorB = anim.colorB or 1; + anim.colorA = anim.colorA or 1; + else + anim.x = anim.x or 0; + anim.y = anim.y or 0; + if not region.SetOffsetAnim then + selfPoint, anchor, anchorPoint, startX, startY = region:GetPoint(1); + end + anim.alpha = anim.alpha or 0; + startAlpha = region:GetAlpha(); + anim.scalex = anim.scalex or 1; + anim.scaley = anim.scaley or 1; + startWidth, startHeight = region:GetWidth(), region:GetHeight(); + anim.rotate = anim.rotate or 0; + startRotation = region.GetRotation and region:GetRotation() or 0; + anim.colorR = anim.colorR or 1; + anim.colorG = anim.colorG or 1; + anim.colorB = anim.colorB or 1; + anim.colorA = anim.colorA or 1; + end + + if(anim.use_translate) then + if not(anim.translateType == "custom" and anim.translateFunc) then + anim.translateType = anim.translateType or "straightTranslate"; + anim.translateFunc = anim_function_strings[anim.translateType] + end + if (anim.translateFunc) then + translateFunc = WeakAuras.LoadFunction("return " .. anim.translateFunc, id, "translate animation"); + else + if (region.SetOffsetAnim) then + region:SetOffsetAnim(0, 0); + else + region:SetPoint(selfPoint, anchor, anchorPoint, startX, startY); + end + end + else + if (region.SetOffsetAnim) then + region:SetOffsetAnim(0, 0); + else + region:SetPoint(selfPoint, anchor, anchorPoint, startX, startY); + end + end + if(anim.use_alpha) then + if not(anim.alphaType == "custom" and anim.alphaFunc) then + anim.alphaType = anim.alphaType or "straight"; + anim.alphaFunc = anim_function_strings[anim.alphaType] + end + if (anim.alphaFunc) then + alphaFunc = WeakAuras.LoadFunction("return " .. anim.alphaFunc, id, "alpha animation"); + else + if (region.SetAnimAlpha) then + region:SetAnimAlpha(nil); + else + region:SetAlpha(startAlpha); + end + end + else + if (region.SetAnimAlpha) then + region:SetAnimAlpha(nil); + else + region:SetAlpha(startAlpha); + end + end + if(anim.use_scale) then + if not(anim.scaleType == "custom" and anim.scaleFunc) then + anim.scaleType = anim.scaleType or "straightScale"; + anim.scaleFunc = anim_function_strings[anim.scaleType] + end + if (anim.scaleFunc) then + scaleFunc = WeakAuras.LoadFunction("return " .. anim.scaleFunc, id, "scale animation"); + else + region:Scale(1, 1); + end + elseif(region.Scale) then + region:Scale(1, 1); + end + if(anim.use_rotate) then + if not(anim.rotateType == "custom" and anim.rotateFunc) then + anim.rotateType = anim.rotateType or "straight"; + anim.rotateFunc = anim_function_strings[anim.rotateType] + end + if (anim.rotateFunc) then + rotateFunc = WeakAuras.LoadFunction("return " .. anim.rotateFunc, id, "rotate animation"); + else + region:Rotate(startRotation); + end + elseif(region.Rotate) then + region:Rotate(startRotation); + end + if(anim.use_color) then + if not(anim.colorType == "custom" and anim.colorFunc) then + anim.colorType = anim.colorType or "straightColor"; + anim.colorFunc = anim_function_strings[anim.colorType] + end + if (anim.colorFunc) then + colorFunc = WeakAuras.LoadFunction("return " .. anim.colorFunc, id, "color animation"); + else + region:ColorAnim(nil); + end + elseif(region.ColorAnim) then + region:ColorAnim(nil); + end + easeFunc = WeakAuras.anim_ease_functions[anim.easeType or "none"] + + duration = WeakAuras.ParseNumber(anim.duration) or 0; + progress = 0; + if(namespace == "display" and type == "main" and not onFinished and not anim.duration_type == "relative") then + local data = WeakAuras.GetData(id); + if(data and data.parent) then + local parentRegion = WeakAuras.regions[data.parent].region; + if(parentRegion and parentRegion.controlledRegions) then + for index, regionData in pairs(parentRegion.controlledRegions) do + local childRegion = regionData.region; + local childKey = regionData.key; + if(childKey and childKey ~= tostring(region) and animations[childKey] and animations[childKey].type == "main" and duration == animations[childKey].duration) then + progress = animations[childKey].progress; + break; + end + end + end + end + end + + local animation = animations[key] or {} + animations[key] = animation + + animation.progress = progress + animation.startX = startX + animation.startY = startY + animation.startAlpha = startAlpha + animation.startWidth = startWidth + animation.startHeight = startHeight + animation.startRotation = startRotation + animation.dX = (anim.use_translate and anim.x) + animation.dY = (anim.use_translate and anim.y) + animation.dAlpha = (anim.use_alpha and (anim.alpha - startAlpha)) + animation.scaleX = (anim.use_scale and anim.scalex) + animation.scaleY = (anim.use_scale and anim.scaley) + animation.rotate = anim.rotate + animation.colorR = (anim.use_color and anim.colorR) + animation.colorG = (anim.use_color and anim.colorG) + animation.colorB = (anim.use_color and anim.colorB) + animation.colorA = (anim.use_color and anim.colorA) + animation.translateFunc = translateFunc + animation.alphaFunc = alphaFunc + animation.scaleFunc = scaleFunc + animation.rotateFunc = rotateFunc + animation.colorFunc = colorFunc + animation.region = region + animation.selfPoint = selfPoint + animation.anchor = anchor + animation.anchorPoint = anchorPoint + animation.duration = duration + animation.duration_type = anim.duration_type or "seconds" + animation.inverse = inverse + animation.easeType = anim.easeType + animation.easeFunc = easeFunc + animation.easeStrength = anim.easeStrength + animation.type = type + animation.loop = loop + animation.onFinished = onFinished + animation.name = id + animation.cloneId = cloneId or "" + animation.namespace = namespace; + animation.data = data; + animation.anim = anim; + + if not(updatingAnimations) then + frame:SetScript("OnUpdate", UpdateAnimations); + updatingAnimations = true; + end + return true; + else + if(animations[key]) then + if(animations[key].type ~= type or loop) then + WeakAuras.CancelAnimation(region, true, true, true, true, true); + end + end + return false; + end +end + +function WeakAuras.IsAnimating(region) + local key = tostring(region); + local anim = animations[key]; + if(anim) then + return anim.type; + else + return nil; + end +end + +function WeakAuras.CancelAnimation(region, resetPos, resetAlpha, resetScale, resetRotation, resetColor, doOnFinished) + local key = tostring(region); + local anim = animations[key]; + + if(anim) then + if(resetPos) then + if (anim.region.SetOffsetAnim) then + anim.region:SetOffsetAnim(0, 0); + else + anim.region:ClearAllPoints(); + anim.region:SetPoint(anim.selfPoint, anim.anchor, anim.anchorPoint, anim.startX, anim.startY); + end + end + if(resetAlpha) then + if (anim.region.SetAnimAlpha) then + anim.region:SetAnimAlpha(nil); + else + anim.region:SetAlpha(anim.startAlpha); + end + end + if(resetScale) then + if(anim.region.Scale) then + anim.region:Scale(1, 1); + else + anim.region:SetWidth(anim.startWidth); + anim.region:SetHeight(anim.startHeight); + end + end + if(resetRotation and anim.region.Rotate) then + anim.region:Rotate(anim.startRotation); + end + if(resetColor and anim.region.ColorAnim) then + anim.region:ColorAnim(nil); + end + + animations[key] = nil; + if(doOnFinished and anim.onFinished) then + anim.onFinished(); + end + return true; + else + return false; + end +end + +function Private.RenameAnimations(oldid, newid) + for key, animation in pairs(animations) do + if animation.name == oldid then + animation.name = newid; + end + end +end diff --git a/WeakAuras/AuraEnvironment.lua b/WeakAuras/AuraEnvironment.lua index f2734c9..924f863 100644 --- a/WeakAuras/AuraEnvironment.lua +++ b/WeakAuras/AuraEnvironment.lua @@ -260,7 +260,7 @@ function WeakAuras.ActivateAuraEnvironment(id, cloneId, state, states, onlyConfi -- Finally, run the init function if supplied local actions = data.actions.init if(actions and actions.do_custom and actions.custom) then - local func = WeakAuras.customActionsFunctions[id]["init"] + local func = Private.customActionsFunctions[id]["init"] if func then xpcall(func, geterrorhandler()) end diff --git a/WeakAuras/BuffTrigger.lua b/WeakAuras/BuffTrigger.lua index 826941f..6617446 100644 --- a/WeakAuras/BuffTrigger.lua +++ b/WeakAuras/BuffTrigger.lua @@ -1,1879 +1,50 @@ --[[ BuffTrigger.lua -This file contains the "aura" trigger for buffs and debuffs. - -It registers the BuffTrigger table for the trigger type "aura" and has the following API: - -Add(data) -Adds an aura, setting up internal data structures for all buff triggers. - -LoadDisplays(id) -Loads the aura ids, enabling all buff triggers in the aura. - -UnloadDisplays(id) -Unloads the aura ids, disabling all buff triggers in the aura. - -UnloadAll() -Unloads all auras, disabling all buff triggers. - -ScanAll() -Updates all triggers by checking all triggers. - -Delete(id) -Removes all data for aura id. - -Rename(oldid, newid) -Updates all data for aura oldid to use newid. - -Modernize(data) -Updates all buff triggers in data. - -##################################################### -# Helper functions mainly for the WeakAuras Options # -##################################################### - -CanHaveDuration(data, triggernum) -Returns whether the trigger can have a duration. - -GetOverlayInfo(data, triggernum) -Returns a table containing all overlays. Currently there aren't any - -CanHaveAuto(data, triggernum) -Returns whether the icon can be automatically selected. - -CanHaveClones(data, triggernum) -Returns whether the trigger can have clones. - -CanHaveTooltip(data, triggernum) -Returns the type of tooltip to show for the trigger. - -GetNameAndIcon(data, triggernum) -Returns the name and icon to show in the options. - -GetAdditionalProperties(data, triggernum) -Returns the tooltip text for additional properties. - -GetTriggerConditions(data, triggernum) -Returns the potential conditions for a trigger +This used to contains the "aura" trigger for buffs and debuffs. Nowadays all functions do essentially nothing ]]-- if not WeakAuras.IsCorrectVersion() then return end local AddonName, Private = ... --- Lua APIs -local tinsert, wipe = table.insert, wipe -local pairs, next, type = pairs, next, type -local BUFF_MAX_DISPLAY = 255 -- Do tell when you find the real value. -local UnitGroupRolesAssigned = function() return "DAMAGER" end +local L = WeakAuras.L -local WeakAuras = WeakAuras; -local L = WeakAuras.L; -local BuffTrigger = {}; +local BuffTrigger = {} -local timer = WeakAuras.timer; -local function_strings = WeakAuras.function_strings; -local auras = WeakAuras.auras; -local specificBosses = WeakAuras.specificBosses; -local specificUnits = WeakAuras.specificUnits; -local loaded_auras = WeakAuras.loaded_auras; +function BuffTrigger.UnloadAll() end -WeakAuras.me = GetUnitName("player", true) -WeakAuras.myGUID = nil +function BuffTrigger.LoadDisplays(toLoad) end -local aura_cache = {}; -do - aura_cache.max = 0; - aura_cache.watched = {}; - aura_cache.players = {}; - aura_cache.TANK = 0; - aura_cache.HEALER = 0; - aura_cache.DAMAGER = 0; - aura_cache.playerRole = "NONE"; +function BuffTrigger.UnloadDisplays(toUnload) end - --- Tests if aura_cache data is consistent with trigger settings, eg. OwnOnly, RemainingTime, StackCount. - -- Extra check needed because aura_cache can potentially contain data of two different triggers with different settings! - -- @param acEntry - -- @param data - -- @return boolean - local function TestNonUniformSettings(acEntry, data) - if(data.remFunc) then - if not(data.remFunc(acEntry.expirationTime - GetTime())) then - return false - end - end +function BuffTrigger.FinishLoadUnload() end - -- Test OwnOnly - if ( - data.ownOnly == true and WeakAuras.myGUID ~= acEntry.casterGUID or - data.ownOnly == false and WeakAuras.myGUID == acEntry.casterGUID - ) then - return false; - end +function BuffTrigger.Delete(id) end - -- Test StackCount - if (data.count and not data.count(acEntry.count)) then - return false; - end +function BuffTrigger.Rename(oldid, newid) end - -- Success - return true; - end +function BuffTrigger.Add(data) end - function aura_cache.ForceUpdate() - if not(WeakAuras.IsPaused()) then - WeakAuras.ScanAurasGroup() - end - end - - function aura_cache.Watch(self, id, triggernum) - self.watched[id] = self.watched[id] or {}; - self.watched[id][triggernum] = self.watched[id][triggernum] or {}; - self.watched[id][triggernum].players = self.watched[id][triggernum].players or {}; - self:ForceUpdate() - end - - function aura_cache.Rename(self, oldid, newid) - self.watched[newid] = self.watched[oldid]; - self.watched[oldid] = nil; - end - - function aura_cache.Unwatch(self, id, triggernum) - self.watched[id][triggernum] = nil; - end - - function aura_cache.GetMaxNumber(self) - return self.max; - end - - function aura_cache.GetNumber(self, id, triggernum, data) - local num = 0; - for guid, _ in pairs(self.players) do - -- Need to check if cached data conforms to trigger - if(self.watched[id][triggernum].players[guid] and TestNonUniformSettings(self.watched[id][triggernum].players[guid], data)) then - num = num + 1; - end - end - return num; - end - - function aura_cache.GetDynamicInfo(self, id, triggernum, data) - local bestDuration, bestExpirationTime, bestName, bestIcon, bestCount, bestSpellId, bestUnitCaster = 0, math.huge, "", "", 0, 0, ""; - if(self.watched[id] and self.watched[id][triggernum]) then - for guid, durationInfo in pairs(self.watched[id][triggernum].players) do - -- Need to check if cached data conforms to trigger - if(durationInfo.expirationTime < bestExpirationTime and TestNonUniformSettings(durationInfo, data)) then - bestDuration = durationInfo.duration; - bestExpirationTime = durationInfo.expirationTime; - bestName = durationInfo.name; - bestIcon = durationInfo.icon; - bestCount = durationInfo.count; - bestSpellId = durationInfo.spellId; - bestUnitCaster = durationInfo.unitCaster; - end - end - end - return bestDuration, bestExpirationTime, bestName, bestIcon, bestCount, bestSpellId, bestUnitCaster; - end - - function aura_cache.GetPlayerDynamicInfo(self, id, triggernum, guid, data) - local bestDuration, bestExpirationTime, bestName, bestIcon, bestCount, bestSpellId, bestUnitCaster = 0, math.huge, "", "", 0, 0, ""; - if(self.watched[id] and self.watched[id][triggernum]) then - local durationInfo = self.watched[id][triggernum].players[guid] - if(durationInfo) then - -- Need to check if cached data conforms to trigger - if(durationInfo.expirationTime < bestExpirationTime and TestNonUniformSettings(durationInfo, data)) then - bestDuration = durationInfo.duration; - bestExpirationTime = durationInfo.expirationTime; - bestName = durationInfo.name; - bestIcon = durationInfo.icon; - bestCount = durationInfo.count; - bestSpellId = durationInfo.spellId; - bestUnitCaster = durationInfo.unitCaster; - end - end - end - return bestDuration, bestExpirationTime, bestName, bestIcon, bestCount, bestSpellId, bestUnitCaster; - end - - function aura_cache.GetAffected(self, id, triggernum, data) - local affected = {}; - if(self.watched[id] and self.watched[id][triggernum]) then - for guid, acEntry in pairs(self.watched[id][triggernum].players) do - -- Need to check if cached data conforms to trigger - if (TestNonUniformSettings(acEntry, data)) then - affected[self.players[guid]] = true; - end - end - end - return affected; - end - - function aura_cache.GetUnaffected(self, id, triggernum, data) - local affected = self:GetAffected(id, triggernum, data); - local ret = {}; - for guid, name in pairs(self.players) do - if not(affected[name]) then - ret[name] = true; - end - end - return ret; - end - - function aura_cache.AssertAura(self, id, triggernum, guid, duration, expirationTime, name, icon, count, casterGUID, spellId, unitCaster) - -- Don't watch aura on non watched players - if not self.players[guid] then return end - - if not(self.watched[id][triggernum].players[guid]) then - self.watched[id][triggernum].players[guid] = { - duration = duration, - expirationTime = expirationTime, - name = name, - icon = icon, - count = count, - unitCaster = unitCaster, - spellId = spellId, - casterGUID = casterGUID - }; - else - local auradata = self.watched[id][triggernum].players[guid]; - if(expirationTime ~= auradata.expirationTime) then - auradata.duration = duration; - auradata.expirationTime = expirationTime; - auradata.name = name; - auradata.icon = icon; - auradata.count = count; - auradata.unitCaster = unitCaster; - auradata.spellId = spellId; - auradata.casterGUID = casterGUID; - end - end - end - - function aura_cache.DeassertAura(self, id, triggernum, guid) - if(self.watched[id] and self.watched[id][triggernum] and self.watched[id][triggernum].players[guid]) then - self.watched[id][triggernum].players[guid] = nil; - end - end - - function aura_cache.AssertMember(self, guid, name) - if not(self.players[guid]) then - self.max = self.max + 1; - end - self.players[guid] = name; - end - - function aura_cache.DeassertMember(self, guid) - if(self.players[guid]) then - self.players[guid] = nil; - for id, v in pairs(self.watched) do - for triggernum, _ in pairs(v) do - self:DeassertAura(id, triggernum, guid); - end - end - self.max = self.max - 1; - end - end - - function aura_cache.AssertMemberList(self, guids) - local toDelete = {}; - - for guid, _ in pairs(self.players) do - if not(guids[guid]) then - toDelete[guid] = true; - end - end - - for guid, _ in pairs(toDelete) do - self:DeassertMember(guid); - end - for guid, name in pairs(guids) do - self:AssertMember(guid, name); - end - self:ForceUpdate(); - end -end -WeakAuras.aura_cache = aura_cache; - -function WeakAuras.SetAuraVisibility(id, triggernum, cloneId, buffShowOn, unitExists, active, unit, duration, expirationTime, name, icon, count, index, spellId, unitCaster) - local triggerState = WeakAuras.GetTriggerStateForTrigger(id, triggernum); - - local show; - if (not UnitExists(unit)) then - show = unitExists; - elseif (buffShowOn == "showAlways") then - show = true; - elseif(buffShowOn == "showOnMissing") then - show = not active; - else - show = active; - end - - cloneId = cloneId or ""; - - if (not show and not triggerState[cloneId]) then - return false; - end - - triggerState[cloneId] = triggerState[cloneId] or {}; - local state = triggerState[cloneId]; - if (state.show ~= show) then - state.show = show; - state.changed = true; - end - - if (state.show) then - if (state.active ~= active) then - state.active = active; - state.changed = true; - end - if (state.index ~= index) then - state.index = index; - state.changed = true; - end - - if (state.spellId ~= spellId) then - state.spellId = spellId; - state.changed = true; - end - - if (state.progressType ~= "timed") then - state.progressType = "timed"; - state.changed = true; - end - - if (state.expirationTime ~= expirationTime) then - state.expirationTime = expirationTime; - state.changed = true; - end - - if (state.duration ~= duration) then - state.duration = duration; - state.changed = true; - end - - local autoHide = false; - if (state.autoHide ~= autoHide) then - state.autoHide = autoHide; - state.changed = true; - end - - if (state.name ~= name) then - state.name = name; - state.changed = true; - end - - if (state.icon ~= icon) then - state.icon = icon; - state.changed = true; - end - - if (state.stacks ~= count) then - state.stacks = count; - state.changed = true; - end - - unitCaster = unitCaster and UnitName(unitCaster); - if (state.unitCaster ~= unitCaster) then - state.unitCaster = unitCaster; - state.changed = true; - end - - if (state.GUID ~= UnitGUID(unit)) then - state.GUID = UnitGUID(unit); - state.changed = true; - end - end - - if (state.changed) then - return true; - end - return false; -end - ---- Calls GetSpellInfo on trigger data to return aura name and icon. --- @param trigger --- @return name and icon -local function GetNameAndIconFromTrigger(trigger) - if (trigger.fullscan) then - if (trigger.spellId) then - local name, _, icon = GetSpellInfo(trigger.spellId); - return name, icon; - end - if (trigger.name) then - return trigger.name, WeakAuras.GetDynamicIconCache(trigger.name); - end - else - if (trigger.spellIds and trigger.spellIds[1]) then - local name, _, icon = GetSpellInfo(trigger.spellIds[1]); - return name, icon; - end - if (trigger.names and trigger.names[1]) then - return trigger.names[1], WeakAuras.GetDynamicIconCache(trigger.names[1]); - end - end -end - -local aura_scan_cache = {}; -local aura_lists = {}; -function WeakAuras.ScanAuras(unit) - local time = GetTime(); - - -- Reset scan cache for this unit - aura_scan_cache[unit] = aura_scan_cache[unit] or {}; - for i,v in pairs(aura_scan_cache[unit]) do - v.up_to_date = 0; - end - - -- Make unit available outside - local old_unit = WeakAuras.CurrentUnit; - WeakAuras.CurrentUnit = unit; - - local fixedUnit = unit == "party0" and "player" or unit - local uGUID = UnitGUID(fixedUnit) or fixedUnit; - - -- Link corresponding display (and aura cache) - local aura_object; - wipe(aura_lists); - if(unit:sub(1, 4) == "raid") then - if(aura_cache.players[uGUID]) then - aura_lists[1] = loaded_auras["group"]; - aura_object = aura_cache; - end - elseif(unit:sub(1, 5) == "party") then - aura_lists[1] = loaded_auras["group"]; - aura_object = aura_cache; - elseif(specificBosses[unit]) then - aura_lists[1] = loaded_auras["boss"]; - elseif(unit:sub(1,5) == "arena") then - aura_lists[1] = loaded_auras["arena"]; - else - if(unit == "player" and loaded_auras["group"]) then - WeakAuras.ScanAuras("party0"); - end - aura_lists[1] = loaded_auras[unit]; - end - - -- Add group auras for specific units -- XXX: why? - if(specificUnits[unit] and not aura_object) then - tinsert(aura_lists, loaded_auras["group"]); - end - - -- Locals - local cloneIdList; - local groupcloneToUpdate; - - -- Units GUID - unit = fixedUnit; - - -- Iterate over all displays (list of display lists) - for _, aura_list in pairs(aura_lists) do - -- Locals - local name, icon, count, duration, expirationTime, unitCaster, isStealable, spellId = true; - local tooltip, debuffClass, tooltipSize; - local remaining, checkPassed; - - -- Iterate over all displays (display lists) - for id,triggers in pairs(aura_list) do - WeakAuras.StartProfileAura(id); - -- Iterate over all triggers - local updateTriggerState = false; - for triggernum, data in pairs(triggers) do - if(not data.specificUnit or UnitIsUnit(data.unit, unit)) then - -- Filters - local filter = data.debuffType..(data.ownOnly and "|PLAYER" or ""); - local active = false; - - -- Full aura scan works differently - if(data.fullscan) then - -- Make sure scan cache exists - aura_scan_cache[unit][filter] = aura_scan_cache[unit][filter] or {up_to_date = 0}; - - -- Reset clone list - if cloneIdList then wipe(cloneIdList); end - if(data.autoclone and not cloneIdList) then - cloneIdList = {}; - end - - -- Iterate over all units auras - local index = 0; name = true; - while(name) do - -- Get nexted! - index = index + 1; - - -- Update scan cache - if(aura_scan_cache[unit][filter].up_to_date < index) then - -- Query aura data - name, _, icon, count, debuffClass, duration, expirationTime, unitCaster, isStealable, _, spellId = UnitAura(unit, index, filter); - if (debuffClass == nil) then - debuffClass = "none"; - elseif (debuffClass == "") then - debuffClass = "enrage" - else - debuffClass = string.lower(debuffClass); - end - local tooltipSize1, tooltipSize2, tooltipSize3; - tooltip, _, tooltipSize1, tooltipSize2, tooltipSize3 = WeakAuras.GetAuraTooltipInfo(unit, index, filter); - tooltipSize = {tooltipSize1, tooltipSize2, tooltipSize3} - aura_scan_cache[unit][filter][index] = aura_scan_cache[unit][filter][index] or {}; - - -- Save aura data to cache - local current_aura = aura_scan_cache[unit][filter][index]; - current_aura.name = name; - current_aura.icon = icon; - current_aura.count = count; - current_aura.duration = duration; - current_aura.expirationTime = expirationTime; - current_aura.isStealable = isStealable; - current_aura.spellId = spellId; - current_aura.tooltip = tooltip; - current_aura.debuffClass = debuffClass; - current_aura.tooltipSize = tooltipSize; - current_aura.unitCaster = unitCaster; - - -- Updated - aura_scan_cache[unit][filter].up_to_date = index; - - -- Use cached data instead - else - -- Fetch cached aura data - local current_aura = aura_scan_cache[unit][filter][index]; - name = current_aura.name; - icon = current_aura.icon; - count = current_aura.count; - duration = current_aura.duration; - expirationTime = current_aura.expirationTime; - isStealable = current_aura.isStealable; - spellId = current_aura.spellId; - tooltip = current_aura.tooltip; - debuffClass = current_aura.debuffClass; - tooltipSize = current_aura.tooltipSize; - if unitCaster ~= nil then - unitCaster = current_aura.unitCaster - else - unitCaster = "Unknown" - end - end - - local casGUID = unitCaster and UnitGUID(unitCaster); - - -- Aura conforms to trigger options? - if(data.subcount) then - local index = data.subcountCount or 1; - count = tooltipSize[index]; - end - if(name and ((not data.count) or count and data.count(count)) and (data.ownOnly ~= false or not UnitIsUnit("player", unitCaster or "")) and data.scanFunc(name, tooltip, isStealable, spellId, debuffClass)) then - remaining = expirationTime - time; - checkPassed = true; - if(data.remFunc) then - if not(data.remFunc(remaining)) then - checkPassed = false; - end - - -- Schedule remaining time, re-scan later - if(remaining > data.rem) then - WeakAuras.ScheduleAuraScan(unit, time + (remaining - data.rem)); - end - end - - if checkPassed then - -- Show display and handle clones - WeakAuras.SetDynamicIconCache(name, spellId, icon); - if(data.autoclone) then - local cloneId = name .. spellId .."-"..(casGUID or "unknown"); - if (WeakAuras.SetAuraVisibility(id, triggernum, cloneId, data.buffShowOn, data.unitExists, true, unit, duration, expirationTime, name, icon, count, index, spellId, unitCaster)) then - updateTriggerState = true; - end - active = true; - cloneIdList[cloneId] = true; - -- Simply show display (show) - else - if (WeakAuras.SetAuraVisibility(id, triggernum, nil, data.buffShowOn, data.unitExists, true, unit, duration, expirationTime, name, icon, count, index, spellId, unitCaster)) then - updateTriggerState = true; - end - active = true; - break; - end - end - end - end - - -- Update display visibility and clones visibility (hide) - if not(active) then - local nameFromTrigger, iconFromTrigger; - nameFromTrigger, iconFromTrigger = GetNameAndIconFromTrigger(data); - if (WeakAuras.SetAuraVisibility(id, triggernum, nil, data.buffShowOn, data.unitExists, nil, unit, 0, math.huge, nameFromTrigger, iconFromTrigger)) then - updateTriggerState = true; - end - end - if(data.autoclone) then - WeakAuras.SetAllStatesHiddenExcept(id, triggernum, cloneIdList); - updateTriggerState = true; - end - - -- Not using full aura scan - else - -- Reset clone list - if groupcloneToUpdate then wipe(groupcloneToUpdate); end - if(aura_object and data.groupclone and not data.specificUnit and not groupcloneToUpdate) then - groupcloneToUpdate = {}; - end - - -- Check all selected auras (for one trigger) - - local bestDuration, bestExpirationTime, bestName, bestIcon, bestCount, bestCasGUID, bestSpellId, bestUnitCaster; - - for index, checkname in pairs(data.names) do - -- Fetch aura data - local detected - for i = 1, BUFF_MAX_DISPLAY do - name, _, icon, count, _, duration, expirationTime, unitCaster, isStealable, _, spellId = UnitAura(unit, i, filter); - if not name then break end - if name == checkname then - detected = true - break - end - end - - if (detected) then - WeakAuras.SetDynamicIconCache(name, spellId, icon); - end - checkPassed = false; - - -- Aura conforms to trigger options? - if(name and ((not data.count) or data.count(count)) and (data.ownOnly ~= false or not UnitIsUnit("player", unitCaster or ""))) then - remaining = expirationTime - time; - checkPassed = true; - if(data.remFunc) then - if not(data.remFunc(remaining)) then - checkPassed = false; - end - - -- Schedule remaining time, re-scan later - if(remaining > data.rem) then - WeakAuras.ScheduleAuraScan(unit, time + (remaining - data.rem)); - end - end - end - - local casGUID = unitCaster and UnitGUID(unitCaster); - - -- Aura conforms to trigger - if(checkPassed) then - active = true; - - if (not bestExpirationTime or expirationTime > bestExpirationTime) then - bestDuration = duration; - bestExpirationTime = expirationTime; - bestName = name; - bestIcon = icon; - bestCount = count; - bestCasGUID = casGUID; - bestSpellId = spellId; - bestUnitCaster = unitCaster; - end - end - end - - local satisfies_ignoreSelf = not data.ignoreSelf or not UnitIsUnit(unit, "player") - if not satisfies_ignoreSelf then - active = false - end - - -- Update aura cache (and clones) - if (active) then - if(aura_object and not data.specificUnit) then - aura_object:AssertAura(id, triggernum, uGUID, bestDuration, bestExpirationTime, bestName, bestIcon, bestCount, bestCasGUID, bestSpellId, bestUnitCaster); - if(data.groupclone) then - groupcloneToUpdate[uGUID] = GetUnitName(unit, true); - end - else - if (WeakAuras.SetAuraVisibility(id, triggernum, nil, data.buffShowOn, data.unitExists, true, unit, bestDuration, bestExpirationTime, bestName, bestIcon, bestCount, nil, bestSpellId, bestUnitCaster)) then - updateTriggerState = true; - end - end - else - if(aura_object and not data.specificUnit) then - -- Update aura cache (and clones) - aura_object:DeassertAura(id, triggernum, uGUID); - if(data.groupclone) then - groupcloneToUpdate[uGUID] = GetUnitName(unit, true); - end - else - local nameFromTrigger, iconFromTrigger; - nameFromTrigger, iconFromTrigger = GetNameAndIconFromTrigger(data); - if (WeakAuras.SetAuraVisibility(id, triggernum, nil, data.buffShowOn, data.unitExists, nil, unit, 0, math.huge, nameFromTrigger, iconFromTrigger)) then - updateTriggerState = true; - end - end - end - - -- Processing a unit=group related unit - if(aura_object and not data.specificUnit) then - -- unit=group require valid count function - if(data.group_count) then - -- Query count from aura cache - local aura_count = aura_object:GetNumber(id, triggernum, data) - local max = aura_object:GetMaxNumber(); - if (data.ignoreSelf) then - max = max - 1; - end - local satisfies_count = data.group_count(aura_count, max); - - if(data.hideAlone and not IsInGroup()) then - satisfies_count = false; - end - - -- Satisfying count condition - if(satisfies_count) then - -- Update clones (show) - if(data.groupclone) then - for guid, playerName in pairs(aura_cache.players) do - local duration, expirationTime, name, icon, count, spellId, unitCaster = aura_object:GetPlayerDynamicInfo(id, triggernum, guid, data); - if(name ~= "") then - if (WeakAuras.SetAuraVisibility(id, triggernum, playerName, data.buffShowOn, data.unitExists, true, unit, duration, expirationTime, playerName, icon, count, nil, spellId, unitCaster)) then - updateTriggerState = true; - end - else - if (WeakAuras.SetAuraVisibility(id, triggernum, playerName, data.buffShowOn, data.unitExists, nil, unit, duration, expirationTime, playerName, icon, count, nil, spellId, unitCaster)) then - updateTriggerState = true; - end - end - end - - -- Update display information - else - -- Get display related information - local duration, expirationTime, name, icon, count, spellId, unitCaster = aura_object:GetDynamicInfo(id, triggernum, data); - - -- Process affected players - if(data.name_info == "players") then - local affected = aura_object:GetAffected(id, triggernum, data); - local num = 0; - name = ""; - for affected_name, _ in pairs(affected) do - local space = affected_name:find(" "); - name = name..(space and affected_name:sub(1, space - 1).."*" or affected_name)..", "; - num = num + 1; - end - if(num == 0) then - name = WeakAuras.L["None"]; - else - name = name:sub(1, -3); - end - -- Process unaffected players - elseif(data.name_info == "nonplayers") then - local unaffected = aura_object:GetUnaffected(id, triggernum, data); - local num = 0; - name = ""; - for unaffected_name, _ in pairs(unaffected) do - local space = unaffected_name:find(" "); - name = name..(space and unaffected_name:sub(1, space - 1).."*" or unaffected_name)..", "; - num = num + 1; - end - if(num == 0) then - name = WeakAuras.L["None"]; - else - name = name:sub(1, -3); - end - end - - -- Process stacks/aura count - if(data.stack_info == "count") then - count = aura_count; - end - - -- Update display visibility (show) - if (WeakAuras.SetAuraVisibility(id, triggernum, nil, data.buffShowOn, data.unitExists, true, unit, duration, expirationTime, name, icon, count, nil, spellId, unitCaster)) then - updateTriggerState = true; - end - end - - -- Not satisfying count - else - -- Update clones - if(data.groupclone) then - WeakAuras.SetAllStatesHidden(id, triggernum); - updateTriggerState = true; - -- Update display visibility (hide) - else - local nameFromTrigger, iconFromTrigger; - nameFromTrigger, iconFromTrigger = GetNameAndIconFromTrigger(data); - if (WeakAuras.SetAuraVisibility(id, triggernum, nil, data.buffShowOn, data.unitExists, nil, unit, 0, math.huge, nameFromTrigger, iconFromTrigger)) then - updateTriggerState = true; - end - end - end - end - end - end - end - end - if (updateTriggerState) then - WeakAuras.UpdatedTriggerState(id); - end - WeakAuras.StopProfileAura(id); - end - end - - -- Update current unit once again - WeakAuras.CurrentUnit = old_unit; -end - -function WeakAuras.ScanAurasGroup() - if IsInRaid() then - for i=1, GetNumGroupMembers() do - WeakAuras.ScanAuras(WeakAuras.raidUnits[i]) - end - elseif IsInGroup() then - for i=1, GetNumSubgroupMembers() do - WeakAuras.ScanAuras(WeakAuras.partyUnits[i]) - end - WeakAuras.ScanAuras("player") - else - WeakAuras.ScanAuras("player") - end -end - -local function GroupRosterUpdate(event) - WeakAuras.StartProfileSystem("bufftrigger"); - aura_cache.TANK = 0; - aura_cache.HEALER = 0; - aura_cache.DAMAGER = 0; - aura_cache.playerRole = "NONE"; - local recheck = false; - local groupMembers,playerName,uid,guid, role = {}; - if IsInRaid() then - for i=1, GetNumGroupMembers() do - uid = WeakAuras.raidUnits[i]; - role = UnitGroupRolesAssigned(uid) - if aura_cache[role] then - aura_cache[role] = aura_cache[role] + 1; - end - playerName = GetUnitName(uid,true); - if playerName then - playerName = playerName:gsub("-", " - "); - else - recheck = true; - end - if (playerName == UNKNOWNOBJECT) then - recheck = true; - end - guid = UnitGUID(uid); - if (guid) then - groupMembers[guid] = playerName; - end - end - role = UnitGroupRolesAssigned("player") - aura_cache.playerRole = role; - elseif IsInGroup() then - for i=1, GetNumSubgroupMembers() do - uid = WeakAuras.partyUnits[i]; - role = UnitGroupRolesAssigned(uid) - if (aura_cache[role]) then - aura_cache[role] = aura_cache[role] + 1; - end - guid = UnitGUID(uid); - local playerName = GetUnitName(uid,true); - if (playerName == UNKNOWNOBJECT) then - recheck = true; - end - if (guid) then - groupMembers[guid] = playerName; - end - end - role = UnitGroupRolesAssigned("player") - if (aura_cache[role]) then - aura_cache[role] = aura_cache[role] + 1; - aura_cache.playerRole = role; - end - end - - if (not WeakAuras.myGUID) then - WeakAuras.myGUID = UnitGUID("player") - end - groupMembers[WeakAuras.myGUID] = WeakAuras.me; - aura_cache:AssertMemberList(groupMembers); - if (recheck) then - timer:ScheduleTimer(GroupRosterUpdate, 0.5); - end - WeakAuras.StopProfileSystem("bufftrigger"); -end - -local groupFrame = CreateFrame("FRAME"); -WeakAuras.frames["Group Makeup Handler"] = groupFrame; -groupFrame:RegisterEvent("PARTY_MEMBERS_CHANGED"); -groupFrame:RegisterEvent("RAID_ROSTER_UPDATE"); -groupFrame:RegisterEvent("PLAYER_ENTERING_WORLD"); -groupFrame:SetScript("OnEvent", function(self, event) - GroupRosterUpdate(); -end); - -do - local pendingTracks = {}; - local UIDsfromGUID = {}; - local GUIDfromUID = {}; - - function WeakAuras.ReleaseUID(UID) - if(GUIDfromUID[UID]) then - if(UIDsfromGUID[GUIDfromUID[UID]] and UIDsfromGUID[GUIDfromUID[UID]][UID]) then - UIDsfromGUID[GUIDfromUID[UID]][UID] = nil; - else - -- If this code is reached, it means there was some kind of coordination error between the two lists - -- This shouldn't ever happen, but it is recoverable - -- Search through the whole UIDsfromGUID table and remove all instances of UID - for GUID,UIDs in pairs(UIDsfromGUID) do - for iUID,v in pairs(UIDs) do - if(iUID == UID or iUID == UID) then - UIDs[iUID] = nil; - end - end - end - end - end - GUIDfromUID[UID] = nil; - end - - function WeakAuras.SetUID(GUID, UID) - WeakAuras.ReleaseUID(UID); - if not(UIDsfromGUID[GUID]) then - UIDsfromGUID[GUID] = {}; - end - UIDsfromGUID[GUID][UID] = true; - GUIDfromUID[UID] = GUID; - end - - function WeakAuras.GetUID(GUID) - if not(UIDsfromGUID[GUID]) then - return nil; - end - -- iterate through key/value pairs from the table of UIDs that are registered for this GUID, until a *confirmed* match is found - -- confirming is necessary in case UIDs are not always released correctly (which may actually be close to impossible) - for returnUID,v in pairs(UIDsfromGUID[GUID]) do - -- check the validity of this entry - if(UnitGUID(returnUID) == GUID) then - return returnUID; - else - WeakAuras.ReleaseUID(returnUID); - end - end - return nil; - end - - --- Updates region data to see if states changed. - -- @param id - -- @param data - -- @param triggernum - -- @param GUID - -- @return boolean - local function updateRegion(id, data, triggernum, GUID) - local auradata = data.GUIDs[GUID]; - local triggerState = WeakAuras.GetTriggerStateForTrigger(id, triggernum); - triggerState[GUID] = triggerState[GUID] or {}; - local state = triggerState[GUID]; - if (state.progressType ~= "timed") then - state.progressType = "timed"; - state.changed = true; - end - - if(auradata and auradata.unitName) then - if (state.show ~= true) then - state.show = true; - state.changed = true; - end - - if (state.expirationTime ~= auradata.expirationTime) then - state.expirationTime = auradata.expirationTime; - state.changed = true; - end - - if (state.duration ~= auradata.duration) then - state.duration = auradata.duration; - state.changed = true; - end - - if (state.autoHide ~= true) then - state.autoHide = true; - state.changed = true; - end - - if (state.name ~= auradata.unitName) then - state.name = auradata.unitName; - state.changed = true; - end - - local icon = auradata.icon or WeakAuras.GetDynamicIconCache(auradata.name) or "Interface\\Icons\\INV_Misc_QuestionMark"; - if (state.icon ~= icon) then - state.icon = icon; - state.changed = true; - end - - if (state.stacks ~= auradata.count) then - state.stacks = auradata.count; - state.changed = true; - end - - if (state.unitCaster ~= auradata.unitCaster) then - state.unitCaster = auradata.unitCaster; - state.changed = true; - end - - if (state.GUID ~= GUID) then - state.GUID = GUID; - state.changed = true; - end - else - if (state.show ~= false) then - state.show = false; - state.changed = true; - end - end - - if (state.changed) then - return true; - end - return false; - end - - local function updateSpell(spellName, unit, destGUID) - if (not loaded_auras[spellName]) then return end; - for id, triggers in pairs(loaded_auras[spellName]) do - WeakAuras.StartProfileAura(id); - local updateTriggerState = false; - for triggernum, data in pairs(triggers) do - local filter = data.debuffType..(data.ownOnly and "|PLAYER" or ""); - local detected - local name, icon, count, duration, expirationTime, unitCaster, spellId, _ - for i = 1, BUFF_MAX_DISPLAY do - name, _, icon, count, _, duration, expirationTime, unitCaster, _, _, spellId = UnitAura(unit, i, filter); - if not name then break end - if name == spellName then - detected = true - break - end - end - - if(detected and (data.spellId == nil or data.spellId == spellId)) then - data.GUIDs = data.GUIDs or {}; - data.GUIDs[destGUID] = data.GUIDs[destGUID] or {}; - data.GUIDs[destGUID].name = spellName; - data.GUIDs[destGUID].unitName = GetUnitName(unit, true); - data.GUIDs[destGUID].duration = duration; - data.GUIDs[destGUID].expirationTime = expirationTime; - data.GUIDs[destGUID].icon = icon; - data.GUIDs[destGUID].count = count; - data.GUIDs[destGUID].unitCaster = unitCaster and UnitName(unitCaster); - data.GUIDs[destGUID].spellId = spellId; - updateTriggerState = updateRegion(id, data, triggernum, destGUID) or updateTriggerState; - end - end - if (updateTriggerState) then - WeakAuras.UpdatedTriggerState(id); - end - WeakAuras.StopProfileAura(id); - end - end - - local function combatLog(_, event, _, sourceName, _, destGUID, destName, _, spellId, spellName, _, auraType, amount) - if(loaded_auras[spellName]) then - if(message == "SPELL_AURA_APPLIED" or message == "SPELL_AURA_REFRESH" or message == "SPELL_AURA_APPLIED_DOSE" or message == "SPELL_AURA_REMOVED_DOSE") then - local unit = WeakAuras.GetUID(destGUID); - if(unit) then - updateSpell(spellName, unit, destGUID); - else - for id, triggers in pairs(loaded_auras[spellName]) do - WeakAuras.StartProfileAura(id); - local updateTriggerState = false; - for triggernum, data in pairs(triggers) do - if((not data.ownOnly) or UnitIsUnit(sourceName or "", "player")) then - pendingTracks[destGUID] = pendingTracks[destGUID] or {}; - pendingTracks[destGUID][spellName] = true; - - data.GUIDs = data.GUIDs or {}; - data.GUIDs[destGUID] = data.GUIDs[destGUID] or {}; - data.GUIDs[destGUID].name = spellName; - data.GUIDs[destGUID].unitName = destName; - local icon = spellId and select(3, GetSpellInfo(spellId)); - if (message == "SPELL_AURA_APPLIED_DOSE" or message == "SPELL_AURA_REMOVED_DOSE") then - -- Shouldn't affect duration/expirationTime nor icon - data.GUIDs[destGUID].duration = data.GUIDs[destGUID].duration or 0; - data.GUIDs[destGUID].expirationTime = data.GUIDs[destGUID].expirationTime or math.huge; - data.GUIDs[destGUID].icon = data.GUIDs[destGUID].icon or icon; - else - data.GUIDs[destGUID].duration = 0; - data.GUIDs[destGUID].expirationTime = math.huge; - data.GUIDs[destGUID].icon = icon; - end - data.GUIDs[destGUID].count = amount or 0; - data.GUIDs[destGUID].spellId = spellId; - data.GUIDs[destGUID].unitCaster = sourceName and UnitName(sourceName); - - updateTriggerState = updateRegion(id, data, triggernum, destGUID) or updateTriggerState; - end - end - if (updateTriggerState) then - WeakAuras.UpdatedTriggerState(id); - end - WeakAuras.StopProfileAura(id); - end - end - elseif(message == "SPELL_AURA_REMOVED") then - for id, triggers in pairs(loaded_auras[spellName]) do - local updateTriggerState = false; - for triggernum, data in pairs(triggers) do - if((not data.ownOnly) or UnitIsUnit(sourceName or "", "player")) then - -- WeakAuras.debug("Removed "..spellName.." from "..destGUID.." ("..(data.GUIDs and data.GUIDs[destGUID] and data.GUIDs[destGUID].unitName or "error")..") - "..(data.ownOnly and "own only" or "not own only")..", "..sourceName, 3); - data.GUIDs = data.GUIDs or {}; - data.GUIDs[destGUID] = nil; - - updateTriggerState = updateRegion(id, data, triggernum, destGUID) or updateTriggerState; - end - end - if (updateTriggerState) then - WeakAuras.UpdatedTriggerState(id); - end - end - end - end - end - - local function uidTrack(unit) - local GUID = UnitGUID(unit); - if(GUID) then - WeakAuras.SetUID(GUID, unit); - if(pendingTracks[GUID]) then - for spellName,_ in pairs(pendingTracks[GUID]) do - updateSpell(spellName, unit, GUID); - pendingTracks[GUID][spellName] = nil; - end - end - else - WeakAuras.ReleaseUID(unit); - end - unit = unit.."target"; - GUID = UnitGUID(unit); - if(GUID) then - WeakAuras.SetUID(GUID, unit); - if(pendingTracks[GUID]) then - for spellName,_ in pairs(pendingTracks[GUID]) do - updateSpell(spellName, unit, GUID); - pendingTracks[GUID][spellName] = nil; - end - end - else - WeakAuras.ReleaseUID(unit); - end - end - - local function checkExists() - WeakAuras.StartProfileSystem("bufftrigger - multi"); - for unit, auras in pairs(loaded_auras) do - if not(WeakAuras.unit_types[unit]) then - for id, triggers in pairs(auras) do - WeakAuras.StartProfileAura(id); - local updateTriggerState = false; - for triggernum, data in pairs(triggers) do - if(data.GUIDs) then - for GUID, GUIDData in pairs(data.GUIDs) do - if(GUIDData.expirationTime and GUIDData.expirationTime + 2 < GetTime()) then - data.GUIDs[GUID] = nil; - updateTriggerState = updateRegion(id, data, triggernum, GUID) or updateTriggerState; - end - end - end - end - if (updateTriggerState) then - WeakAuras.UpdatedTriggerState(id); - end - WeakAuras.StopProfileAura(id); - end - end - end - WeakAuras.StopProfileSystem("bufftrigger - multi"); - end - - local function handleEvent(frame, event, ...) - WeakAuras.StartProfileSystem("bufftrigger - multi"); - if(event == "COMBAT_LOG_EVENT_UNFILTERED") then - combatLog(...); - elseif(event == "UNIT_TARGET") then - uidTrack(...); - elseif(event == "PLAYER_FOCUS_CHANGED") then - uidTrack("focus"); - elseif(event == "NAME_PLATE_UNIT_ADDED") then - uidTrack(...); - elseif(event == "NAME_PLATE_UNIT_REMOVED") then - local unit = ... - WeakAuras.ReleaseUID(unit); - unit = unit.."target"; - WeakAuras.ReleaseUID(unit); - elseif(event == "UNIT_AURA") then - -- Note: Using UNIT_AURA in addition to COMBAT_LOG_EVENT_UNFILTERED, - -- because the combat log event does not contain duration information - local uid = ...; - if not uid then return end - local guid = UnitGUID(uid); - - for spellName, auras in pairs(loaded_auras) do - if not(WeakAuras.unit_types[spellName]) then - for id, triggers in pairs(auras) do - WeakAuras.StartProfileAura(id); - local updateTriggerState = false; - for triggernum, data in pairs(triggers) do - local filter = data.debuffType..(data.ownOnly and "|PLAYER" or ""); - - local detected - local name, icon, count, duration, expirationTime, unitCaster, _ - for i = 1, BUFF_MAX_DISPLAY do - name, _, icon, count, _, duration, expirationTime, unitCaster = UnitAura(uid, i, filter); - if not name then break end - if name == spellName then - detected = true - break - end - end - - if(detected) then - data.GUIDs = data.GUIDs or {}; - data.GUIDs[guid] = data.GUIDs[guid] or {}; - data.GUIDs[guid].name = spellName; - data.GUIDs[guid].unitName = GetUnitName(uid, true); - data.GUIDs[guid].duration = duration; - data.GUIDs[guid].expirationTime = expirationTime; - data.GUIDs[guid].icon = icon; - data.GUIDs[guid].count = count; - data.GUIDs[guid].unitCaster = unitCaster and UnitName(unitCaster); - updateTriggerState = updateRegion(id, data, triggernum, guid) or updateTriggerState; - end - end - if (updateTriggerState) then - WeakAuras.UpdatedTriggerState(id); - end - WeakAuras.StopProfileAura(id); - end - end - end - end - WeakAuras.StopProfileSystem("bufftrigger - multi"); - end - - local combatAuraFrame; - function WeakAuras.InitMultiAura() - if not(combatAuraFrame) then - combatAuraFrame = CreateFrame("frame"); - combatAuraFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED"); - combatAuraFrame:RegisterEvent("UNIT_TARGET"); - combatAuraFrame:RegisterEvent("UNIT_AURA"); - combatAuraFrame:RegisterEvent("PLAYER_FOCUS_CHANGED"); - combatAuraFrame:RegisterEvent("NAME_PLATE_UNIT_ADDED"); - combatAuraFrame:RegisterEvent("NAME_PLATE_UNIT_REMOVED"); - combatAuraFrame:RegisterEvent("PLAYER_LEAVING_WORLD"); - combatAuraFrame:SetScript("OnEvent", handleEvent); - WeakAuras.frames["Multi-target Aura Trigger Handler"] = combatAuraFrame; - timer:ScheduleRepeatingTimer(checkExists, 10) - end - end -end - -do - local scheduled_scans = {}; - - function WeakAuras.ScheduleAuraScan(unit, fireTime) - scheduled_scans[unit] = scheduled_scans[unit] or {}; - if not(scheduled_scans[unit][fireTime]) then - WeakAuras.debug("Scheduled aura scan for "..unit.." at "..fireTime); - local doScan = function() - WeakAuras.debug("Performing aura scan for "..unit.." at "..fireTime.." ("..GetTime()..")"); - scheduled_scans[unit][fireTime] = nil; - WeakAuras.ScanAuras(unit); - end - scheduled_scans[unit][fireTime] = timer:ScheduleTimerFixed(doScan, fireTime - GetTime() + 0.1); - end - end -end - ---- Adds auras to the loaded_auras table --- @param id --- @param triggernum --- @param data -local function LoadAura(id, triggernum, data) - local unit; - if(data.specificUnit) then - if(data.unit:lower():sub(1,4) == "boss") then - specificBosses[data.unit] = true; - unit = "boss"; - elseif(data.unit:lower():sub(1,5) == "arena") then - unit = "arena"; - else - specificUnits[data.unit] = true; - unit = "group"; - end - elseif(data.unit == "multi") then - unit = data.name - else - unit = data.unit; - end - if(unit) then - loaded_auras[unit] = loaded_auras[unit] or {}; - loaded_auras[unit][id] = loaded_auras[unit][id] or {}; - loaded_auras[unit][id][triggernum] = data; - end -end - -function BuffTrigger.ScanAll() - local unitIdstoScan = {}; - local groupScan = false; - for unit, auras in pairs(loaded_auras) do - if(unit == "group") then - groupScan = true; - elseif(WeakAuras.unit_types[unit]) then - unitIdstoScan[unit] = true; - end - end - - if (groupScan) then - WeakAuras.ScanAurasGroup(); - end - - for unit, _ in pairs(unitIdstoScan) do - WeakAuras.ScanAuras(unit); - end -end - -local aura_scan_cooldowns = {}; -local checkingScanCooldowns; -local scanCooldownFrame = CreateFrame("frame"); -WeakAuras.frames["Aura Scan Cooldown"] = scanCooldownFrame; - -local checkScanCooldownsFunc = function() - WeakAuras.StartProfileSystem("bufftrigger") - for unit,_ in pairs(aura_scan_cooldowns) do - aura_scan_cooldowns[unit] = nil; - WeakAuras.ScanAuras(unit); - end - checkingScanCooldowns = nil; - scanCooldownFrame:SetScript("OnUpdate", nil); - WeakAuras.StopProfileSystem("bufftrigger") -end - -local frame = CreateFrame("FRAME"); -WeakAuras.frames["WeakAuras Buff Frame"] = frame; -frame:RegisterEvent("PLAYER_ENTERING_WORLD"); -frame:RegisterEvent("PLAYER_FOCUS_CHANGED"); -frame:RegisterEvent("PLAYER_TARGET_CHANGED"); -frame:RegisterEvent("INSTANCE_ENCOUNTER_ENGAGE_UNIT"); -frame:RegisterEvent("UNIT_AURA"); -frame:RegisterEvent("UNIT_PET") -frame:SetScript("OnEvent", function (frame, event, arg1, arg2, ...) - if (WeakAuras.IsPaused()) then return end; - WeakAuras.StartProfileSystem("bufftrigger"); - if (event == "PLAYER_ENTERING_WORLD") then - BuffTrigger.ScanAll(); - elseif(event == "PLAYER_TARGET_CHANGED") then - WeakAuras.ScanAuras("target"); - elseif(event == "PLAYER_FOCUS_CHANGED") then - WeakAuras.ScanAuras("focus"); - elseif(event == "UNIT_PET" and arg1 == "player") then - WeakAuras.ScanAuras("pet"); - elseif(event == "INSTANCE_ENCOUNTER_ENGAGE_UNIT") then - for unit,_ in pairs(specificBosses) do - WeakAuras.ScanAuras(unit); - end - elseif(event == "UNIT_AURA") then - if not arg1 then return end - if( - loaded_auras[arg1] - or ( - loaded_auras["group"] - and ( - arg1:sub(1, 4) == "raid" - or arg1:sub(1, 5) == "party" - or arg1 == "player" - ) - ) - or ( - loaded_auras["boss"] - and arg1:sub(1,4) == "boss" - ) - or ( - loaded_auras["arena"] - and arg1:sub(1,5) == "arena" - ) - ) then - -- This throttles aura scans to only happen at most once per frame per unit - if not(aura_scan_cooldowns[arg1]) then - aura_scan_cooldowns[arg1] = true; - if not(checkingScanCooldowns) then - checkingScanCooldowns = true; - scanCooldownFrame:SetScript("OnUpdate", checkScanCooldownsFunc); - end - end - end - end - WeakAuras.StopProfileSystem("bufftrigger"); -end); - -function BuffTrigger.UnloadAll() - wipe(loaded_auras); -end - -function BuffTrigger.LoadDisplays(toLoad) - for id in pairs(toLoad) do - if(auras[id]) then - for triggernum, data in pairs(auras[id]) do - if(auras[id] and auras[id][triggernum]) then - LoadAura(id, triggernum, data); - end - end - end - end -end - -function BuffTrigger.UnloadDisplays(toUnload) - for id in pairs(toUnload) do - for unitname, auras in pairs(loaded_auras) do - auras[id] = nil; - end - end -end - -function BuffTrigger.FinishLoadUnload() - BuffTrigger.ScanAll(); -end - ---- Removes all data for an aura id --- @param id -function BuffTrigger.Delete(id) - auras[id] = nil; - for i,v in pairs(loaded_auras) do - v[id] = nil; - end -end - ---- Updates all data for aura oldid to use newid --- @param oldid --- @param newid -function BuffTrigger.Rename(oldid, newid) - auras[newid] = auras[oldid]; - auras[oldid] = nil; - - aura_cache:Rename(oldid, newid); - - for i,v in pairs(loaded_auras) do - v[newid] = v[oldid]; - v[newid] = nil; - end -end - ---- Adds an aura, setting up internal data structures for all buff triggers. --- @param data -function BuffTrigger.Add(data) - local id = data.id; - auras[id] = nil; - - for triggernum, triggerData in ipairs(data.triggers) do - local trigger, untrigger = triggerData.trigger, triggerData.untrigger - local triggerType; - if(type(trigger) == "table") then - triggerType = trigger.type; - if(triggerType == "aura") then - trigger.names = trigger.names or {}; - trigger.spellIds = trigger.spellIds or {} - trigger.unit = trigger.unit or "player"; - trigger.debuffType = trigger.debuffType or "HELPFUL"; - - local countFunc, countFuncStr; - if(trigger.useCount) then - countFuncStr = function_strings.count:format(trigger.countOperator or ">=", tonumber(trigger.count) or 0); - countFunc = WeakAuras.LoadFunction(countFuncStr); - end - - local remFunc, remFuncStr; - if(trigger.useRem) then - remFuncStr = function_strings.count:format(trigger.remOperator or ">=", tonumber(trigger.rem) or 0); - remFunc = WeakAuras.LoadFunction(remFuncStr); - end - - local group_countFunc, group_countFuncStr; - if(trigger.unit == "group") then - local count, countType = WeakAuras.ParseNumber(trigger.group_count); - if(trigger.group_countOperator and count and countType) then - if(countType == "whole") then - group_countFuncStr = function_strings.count:format(trigger.group_countOperator, count); - else - group_countFuncStr = function_strings.count_fraction:format(trigger.group_countOperator, count); - end - else - group_countFuncStr = function_strings.count:format(">", 0); - end - group_countFunc = WeakAuras.LoadFunction(group_countFuncStr); - WeakAuras.aura_cache:Watch(id, triggernum); - end - - local scanFunc; - if(trigger.fullscan) then - scanFunc = function(name, tooltip, isStealable, spellId, debuffClass) - if ( - ( - (not trigger.use_name) or ( - trigger.name and trigger.name ~= "" and ( - trigger.name_operator == "==" and name == trigger.name - or trigger.name_operator == "find('%s')" and name:find(trigger.name) - or trigger.name_operator == "match('%s')" and name:match(trigger.name) - ) - ) - ) - and ( - (not trigger.use_tooltip) or ( - trigger.tooltip and trigger.tooltip ~= "" and ( - trigger.tooltip_operator == "==" and tooltip == trigger.tooltip - or trigger.tooltip_operator == "find('%s')" and tooltip:find(trigger.tooltip) - or trigger.tooltip_operator == "match('%s')" and tooltip:match(trigger.tooltip) - ) - ) - ) - and ((not trigger.use_stealable) or isStealable) - and ((not trigger.use_spellId) or spellId == tonumber(trigger.spellId)) - and ((not trigger.use_debuffClass) or debuffClass == trigger.debuffClass) - ) then - return true; - else - return false; - end - end -- end scanFunc - end - - if(trigger.unit == "multi") then - WeakAuras.InitMultiAura(); - end - - if (trigger.buffShowOn == nil) then - trigger.buffShowOn = "showOnActive"; - end - - local buffShowOn = "showOnActive"; - local unitExists = true; - - if (not(trigger.unit ~= "group" and trigger.autoclone) and trigger.unit ~= "multi" and trigger.unit ~= "group" and trigger.unit ~= "player") then - unitExists = trigger.unitExists; - end - - if (trigger.type == "aura" and not(trigger.unit ~= "group" and trigger.autoclone) - and trigger.unit ~= "multi" and not(trigger.unit == "group" and not trigger.groupclone)) then - buffShowOn = trigger.buffShowOn; - end - - auras[id] = auras[id] or {}; - auras[id][triggernum] = { - count = countFunc, - remFunc = remFunc, - rem = tonumber(trigger.rem) or 0, - group_count = group_countFunc, - fullscan = trigger.fullscan, - autoclone = trigger.autoclone, - groupclone = trigger.groupclone, - subcount = trigger.subcount, - subcountCount = trigger.subcountCount, - scanFunc = scanFunc, - debuffType = trigger.debuffType, - names = trigger.names, - spellIds = trigger.spellIds, - name = trigger.name, - spellId = trigger.spellId, - unit = trigger.unit == "member" and trigger.specificUnit or trigger.unit, - specificUnit = trigger.unit == "member", - useCount = trigger.useCount, - ownOnly = trigger.ownOnly, - buffShowOn = buffShowOn, - unitExists = unitExists, - numAdditionalTriggers = max(#data.triggers - 1, 0), - hideAlone = trigger.hideAlone, - stack_info = trigger.stack_info, - name_info = trigger.name_info, - ignoreSelf = trigger.ignoreSelf - }; - end - end - end -end - ---- Updates old data to the new format. --- @param data -function BuffTrigger.Modernize(data) - for triggernum, triggerData in ipairs(data.triggers) do - local trigger = triggerData.trigger; - - if (data.internalVersion < 2) then - if (trigger and trigger.type == "aura") then - if (trigger.showOn == nil or trigger.showOn == "showOnCooldown" or trigger.showOn == "showOnReady" or trigger.showOn == "showAlways") then - trigger.showOn = trigger.inverse and "showOnMissing" or "showOnActive"; - trigger.inverse = nil; - end - end - end - - if data.internalVersion < 6 then - if trigger and trigger.type == "aura" then - if trigger.showOn == "showOnMissing" then - trigger.buffShowOn = "showOnMissing" - elseif trigger.showOn == "showActiveOrMissing" then - trigger.buffShowOn = "showAlways" - else - trigger.buffShowOn = "showOnActive" - end - trigger.showOn = nil - end - end - end -end - ---- Returns whether the first trigger could be shown without any affected group members. --- @param data --- @param triggernum --- @return boolean -local function CanGroupShowWithZero(data, triggernum) - local trigger = data.triggers[triggernum].trigger - local group_countFunc, group_countFuncStr; - if(trigger.unit == "group") then - local count, countType = WeakAuras.ParseNumber(trigger.group_count); - if(trigger.group_countOperator and count and countType) then - if(countType == "whole") then - group_countFuncStr = function_strings.count:format(trigger.group_countOperator, count); - else - group_countFuncStr = function_strings.count_fraction:format(trigger.group_countOperator, count); - end - else - group_countFuncStr = function_strings.count:format(">", 0); - end - group_countFunc = WeakAuras.LoadFunction(group_countFuncStr); - return group_countFunc(0, 1); - else - return false; - end -end - ---- Returns whether the trigger can have a duration. --- @param data --- @param triggernum function BuffTrigger.CanHaveDuration(data, triggernum) - local trigger = data.triggers[triggernum].trigger - if (trigger.type == "aura" and not(trigger.unit ~= "group" and trigger.autoclone) and trigger.unit ~= "multi" and not(trigger.unit == "group" and not trigger.groupclone)) then - if (trigger.buffShowOn ~= "showOnMissing") then - return "timed"; - else - return false; - end - end - return "timed"; -end - ---- Returns a table containing the names of all overlays --- @param data --- @param triggernum -function BuffTrigger.GetOverlayInfo(data, triggernum) - return {}; -end - ---- Returns whether the icon can be automatically selected. --- @param data --- @param triggernum --- @return boolean -function BuffTrigger.CanHaveAuto(data, triggernum) - return true; -end - ---- Returns whether the trigger can have clones. --- @param data --- @param triggernum --- @return -function BuffTrigger.CanHaveClones(data, triggernum) - local trigger = data.triggers[triggernum].trigger - return (trigger.fullscan and trigger.autoclone) - or (trigger.unit == "group" and trigger.groupclone) - or (trigger.unit == "multi"); -end - ----Returns the type of tooltip to show for the trigger. --- @param data --- @param triggernum --- @return string -function BuffTrigger.CanHaveTooltip(data, triggernum) - local trigger = data.triggers[triggernum].trigger - if(trigger.unit == "group" and trigger.name_info ~= "aura" and not trigger.groupclone) then - return "playerlist"; - elseif(trigger.fullscan and trigger.unit ~= "group") then - return "auraindex"; - else - return "aura"; - end -end - -function BuffTrigger.SetToolTip(trigger, state) - local data = auras[state.id][state.triggernum]; - if(trigger.unit == "group" and trigger.name_info ~= "aura" and not trigger.groupclone) then - local name = ""; - local playerList; - if(trigger.name_info == "players") then - playerList = WeakAuras.aura_cache:GetAffected(state.id, state.triggernum, data); - name = L["Affected"]..":"; - elseif(trigger.name_info == "nonplayers") then - playerList = WeakAuras.aura_cache:GetUnaffected(state.id, state.triggernum, data); - name = L["Missing"]..":"; - else - playerList = {}; - end - - local numPlayers = 0; - for playerName, _ in pairs(playerList) do - numPlayers = numPlayers + 1; - end - - if(numPlayers > 0) then - GameTooltip:AddLine(name); - local numRaid = IsInRaid() and GetNumGroupMembers() or 0; - local groupMembers,playersString = {}; - - if(numRaid > 0) then - local playerName, _, subgroup - for i = 1,numRaid do - -- Battleground-name, given by GetRaidRosterInfo (name-server) to GetUnitName(...) (name - server) transition - playerName, _, subgroup = GetRaidRosterInfo(i); - - if(playerName) then - playerName = playerName:gsub("-", " - ") - if (playerList[playerName]) then - groupMembers[subgroup] = groupMembers[subgroup] or {}; - groupMembers[subgroup][playerName] = true - end - end - end - for subgroup, players in pairs(groupMembers) do - playersString = L["Group %s"]:format(subgroup)..": "; - local _,space,class,classColor; - for playerName, _ in pairs(players) do - space = playerName:find(" "); - _, class = UnitClass((space and playerName:sub(1, space - 1) or playerName)); - classColor = WeakAuras.class_color_types[class]; - playersString = playersString..(classColor or "")..(space and playerName:sub(1, space - 1).."*" or playerName)..(classColor and "|r" or "")..(next(players, playerName) and ", " or ""); - end - GameTooltip:AddLine(playersString); - end - else - local num = 0; - playersString = ""; - local _,space,class,classColor; - for playerName, _ in pairs(playerList) do - space = playerName:find(" "); - _, class = UnitClass((space and playerName:sub(1, space - 1) or playerName)); - classColor = WeakAuras.class_color_types[class]; - playersString = playersString..(classColor or "")..(space and playerName:sub(1, space - 1).."*" or playerName)..(classColor and "|r" or "")..(next(playerList, playerName) and (", "..(num % 5 == 4 and "\n" or "")) or ""); - num = num + 1; - end - GameTooltip:AddLine(playersString); - end - else - GameTooltip:AddLine(name.." "..L["None"]); - end - return true - elseif(trigger.fullscan and trigger.unit ~= "group" and state.index) then - local unit = trigger.unit == "member" and trigger.specificUnit or trigger.unit; - if(trigger.debuffType == "HELPFUL") then - GameTooltip:SetUnitBuff(unit, state.index); - elseif(trigger.debuffType == "HARMFUL") then - GameTooltip:SetUnitDebuff(unit, state.index); - end - return true - else - if (state.spellId) then - GameTooltip:SetSpellByID(state.spellId); - return true - end - end return false end ---- Returns the name and icon to show in the options. --- @param data --- @param triggernum --- @return name and icon -function BuffTrigger.GetNameAndIcon(data, triggernum) - local _, name, icon - local trigger = data.triggers[triggernum].trigger - if (trigger.fullscan) then - if (trigger.spellId) then - name, _, icon = GetSpellInfo(trigger.spellId); - else - name = trigger.name; - icon = WeakAuras.spellCache.GetIcon(trigger.name); - end - else - if (trigger.spellIds and trigger.spellIds[1]) then - name, _, icon = GetSpellInfo(trigger.spellIds[1]) - elseif(not (trigger.buffShowOn == "showOnMissing" or CanGroupShowWithZero(data, triggernum)) and trigger.names) then - -- Try to get an icon from the icon cache - for index, checkname in pairs(trigger.names) do - local iconFromSpellCache = WeakAuras.spellCache.GetIcon(checkname); - if(iconFromSpellCache) then - name, icon = checkname, iconFromSpellCache; - break; - end - end - end - end +function BuffTrigger.GetOverlayInfo(data, triggernum) return {} end - return name, icon; -end +function BuffTrigger.CanHaveAuto(data, triggernum) return false end + +function BuffTrigger.CanHaveClones(data, triggernum) return false end + +function BuffTrigger.CanHaveTooltip(data, triggernum) end + +function BuffTrigger.SetToolTip(trigger, state) end + +function BuffTrigger.GetNameAndIcon(data, triggernum) end ---- Returns the tooltip text for additional properties. --- @param data --- @param triggernum --- @return string of additional properties function BuffTrigger.GetAdditionalProperties(data, triggernum) - local ret = "|cFFFF0000%".. triggernum .. ".spellId|r -" .. L["Spell ID"] .. "\n"; - ret = ret .. "|cFFFF0000%".. triggernum .. ".unitCaster|r -" .. L["Caster"] .. "\n"; - - return ret; + return "" end function BuffTrigger.GetTriggerConditions(data, triggernum) - local result = {}; - local trigger = data.triggers[triggernum].trigger - - result["unitCaster"] = { - display = L["Caster"], - type = "string", - } - - result["expirationTime"] = { - display = L["Remaining Duration"], - type = "timer", - } - result["duration"] = { - display = L["Total Duration"], - type = "number", - } - - result["stacks"] = { - display = L["Stacks"], - type = "number" - } - - result["name"] = { - display = L["Name"], - type = "string" - } - - if (trigger.type == "aura" and not(trigger.unit ~= "group" and trigger.fullscan and trigger.autoclone) and trigger.unit ~= "multi" and not(trigger.unit == "group" and not trigger.groupclone)) then - if (trigger.buffShowOn == "showAlways") then - result["buffed"] = { - display = L["Buffed/Debuffed"], - type = "bool", - test = function(state, needle) - return state and state.show and ((state.active and true or false) == (needle == 1)); - end - } - end - end - - return result; + return {} end function BuffTrigger.CreateFallbackState(data, triggernum, state) @@ -1882,37 +53,16 @@ function BuffTrigger.CreateFallbackState(data, triggernum, state) state.progressType = "timed"; state.duration = 0; state.expirationTime = math.huge; - local name, icon = GetNameAndIconFromTrigger(data, triggernum) - state.name = name - state.icon = icon end function BuffTrigger.GetName(triggerType) if (triggerType == "aura") then - return L["Legacy Aura"]; + return L["Legacy Aura (disabled)"]; end end function BuffTrigger.GetTriggerDescription(data, triggernum, namestable) - local trigger = data.triggers[triggernum].trigger; - if(trigger.fullscan) then - tinsert(namestable, {L["Aura:"], L["Full Scan"]}); - else - for index, name in pairs(trigger.names) do - local left = " "; - if(index == 1) then - if(#trigger.names > 0) then - if(#trigger.names > 1) then - left = L["Auras:"]; - else - left = L["Aura:"]; - end - end - end - local icon = WeakAuras.spellCache.GetIcon(name) or "Interface\\Icons\\INV_Misc_QuestionMark"; - tinsert(namestable, {left, name, icon}); - end - end + tinsert(namestable, {L["Legacy Aura (disabled):"], L[""]}); end function BuffTrigger.CreateFakeStates(id, triggernum) @@ -1920,20 +70,7 @@ function BuffTrigger.CreateFakeStates(id, triggernum) local data = WeakAuras.GetData(id) local state = {} BuffTrigger.CreateFallbackState(data, triggernum, state) - state.expirationTime = GetTime() + 60 - state.duration = 65 - state.progressType = "timed" allStates[""] = state - if BuffTrigger.CanHaveClones(data, triggernum) then - for i = 1, 2 do - local state = {} - BuffTrigger.CreateFallbackState(data, triggernum, state) - state.expirationTime = GetTime() + 60 + i * 20 - state.duration = 100 - state.progressType = "timed" - allStates[i] = state - end - end end WeakAuras.RegisterTriggerSystem({"aura"}, BuffTrigger); diff --git a/WeakAuras/BuffTrigger2.lua b/WeakAuras/BuffTrigger2.lua index 09b5189..9cee2cd 100644 --- a/WeakAuras/BuffTrigger2.lua +++ b/WeakAuras/BuffTrigger2.lua @@ -2215,12 +2215,6 @@ function BuffTrigger.Add(data) end end ---- Updates old data to the new format. --- @param data -function BuffTrigger.Modernize(data) - -- Does nothing yet! -end - --- Returns whether the trigger can have a duration. -- @param data -- @param triggernum @@ -2667,7 +2661,7 @@ local function TrackUid(unit) unit = unit.."target" GUID = UnitGUID(unit) if GUID then - WeakAuras.SetUID(GUID, unit) + SetUID(GUID, unit) BuffTrigger.HandlePendingTracks(unit, GUID) else ReleaseUID(unit) diff --git a/WeakAuras/Conditions.lua b/WeakAuras/Conditions.lua new file mode 100644 index 0000000..d49b19e --- /dev/null +++ b/WeakAuras/Conditions.lua @@ -0,0 +1,770 @@ +if not WeakAuras.IsCorrectVersion() then return end +local AddonName, Private = ... + +local L = WeakAuras.L +local timer = WeakAuras.timer + +-- Dynamic Condition functions to run. keyed on event and id +local dynamicConditions = {}; + +-- Global Dynamic Condition Funcs, keyed on the event +local globalDynamicConditionFuncs = {}; + +-- Check Conditions Functions, keyed on id +local checkConditions = {}; + +local clones = WeakAuras.clones; + +local function formatValueForAssignment(vType, value, pathToCustomFunction, pathToFormatters) + if (value == nil) then + value = false; + end + if (vType == "bool") then + return value and tostring(value) or "false"; + elseif(vType == "number") then + return value and tostring(value) or "0"; + elseif (vType == "list") then + return type(value) == "string" and string.format("%q", value) or "nil"; + elseif(vType == "color") then + if (value and type(value) == "table") then + return string.format("{%s, %s, %s, %s}", tostring(value[1]), tostring(value[2]), tostring(value[3]), tostring(value[4])); + end + return "{1, 1, 1, 1}"; + elseif(vType == "chat") then + if (value and type(value) == "table") then + local serialized = string.format("{message_type = %q, message = %q, message_dest = %q, message_channel = %q, message_custom = %s, message_formaters = %s}", + tostring(value.message_type), tostring(value.message or ""), + tostring(value.message_dest), tostring(value.message_channel), + pathToCustomFunction, + pathToFormatters) + return serialized + end + elseif(vType == "sound") then + if (value and type(value) == "table") then + return string.format("{ sound = %q, sound_channel = %q, sound_path = %q, sound_kit_id = %q, sound_type = %q, %s}", + tostring(value.sound or ""), tostring(value.sound_channel or ""), tostring(value.sound_path or ""), + tostring(value.sound_kit_id or ""), tostring(value.sound_type or ""), + value.sound_repeat and "sound_repeat = " .. tostring(value.sound_repeat) or "nil"); + end + elseif(vType == "customcode") then + return string.format("%s", pathToCustomFunction); + elseif vType == "glowexternal" then + if (value and type(value) == "table") then + return ([[{ glow_action = %q, glow_frame_type = %q, glow_type = %q, + glow_frame = %q, use_glow_color = %s, glow_color = {%s, %s, %s, %s}, + glow_lines = %d, glow_frequency = %f, glow_length = %f, glow_thickness = %f, glow_XOffset = %f, glow_YOffset = %f, + glow_scale = %f, glow_border = %s }]]):format( + value.glow_action or "", + value.glow_frame_type or "", + value.glow_type or "", + value.glow_frame or "", + value.use_glow_color and "true" or "false", + type(value.glow_color) == "table" and tostring(value.glow_color[1]) or "1", + type(value.glow_color) == "table" and tostring(value.glow_color[2]) or "1", + type(value.glow_color) == "table" and tostring(value.glow_color[3]) or "1", + type(value.glow_color) == "table" and tostring(value.glow_color[4]) or "1", + value.glow_lines or 8, + value.glow_frequency or 0.25, + value.glow_length or 10, + value.glow_thickness or 1, + value.glow_XOffset or 0, + value.glow_YOffset or 0, + value.glow_scale or 1, + value.glow_border and "true" or "false" + ) + end + end + return "nil"; +end + +local function formatValueForCall(type, property) + if (type == "bool" or type == "number" or type == "list") then + return "propertyChanges['" .. property .. "']"; + elseif (type == "color") then + local pcp = "propertyChanges['" .. property .. "']"; + return pcp .. "[1], " .. pcp .. "[2], " .. pcp .. "[3], " .. pcp .. "[4]"; + end + return "nil"; +end + +local conditionChecksTimers = {}; +conditionChecksTimers.recheckTime = {}; +conditionChecksTimers.recheckHandle = {}; + +function WeakAuras.scheduleConditionCheck(time, id, cloneId) + conditionChecksTimers.recheckTime[id] = conditionChecksTimers.recheckTime[id] or {} + conditionChecksTimers.recheckHandle[id] = conditionChecksTimers.recheckHandle[id] or {}; + + if (conditionChecksTimers.recheckTime[id][cloneId] and conditionChecksTimers.recheckTime[id][cloneId] > time) then + timer:CancelTimer(conditionChecksTimers.recheckHandle[id][cloneId]); + conditionChecksTimers.recheckHandle[id][cloneId] = nil; + end + + if (conditionChecksTimers.recheckHandle[id][cloneId] == nil) then + conditionChecksTimers.recheckHandle[id][cloneId] = timer:ScheduleTimerFixed(function() + conditionChecksTimers.recheckHandle[id][cloneId] = nil; + local region; + if(cloneId and cloneId ~= "") then + region = clones[id] and clones[id][cloneId]; + else + region = WeakAuras.regions[id].region; + end + if (region and region.toShow) then + checkConditions[id](region); + end + end, time - GetTime()) + conditionChecksTimers.recheckTime[id][cloneId] = time; + end +end + +function WeakAuras.CallCustomConditionTest(uid, testFunctionNumber, ...) + local ok, result = pcall(WeakAuras.conditionHelpers[uid].customTestFunctions[testFunctionNumber], ...) + if not ok then + geterrorhandler()(result) + elseif (ok) then + return result + end +end + +local function CreateTestForCondition(uid, input, allConditionsTemplate, usedStates) + local trigger = input and input.trigger; + local variable = input and input.variable; + local op = input and input.op; + local value = input and input.value; + + local check = nil; + local recheckCode = nil; + + if (variable == "AND" or variable == "OR") then + local test = {}; + if (input.checks) then + for i, subcheck in ipairs(input.checks) do + local subtest, subrecheckCode = CreateTestForCondition(uid, subcheck, allConditionsTemplate, usedStates); + if (subtest) then + tinsert(test, "(" .. subtest .. ")"); + end + if (subrecheckCode) then + recheckCode = recheckCode or ""; + recheckCode = recheckCode .. subrecheckCode; + end + end + end + if (next(test)) then + if (variable == "AND") then + check = table.concat(test, " and "); + else + check = table.concat(test, " or "); + end + end + end + + if (trigger and variable and value) then + usedStates[trigger] = true; + + local conditionTemplate = allConditionsTemplate[trigger] and allConditionsTemplate[trigger][variable]; + local cType = conditionTemplate and conditionTemplate.type; + local test = conditionTemplate and conditionTemplate.test; + local preamble = conditionTemplate and conditionTemplate.preamble; + + local stateCheck = "state[" .. trigger .. "] and state[" .. trigger .. "].show and "; + local stateVariableCheck = "state[" .. trigger .. "]." .. variable .. "~= nil and "; + + local preambleString + + if preamble then + WeakAuras.conditionHelpers[uid] = WeakAuras.conditionHelpers[uid] or {} + WeakAuras.conditionHelpers[uid].preambles = WeakAuras.conditionHelpers[uid].preambles or {} + tinsert(WeakAuras.conditionHelpers[uid].preambles, preamble(value)); + local preambleNumber = #WeakAuras.conditionHelpers[uid].preambles + preambleString = string.format("WeakAuras.conditionHelpers[%q].preambles[%s]", uid, preambleNumber) + end + + if (test) then + if (value) then + WeakAuras.conditionHelpers[uid] = WeakAuras.conditionHelpers[uid] or {} + WeakAuras.conditionHelpers[uid].customTestFunctions = WeakAuras.conditionHelpers[uid].customTestFunctions or {} + tinsert(WeakAuras.conditionHelpers[uid].customTestFunctions, test); + local testFunctionNumber = #(WeakAuras.conditionHelpers[uid].customTestFunctions); + local valueString = type(value) == "string" and string.format("%q", value) or value; + local opString = type(op) == "string" and string.format("%q", op) or op; + check = string.format("state and WeakAuras.CallCustomConditionTest(%q, %s, state[%s], %s, %s, %s)", + uid, testFunctionNumber, trigger, valueString, (opString or "nil"), preambleString or "nil"); + end + elseif (cType == "customcheck") then + if value then + local customCheck = WeakAuras.LoadFunction("return " .. value, "custom check") + if customCheck then + WeakAuras.conditionHelpers[uid] = WeakAuras.conditionHelpers[uid] or {} + WeakAuras.conditionHelpers[uid].customTestFunctions = WeakAuras.conditionHelpers[uid].customTestFunctions or {} + tinsert(WeakAuras.conditionHelpers[uid].customTestFunctions, customCheck); + local testFunctionNumber = #(WeakAuras.conditionHelpers[uid].customTestFunctions); + + check = string.format("state and WeakAuras.CallCustomConditionTest(%q, %s, state)", + uid, testFunctionNumber, trigger); + end + end + elseif (cType == "number" and op) then + local v = tonumber(value) + if (v) then + check = stateCheck .. stateVariableCheck .. "state[" .. trigger .. "]." .. variable .. op .. v; + end + elseif (cType == "timer" and op) then + if (op == "==") then + check = stateCheck .. stateVariableCheck .. "abs(state[" .. trigger .. "]." ..variable .. "- now -" .. value .. ") < 0.05"; + else + check = stateCheck .. stateVariableCheck .. "state[" .. trigger .. "]." .. variable .. "- now" .. op .. value; + end + elseif (cType == "select" and op) then + if (tonumber(value)) then + check = stateCheck .. stateVariableCheck .. "state[" .. trigger .. "]." .. variable .. op .. tonumber(value); + else + check = stateCheck .. stateVariableCheck .. "state[" .. trigger .. "]." .. variable .. op .. "'" .. value .. "'"; + end + elseif (cType == "bool") then + local rightSide = value == 0 and "false" or "true"; + check = stateCheck .. stateVariableCheck .. "state[" .. trigger .. "]." .. variable .. "==" .. rightSide + elseif (cType == "string") then + if(op == "==") then + check = stateCheck .. stateVariableCheck .. "state[" .. trigger .. "]." .. variable .. " == [[" .. value .. "]]"; + elseif (op == "find('%s')") then + check = stateCheck .. stateVariableCheck .. "state[" .. trigger .. "]." .. variable .. ":find([[" .. value .. "]], 1, true)"; + elseif (op == "match('%s')") then + check = stateCheck .. stateVariableCheck .. "state[" .. trigger .. "]." .. variable .. ":match([[" .. value .. "]], 1, true)"; + end + end + + if (cType == "timer" and value) then + recheckCode = " nextTime = state[" .. trigger .. "] and state[" .. trigger .. "]." .. variable .. " and (state[" .. trigger .. "]." .. variable .. " -" .. value .. ")\n"; + recheckCode = recheckCode .. " if (nextTime and (not recheckTime or nextTime < recheckTime) and nextTime >= now) then\n" + recheckCode = recheckCode .. " recheckTime = nextTime\n"; + recheckCode = recheckCode .. " end\n" + end + end + + return check, recheckCode; +end + +local function CreateCheckCondition(uid, ret, condition, conditionNumber, allConditionsTemplate, debug) + local usedStates = {}; + local check, recheckCode = CreateTestForCondition(uid, condition.check, allConditionsTemplate, usedStates); + if (check) then + ret = ret .. " state = region.states\n" + ret = ret .. " if (" .. check .. ") then\n"; + ret = ret .. " newActiveConditions[" .. conditionNumber .. "] = true;\n"; + ret = ret .. " end\n"; + end + if (recheckCode) then + ret = ret .. recheckCode; + end + if (check or recheckCode) then + ret = ret .. "\n"; + end + return ret; +end + +local function ParseProperty(property) + local subIndex, prop = string.match(property, "^sub%.(%d*).(.*)") + if subIndex then + return tonumber(subIndex), prop + else + return nil, property + end +end + +local function GetBaseProperty(data, property, start) + if (not data) then + return nil; + end + + local subIndex, prop = ParseProperty(property) + if subIndex then + return GetBaseProperty(data.subRegions[subIndex], prop, start) + end + + start = start or 1; + local next = string.find(property, ".", start, true); + if (next) then + return GetBaseProperty(data[string.sub(property, start, next - 1)], property, next + 1); + end + + local key = string.sub(property, start); + return data[key] or data[tonumber(key)]; +end + +local function CreateDeactivateCondition(ret, condition, conditionNumber, data, properties, usedProperties, debug) + if (condition.changes) then + ret = ret .. " if (activatedConditions[".. conditionNumber .. "] and not newActiveConditions[" .. conditionNumber .. "]) then\n" + if (debug) then ret = ret .. " print('Deactivating condition " .. conditionNumber .. "' )\n"; end + for changeNum, change in ipairs(condition.changes) do + if (change.property) then + local propertyData = properties and properties[change.property] + if (propertyData and propertyData.type and propertyData.setter) then + usedProperties[change.property] = true; + ret = ret .. " propertyChanges['" .. change.property .. "'] = " .. formatValueForAssignment(propertyData.type, GetBaseProperty(data, change.property)) .. "\n"; + if (debug) then ret = ret .. " print('- " .. change.property .. " " ..formatValueForAssignment(propertyData.type, GetBaseProperty(data, change.property)) .. "')\n"; end + end + end + end + ret = ret .. " end\n" + end + + return ret; +end + +local function CreateActivateCondition(ret, id, condition, conditionNumber, properties, debug) + if (condition.changes) then + ret = ret .. " if (newActiveConditions[" .. conditionNumber .. "]) then\n" + ret = ret .. " if (not activatedConditions[".. conditionNumber .. "]) then\n" + if (debug) then ret = ret .. " print('Activating condition " .. conditionNumber .. "' )\n"; end + -- non active => active + for changeNum, change in ipairs(condition.changes) do + if (change.property) then + local propertyData = properties and properties[change.property] + if (propertyData and propertyData.type) then + if (propertyData.setter) then + ret = ret .. " propertyChanges['" .. change.property .. "'] = " .. formatValueForAssignment(propertyData.type, change.value) .. "\n"; + if (debug) then ret = ret .. " print('- " .. change.property .. " " .. formatValueForAssignment(propertyData.type, change.value) .. "')\n"; end + elseif (propertyData.action) then + local pathToCustomFunction = "nil"; + local pathToFormatter = "nil" + if (WeakAuras.customConditionsFunctions[id] + and WeakAuras.customConditionsFunctions[id][conditionNumber] + and WeakAuras.customConditionsFunctions[id][conditionNumber].changes + and WeakAuras.customConditionsFunctions[id][conditionNumber].changes[changeNum]) then + pathToCustomFunction = string.format("WeakAuras.customConditionsFunctions[%q][%s].changes[%s]", id, conditionNumber, changeNum); + end + if WeakAuras.conditionTextFormatters[id] + and WeakAuras.conditionTextFormatters[id][conditionNumber] + and WeakAuras.conditionTextFormatters[id][conditionNumber].changes + and WeakAuras.conditionTextFormatters[id][conditionNumber].changes[changeNum] then + pathToFormatter = string.format("WeakAuras.conditionTextFormatters[%q][%s].changes[%s]", id, conditionNumber, changeNum); + end + ret = ret .. " region:" .. propertyData.action .. "(" .. formatValueForAssignment(propertyData.type, change.value, pathToCustomFunction, pathToFormatter) .. ")" .. "\n"; + if (debug) then ret = ret .. " print('# " .. propertyData.action .. "(" .. formatValueForAssignment(propertyData.type, change.value, pathToCustomFunction, pathToFormatter) .. "')\n"; end + end + end + end + end + ret = ret .. " else\n" + -- active => active, only override properties + for changeNum, change in ipairs(condition.changes) do + if (change.property) then + local propertyData = properties and properties[change.property] + if (propertyData and propertyData.type and propertyData.setter) then + ret = ret .. " if(propertyChanges['" .. change.property .. "'] ~= nil) then\n" + ret = ret .. " propertyChanges['" .. change.property .. "'] = " .. formatValueForAssignment(propertyData.type, change.value) .. "\n"; + if (debug) then ret = ret .. " print('- " .. change.property .. " " .. formatValueForAssignment(propertyData.type, change.value) .. "')\n"; end + ret = ret .. " end\n" + end + end + end + ret = ret .. " end\n" + ret = ret .. " end\n" + ret = ret .. "\n"; + ret = ret .. " activatedConditions[".. conditionNumber .. "] = newActiveConditions[" .. conditionNumber .. "]\n"; + end + + return ret; +end + +function WeakAuras.GetProperties(data) + local properties; + local propertiesFunction = WeakAuras.regionTypes[data.regionType] and WeakAuras.regionTypes[data.regionType].properties; + if (type(propertiesFunction) == "function") then + properties = propertiesFunction(data); + elseif propertiesFunction then + properties = CopyTable(propertiesFunction); + else + properties = {} + end + + if data.subRegions then + local subIndex = {} + for index, subRegion in ipairs(data.subRegions) do + local subRegionTypeData = WeakAuras.subRegionTypes[subRegion.type]; + local propertiesFunction = subRegionTypeData and subRegionTypeData.properties + local subProperties; + if (type(propertiesFunction) == "function") then + subProperties = propertiesFunction(data, subRegion); + elseif propertiesFunction then + subProperties = CopyTable(propertiesFunction) + end + + if subProperties then + for key, property in pairs(subProperties) do + subIndex[key] = subIndex[key] and subIndex[key] + 1 or 1 + property.display = { subIndex[key] .. ". " .. subRegionTypeData.displayName, property.display, property.defaultProperty } + properties["sub." .. index .. "." .. key ] = property; + end + end + end + end + + return properties; +end + +function Private.LoadConditionPropertyFunctions(data) + local id = data.id; + if (data.conditions) then + WeakAuras.customConditionsFunctions[id] = {}; + for conditionNumber, condition in ipairs(data.conditions) do + if (condition.changes) then + for changeIndex, change in ipairs(condition.changes) do + if ( (change.property == "chat" or change.property == "customcode") and type(change.value) == "table" and change.value.custom) then + local custom = change.value.custom; + local prefix, suffix; + if (change.property == "chat") then + prefix, suffix = "return ", ""; + else + prefix, suffix = "return function()", "\nend"; + end + local customFunc = WeakAuras.LoadFunction(prefix .. custom .. suffix, id, "condition"); + if (customFunc) then + WeakAuras.customConditionsFunctions[id][conditionNumber] = WeakAuras.customConditionsFunctions[id][conditionNumber] or {}; + WeakAuras.customConditionsFunctions[id][conditionNumber].changes = WeakAuras.customConditionsFunctions[id][conditionNumber].changes or {}; + WeakAuras.customConditionsFunctions[id][conditionNumber].changes[changeIndex] = customFunc; + end + end + if change.property == "chat" then + local getter = function(key, default) + local fullKey = "message_format_" .. key + if change.value[fullKey] == nil then + change.value[fullKey] = default + end + return change.value[fullKey] + end + local formatters = change.value and WeakAuras.CreateFormatters(change.value.message, getter) + WeakAuras.conditionTextFormatters[id] = WeakAuras.conditionTextFormatters[id] or {} + WeakAuras.conditionTextFormatters[id][conditionNumber] = WeakAuras.conditionTextFormatters[id][conditionNumber] or {}; + WeakAuras.conditionTextFormatters[id][conditionNumber].changes = WeakAuras.conditionTextFormatters[id][conditionNumber].changes or {}; + WeakAuras.conditionTextFormatters[id][conditionNumber].changes[changeIndex] = formatters; + end + end + end + end + end +end + +local globalConditions = +{ + ["incombat"] = { + display = L["In Combat"], + type = "bool", + events = {"PLAYER_REGEN_ENABLED", "PLAYER_REGEN_DISABLED"}, + globalStateUpdate = function(state) + state.incombat = UnitAffectingCombat("player") == 1 and true or false; + end + }, + ["hastarget"] = { + display = L["Has Target"], + type = "bool", + events = {"PLAYER_TARGET_CHANGED", "PLAYER_ENTERING_WORLD"}, + globalStateUpdate = function(state) + state.hastarget = UnitExists("target") == 1 and true or false; + end + }, + ["attackabletarget"] = { + display = L["Attackable Target"], + type = "bool", + events = {"PLAYER_TARGET_CHANGED", "UNIT_FACTION"}, + globalStateUpdate = function(state) + state.attackabletarget = UnitCanAttack("player", "target") == 1 and true or false; + end + }, + ["customcheck"] = { + display = L["Custom Check"], + type = "customcheck" + } +} + +function WeakAuras.GetGlobalConditions() + return globalConditions; +end + +local function ConstructConditionFunction(data) + local debug = false; + if (not data.conditions or #data.conditions == 0) then + return nil; + end + + local usedProperties = {}; + + local allConditionsTemplate = WeakAuras.GetTriggerConditions(data); + allConditionsTemplate[-1] = WeakAuras.GetGlobalConditions(); + + local ret = ""; + ret = ret .. "local newActiveConditions = {};\n" + ret = ret .. "local propertyChanges = {};\n" + ret = ret .. "local nextTime;\n" + ret = ret .. "return function(region, hideRegion)\n"; + if (debug) then ret = ret .. " print('check conditions for:', region.id, region.cloneId)\n"; end + ret = ret .. " local id = region.id\n"; + ret = ret .. " local cloneId = region.cloneId or ''\n"; + ret = ret .. " local activatedConditions = WeakAuras.GetActiveConditions(id, cloneId)\n"; + ret = ret .. " wipe(newActiveConditions)\n"; + ret = ret .. " local recheckTime;\n" + ret = ret .. " local now = GetTime();\n" + + local normalConditionCount = data.conditions and #data.conditions; + -- First Loop gather which conditions are active + ret = ret .. " if (not hideRegion) then\n" + if (data.conditions) then + WeakAuras.conditionHelpers[data.uid] = nil + for conditionNumber, condition in ipairs(data.conditions) do + ret = CreateCheckCondition(data.uid, ret, condition, conditionNumber, allConditionsTemplate, debug) + end + end + ret = ret .. " end\n"; + + ret = ret .. " if (recheckTime) then\n" + ret = ret .. " WeakAuras.scheduleConditionCheck(recheckTime, id, cloneId);\n" + ret = ret .. " end\n" + + local properties = WeakAuras.GetProperties(data); + + -- Now build a property + change list + -- Second Loop deals with conditions that are no longer active + ret = ret .. " wipe(propertyChanges)\n" + if (data.conditions) then + for conditionNumber, condition in ipairs(data.conditions) do + ret = CreateDeactivateCondition(ret, condition, conditionNumber, data, properties, usedProperties, debug) + end + end + ret = ret .. "\n"; + + -- Third Loop deals with conditions that are newly active + if (data.conditions) then + for conditionNumber, condition in ipairs(data.conditions) do + ret = CreateActivateCondition(ret, data.id, condition, conditionNumber, properties, debug) + end + end + + -- Last apply changes to region + for property, _ in pairs(usedProperties) do + ret = ret .. " if(propertyChanges['" .. property .. "'] ~= nil) then\n" + local arg1 = ""; + if (properties[property].arg1) then + if (type(properties[property].arg1) == "number") then + arg1 = tostring(properties[property].arg1) .. ", "; + else + arg1 = "'" .. properties[property].arg1 .. "', "; + end + end + + local base = "region:" + local subIndex = ParseProperty(property) + if subIndex then + base = "region.subRegions[" .. subIndex .. "]:" + end + + ret = ret .. " " .. base .. properties[property].setter .. "(" .. arg1 .. formatValueForCall(properties[property].type, property) .. ")\n"; + if (debug) then ret = ret .. " print('Calling " .. properties[property].setter .. " with', " .. arg1 .. formatValueForCall(properties[property].type, property) .. ")\n"; end + ret = ret .. " end\n"; + end + ret = ret .. "end\n"; + + return ret; +end + +function Private.LoadConditionFunction(data) + local checkConditionsFuncStr = ConstructConditionFunction(data); + local checkCondtionsFunc = checkConditionsFuncStr and WeakAuras.LoadFunction(checkConditionsFuncStr, data.id, "condition checks"); + + checkConditions[data.id] = checkCondtionsFunc; +end + +function Private.RunConditions(region, id, hideRegion) + -- TODO rename show + if (checkConditions[id]) then + checkConditions[id](region, hideRegion); + end +end + + +local dynamicConditionsFrame = nil; + +local globalConditionAllState = { + [""] = { + show = true; + } +}; + +local globalConditionState = globalConditionAllState[""]; + +function WeakAuras.GetGlobalConditionState() + return globalConditionAllState; +end + +local function runDynamicConditionFunctions(funcs) + for id in pairs(funcs) do + if (Private.IsAuraActive(id) and checkConditions[id]) then + local activeTriggerState = WeakAuras.GetTriggerStateForTrigger(id, Private.ActiveTrigger(id)); + for cloneId, state in pairs(activeTriggerState) do + local region = WeakAuras.GetRegion(id, cloneId); + checkConditions[id](region, false); + end + end + end +end + +local function handleDynamicConditions(self, event) + if (globalDynamicConditionFuncs[event]) then + for i, func in ipairs(globalDynamicConditionFuncs[event]) do + func(globalConditionState); + end + end + if (dynamicConditions[event]) then + runDynamicConditionFunctions(dynamicConditions[event]); + end +end + +local lastDynamicConditionsUpdateCheck; +local function handleDynamicConditionsOnUpdate(self) + handleDynamicConditions(self, "FRAME_UPDATE"); + if (not lastDynamicConditionsUpdateCheck or GetTime() - lastDynamicConditionsUpdateCheck > 0.2) then + lastDynamicConditionsUpdateCheck = GetTime(); + handleDynamicConditions(self, "WA_SPELL_RANGECHECK"); + end +end + +local registeredGlobalFunctions = {}; + +local function EvaluateCheckForRegisterForGlobalConditions(id, check, allConditionsTemplate, register) + local trigger = check and check.trigger; + local variable = check and check.variable; + + if (trigger == -2) then + if (check.checks) then + for _, subcheck in ipairs(check.checks) do + EvaluateCheckForRegisterForGlobalConditions(id, subcheck, allConditionsTemplate, register); + end + end + elseif trigger == -1 and variable == "customcheck" then + if check.op then + for event in string.gmatch(check.op, "[%w_]+") do + if (not dynamicConditions[event]) then + register[event] = true; + dynamicConditions[event] = {}; + end + dynamicConditions[event][id] = true; + end + end + elseif (trigger and variable) then + local conditionTemplate = allConditionsTemplate[trigger] and allConditionsTemplate[trigger][variable]; + if (conditionTemplate and conditionTemplate.events) then + for _, event in ipairs(conditionTemplate.events) do + if (not dynamicConditions[event]) then + register[event] = true; + dynamicConditions[event] = {}; + end + dynamicConditions[event][id] = true; + end + + if (conditionTemplate.globalStateUpdate and not registeredGlobalFunctions[variable]) then + registeredGlobalFunctions[variable] = true; + for _, event in ipairs(conditionTemplate.events) do + globalDynamicConditionFuncs[event] = globalDynamicConditionFuncs[event] or {}; + tinsert(globalDynamicConditionFuncs[event], conditionTemplate.globalStateUpdate); + end + conditionTemplate.globalStateUpdate(globalConditionState); + end + end + end +end + +function Private.RegisterForGlobalConditions(id) + local data = WeakAuras.GetData(id); + for event, conditionFunctions in pairs(dynamicConditions) do + conditionFunctions.id = nil; + end + + local register = {}; + if (data.conditions) then + local allConditionsTemplate = WeakAuras.GetTriggerConditions(data); + allConditionsTemplate[-1] = WeakAuras.GetGlobalConditions(); + + for conditionNumber, condition in ipairs(data.conditions) do + EvaluateCheckForRegisterForGlobalConditions(id, condition.check, allConditionsTemplate, register); + end + end + + if (next(register) and not dynamicConditionsFrame) then + dynamicConditionsFrame = CreateFrame("FRAME"); + dynamicConditionsFrame:SetScript("OnEvent", handleDynamicConditions); + WeakAuras.frames["Rerun Conditions Frame"] = dynamicConditionsFrame + end + + for event in pairs(register) do + if (event == "FRAME_UPDATE" or event == "WA_SPELL_RANGECHECK") then + if (not dynamicConditionsFrame.onUpdate) then + dynamicConditionsFrame:SetScript("OnUpdate", handleDynamicConditionsOnUpdate); + dynamicConditionsFrame.onUpdate = true; + end + else + pcall(dynamicConditionsFrame.RegisterEvent, dynamicConditionsFrame, event); + end + end +end + +function Private.UnregisterForGlobalConditions(id) + for event, condFuncs in pairs(dynamicConditions) do + condFuncs[id] = nil; + end +end + + +function Private.UnloadAllConditions() + for id in pairs(conditionChecksTimers.recheckTime) do + if (conditionChecksTimers.recheckHandle[id]) then + for _, v in pairs(conditionChecksTimers.recheckHandle[id]) do + timer:CancelTimer(v) + end + end + end + wipe(conditionChecksTimers.recheckTime) + wipe(conditionChecksTimers.recheckHandle) + + dynamicConditions = {} +end + +function Private.UnloadConditions(id) + conditionChecksTimers.recheckTime[id] = nil; + if (conditionChecksTimers.recheckHandle[id]) then + for _, v in pairs(conditionChecksTimers.recheckHandle[id]) do + timer:CancelTimer(v); + end + end + conditionChecksTimers.recheckHandle[id] = nil; + Private.UnregisterForGlobalConditions(id); +end + +function Private.DeleteConditions(id) + checkConditions[id] = nil + conditionChecksTimers.recheckTime[id] = nil + if (conditionChecksTimers.recheckHandle[id]) then + for cloneId, v in pairs(conditionChecksTimers.recheckHandle[id]) do + timer:CancelTimer(v) + end + end + conditionChecksTimers.recheckHandle[id] = nil + + for event, funcs in pairs(dynamicConditions) do + funcs[id] = nil + end +end + +function Private.RenameConditions(oldid, newid) + checkConditions[newid] = checkConditions[oldid]; + checkConditions[oldid] = nil; + + conditionChecksTimers.recheckTime[newid] = conditionChecksTimers.recheckTime[oldid]; + conditionChecksTimers.recheckTime[oldid] = nil; + + conditionChecksTimers.recheckHandle[newid] = conditionChecksTimers.recheckHandle[oldid]; + conditionChecksTimers.recheckHandle[oldid] = nil; + + for event, funcs in pairs(dynamicConditions) do + funcs[newid] = funcs[oldid] + funcs[oldid] = nil; + end +end diff --git a/WeakAuras/GenericTrigger.lua b/WeakAuras/GenericTrigger.lua index 413aae2..3f69152 100644 --- a/WeakAuras/GenericTrigger.lua +++ b/WeakAuras/GenericTrigger.lua @@ -76,12 +76,11 @@ local event_prototypes = WeakAuras.event_prototypes; local timer = WeakAuras.timer; local debug = WeakAuras.debug; -local events = WeakAuras.events; -local loaded_events = WeakAuras.loaded_events; +local events = {} +local loaded_events = {} local loaded_unit_events = {}; local loaded_auras = {}; -- id to bool map local timers = WeakAuras.timers; -local specificBosses = WeakAuras.specificBosses; -- Local functions local LoadEvent, HandleEvent, HandleUnitEvent, TestForTriState, TestForToggle, TestForLongString, TestForMultiSelect @@ -1128,7 +1127,6 @@ end function GenericTrigger.Add(data, region) local id = data.id; events[id] = nil; - WeakAuras.forceable_events[id] = {}; for triggernum, triggerData in ipairs(data.triggers) do local trigger, untrigger = triggerData.trigger, triggerData.untrigger @@ -1473,103 +1471,6 @@ local oldPowerTriggers = { ["Shards"] = 7, } -function GenericTrigger.Modernize(data) - for triggernum, triggerData in ipairs(data.triggers) do - local trigger, untrigger = triggerData.trigger, triggerData.untrigger - - if (data.internalVersion < 2) then - -- Convert any references to "COMBAT_LOG_EVENT_UNFILTERED_CUSTOM" to "COMBAT_LOG_EVENT_UNFILTERED" - if(trigger and trigger.custom) then - trigger.custom = trigger.custom:gsub("COMBAT_LOG_EVENT_UNFILTERED_CUSTOM", "COMBAT_LOG_EVENT_UNFILTERED"); - end - - if(untrigger and untrigger.custom) then - untrigger.custom = untrigger.custom:gsub("COMBAT_LOG_EVENT_UNFILTERED_CUSTOM", "COMBAT_LOG_EVENT_UNFILTERED"); - end - - if trigger and trigger["event"] and trigger["event"] == "DBM Timer" then - if (type(trigger.spellId) == "number") then - trigger.spellId = tostring(trigger.spellId); - end - end - - if trigger and trigger["event"] and trigger["event"] == "Item Set Equipped" then - trigger.event = "Equipment Set"; - end - - -- Convert ember trigger - local fixEmberTrigger = function(trigger) - if (trigger.power and not trigger.ember) then - trigger.ember = tostring(tonumber(trigger.power) * 10); - trigger.use_ember = trigger.use_power - trigger.ember_operator = trigger.power_operator; - trigger.power = nil; - trigger.use_power = nil; - trigger.power_operator = nil; - end - end - - if (trigger and trigger.type and trigger.event and trigger.type == "status" and trigger.event == "Burning Embers") then - fixEmberTrigger(trigger); - fixEmberTrigger(untrigger); - end - - if (trigger and trigger.type and trigger.event and trigger.type == "status" - and (trigger.event == "Cooldown Progress (Spell)" - or trigger.event == "Cooldown Progress (Item)" - or trigger.event == "Death Knight Rune")) then - - if (not trigger.showOn) then - if (trigger.use_inverse) then - trigger.showOn = "showOnReady" - else - trigger.showOn = "showOnCooldown" - end - - if (trigger.event == "Death Knight Rune") then - trigger.use_genericShowOn = true; - end - trigger.use_inverse = nil - end - end - - for old, new in pairs(combatLogUpgrade) do - if (trigger and trigger[old]) then - local useOld = "use_" .. old; - local useNew = "use_" .. new; - trigger[useNew] = trigger[useOld]; - trigger[new] = trigger[old]; - - trigger[old] = nil; - trigger[useOld] = nil; - end - end - - -- Convert separated Power Triggers to sub options of the Power trigger - if (trigger and trigger.type and trigger.event and trigger.type == "status" and oldPowerTriggers[trigger.event]) then - trigger.powertype = oldPowerTriggers[trigger.event] - trigger.use_powertype = true; - trigger.use_percentpower = false; - if (trigger.event == "Combo Points") then - trigger.power = trigger.combopoints; - trigger.power_operator = trigger.combopoints_operator - trigger.use_power = trigger.use_combopoints; - end - trigger.event = "Power"; - trigger.unit = "player"; - end - end - - if data.internalVersion < 6 then - if trigger and trigger.type ~= "aura" then - trigger.genericShowOn = trigger.showOn or "showOnActive" - trigger.showOn = nil - trigger.use_genericShowOn = trigger.use_showOn - end - end - end -end - function GenericTrigger.AllAdded() -- Remove GTFO options if GTFO isn't enabled and there are no saved GTFO auras local hideGTFO = true; diff --git a/WeakAuras/Init.lua b/WeakAuras/Init.lua index d4fe8a7..6150a29 100644 --- a/WeakAuras/Init.lua +++ b/WeakAuras/Init.lua @@ -2,7 +2,7 @@ WeakAuras = {} WeakAuras.L = {} WeakAuras.frames = {} -WeakAuras.normalWidth = 1.25 +WeakAuras.normalWidth = 1.3 WeakAuras.halfWidth = WeakAuras.normalWidth / 2 WeakAuras.doubleWidth = WeakAuras.normalWidth * 2 diff --git a/WeakAuras/Media/Textures/GitHub.tga b/WeakAuras/Media/Textures/GitHub.tga new file mode 100644 index 0000000..e306cdf Binary files /dev/null and b/WeakAuras/Media/Textures/GitHub.tga differ diff --git a/WeakAuras/Media/Textures/bug_report.tga b/WeakAuras/Media/Textures/bug_report.tga new file mode 100644 index 0000000..f7ffd92 Binary files /dev/null and b/WeakAuras/Media/Textures/bug_report.tga differ diff --git a/WeakAuras/Media/Textures/discord.tga b/WeakAuras/Media/Textures/discord.tga new file mode 100644 index 0000000..1f24ff5 Binary files /dev/null and b/WeakAuras/Media/Textures/discord.tga differ diff --git a/WeakAuras/Modernize.lua b/WeakAuras/Modernize.lua new file mode 100644 index 0000000..9f4a11c --- /dev/null +++ b/WeakAuras/Modernize.lua @@ -0,0 +1,929 @@ +if not WeakAuras.IsCorrectVersion() then return end +local AddonName, Private = ... + +-- Takes as input a table of display data and attempts to update it to be compatible with the current version +function Private.Modernize(data) + if (not data.internalVersion or data.internalVersion < 2) then + WeakAuras.prettyPrint(string.format("Data for '%s' is too old, can't modernize.", data.id)) + data.internalVersion = 2; + end + + -- Version 3 was introduced April 2018 in Legion + if (data.internalVersion < 3) then + if (data.parent) then + local parentData = WeakAuras.GetData(data.parent); + if(parentData and parentData.regionType == "dynamicgroup") then + -- Version 3 allowed for offsets for dynamic groups, before that they were ignored + -- Thus reset them in the V2 to V3 upgrade + data.xOffset = 0; + data.yOffset = 0; + end + end + end + + -- Version 4 was introduced July 2018 in BfA + if (data.internalVersion < 4) then + if (data.conditions) then + for conditionIndex, condition in ipairs(data.conditions) do + if (condition.check) then + local triggernum = condition.check.trigger; + if (triggernum) then + local trigger; + if (triggernum == 0) then + trigger = data.trigger; + elseif(data.additional_triggers and data.additional_triggers[triggernum]) then + trigger = data.additional_triggers[triggernum].trigger; + end + if (trigger and trigger.event == "Cooldown Progress (Spell)") then + if (condition.check.variable == "stacks") then + condition.check.variable = "charges"; + end + end + end + end + end + end + end + + -- Version 5 was introduced July 2018 in BFA + if data.internalVersion < 5 then + -- this is to fix hybrid sorting + if data.sortHybridTable then + if data.controlledChildren then + local newSortTable = {} + for index, isHybrid in pairs(data.sortHybridTable) do + local childID = data.controlledChildren[index] + if childID then + newSortTable[childID] = isHybrid + end + end + data.sortHybridTable = newSortTable + end + end + end + + -- Version 6 was introduced July 30, 2018 in BFA + if data.internalVersion < 6 then + if data.triggers then + for triggernum, triggerData in ipairs(data.triggers) do + local trigger = triggerData.trigger; + if trigger and trigger.type == "aura" then + if trigger.showOn == "showOnMissing" then + trigger.buffShowOn = "showOnMissing" + elseif trigger.showOn == "showActiveOrMissing" then + trigger.buffShowOn = "showAlways" + else + trigger.buffShowOn = "showOnActive" + end + trigger.showOn = nil + elseif trigger and trigger.type ~= "aura" then + trigger.genericShowOn = trigger.showOn or "showOnActive" + trigger.showOn = nil + trigger.use_genericShowOn = trigger.use_showOn + end + end + end + end + + -- Version 7 was introduced September 1, 2018 in BFA + -- Triggers were cleaned up into a 1-indexed array + + if data.internalVersion < 7 then + + -- migrate trigger data + data.triggers = data.additional_triggers or {} + tinsert(data.triggers, 1, { + trigger = data.trigger or {}, + untrigger = data.untrigger or {}, + }) + data.additional_triggers = nil + data.trigger = nil + data.untrigger = nil + data.numTriggers = nil + data.triggers.customTriggerLogic = data.customTriggerLogic + data.customTriggerLogic = nil + local activeTriggerMode = data.activeTriggerMode or WeakAuras.trigger_modes.first_active + if activeTriggerMode ~= WeakAuras.trigger_modes.first_active then + activeTriggerMode = activeTriggerMode + 1 + end + data.triggers.activeTriggerMode = activeTriggerMode + data.activeTriggerMode = nil + data.triggers.disjunctive = data.disjunctive + data.disjunctive = nil + -- migrate condition trigger references + local function recurseRepairChecks(checks) + if not checks then return end + for _, check in pairs(checks) do + if check.trigger and check.trigger >= 0 then + check.trigger = check.trigger + 1 + end + recurseRepairChecks(check.checks) + end + end + for _, condition in pairs(data.conditions) do + if condition.check.trigger and condition.check.trigger >= 0 then + condition.check.trigger = condition.check.trigger + 1 + end + recurseRepairChecks(condition.check.checks) + end + end + + -- Version 8 was introduced in September 2018 + -- Changes are in PreAdd + + -- Version 9 was introduced in September 2018 + if data.internalVersion < 9 then + local function repairCheck(check) + if check and check.variable == "buffed" then + local trigger = check.trigger and data.triggers[check.trigger] and data.triggers[check.trigger].trigger; + if (trigger) then + if(trigger.buffShowOn == "showOnActive") then + check.variable = "show"; + elseif (trigger.buffShowOn == "showOnMissing") then + check.variable = "show"; + check.value = check.value == 0 and 1 or 0; + end + end + end + end + + local function recurseRepairChecks(checks) + if not checks then return end + for _, check in pairs(checks) do + repairCheck(check); + recurseRepairChecks(check.checks); + end + end + for _, condition in pairs(data.conditions) do + repairCheck(condition.check); + recurseRepairChecks(condition.check.checks); + end + end + + -- Version 10 is skipped, due to a bad migration script (see https://github.com/WeakAuras/WeakAuras2/pull/1091) + + -- Version 11 was introduced in January 2019 + if data.internalVersion < 11 then + if data.url and data.url ~= "" then + local slug, version = data.url:match("wago.io/([^/]+)/([0-9]+)") + if not slug and not version then + version = 1 + end + if version and tonumber(version) then + data.version = tonumber(version) + end + end + end + + -- Version 13 was introduced March 2019 in BFA + if data.internalVersion < 13 then + if data.regionType == "dynamicgroup" then + local selfPoints = { + default = "CENTER", + RIGHT = function(data) + if data.align == "LEFT" then + return "TOPLEFT" + elseif data.align == "RIGHT" then + return "BOTTOMLEFT" + else + return "LEFT" + end + end, + LEFT = function(data) + if data.align == "LEFT" then + return "TOPRIGHT" + elseif data.align == "RIGHT" then + return "BOTTOMRIGHT" + else + return "RIGHT" + end + end, + UP = function(data) + if data.align == "LEFT" then + return "BOTTOMLEFT" + elseif data.align == "RIGHT" then + return "BOTTOMRIGHT" + else + return "BOTTOM" + end + end, + DOWN = function(data) + if data.align == "LEFT" then + return "TOPLEFT" + elseif data.align == "RIGHT" then + return "TOPRIGHT" + else + return "TOP" + end + end, + HORIZONTAL = function(data) + if data.align == "LEFT" then + return "TOP" + elseif data.align == "RIGHT" then + return "BOTTOM" + else + return "CENTER" + end + end, + VERTICAL = function(data) + if data.align == "LEFT" then + return "LEFT" + elseif data.align == "RIGHT" then + return "RIGHT" + else + return "CENTER" + end + end, + CIRCLE = "CENTER", + COUNTERCIRCLE = "CENTER", + } + local selfPoint = selfPoints[data.grow or "DOWN"] or selfPoints.DOWN + if type(selfPoint) == "function" then + selfPoint = selfPoint(data) + end + data.selfPoint = selfPoint + end + end + + -- Version 14 was introduced March 2019 in BFA + if data.internalVersion < 14 then + if data.triggers then + for triggerId, triggerData in pairs(data.triggers) do + if type(triggerData) == "table" + and triggerData.trigger + and triggerData.trigger.debuffClass + and type(triggerData.trigger.debuffClass) == "string" + and triggerData.trigger.debuffClass ~= "" + then + local idx = triggerData.trigger.debuffClass + data.triggers[triggerId].trigger.debuffClass = { [idx] = true } + end + end + end + end + + -- Version 15 was introduced April 2019 in BFA + if data.internalVersion < 15 then + if data.triggers then + for triggerId, triggerData in ipairs(data.triggers) do + if triggerData.trigger.type == "status" and triggerData.trigger.event == "Spell Known" then + triggerData.trigger.use_exact_spellName = true + end + end + end + end + + -- Version 16 was introduced May 2019 in BFA + if data.internalVersion < 16 then + -- second conversion: migrate name/realm conditions to tristate + if data.load.use_name == false then + data.load.use_name = nil + end + if data.load.use_realm == false then + data.load.use_realm = nil + end + end + + -- Version 18 was a migration for stance/form trigger, but deleted later because of migration issue + + -- Version 20 was introduced July 2019 in BFA + if data.internalVersion < 20 then + if data.regionType == "icon" then + local convertPoint = function(containment, point) + if not point or point == "CENTER" then + return "CENTER" + elseif containment == "INSIDE" then + return "INNER_" .. point + elseif containment == "OUTSIDE" then + return "OUTER_" .. point + end + end + + local text1 = { + ["type"] = "subtext", + text_visible = data.text1Enabled ~= false, + text_color = data.text1Color, + text_text = data.text1, + text_font = data.text1Font, + text_fontSize = data.text1FontSize, + text_fontType = data.text1FontFlags, + text_selfPoint = "AUTO", + text_anchorPoint = convertPoint(data.text1Containment, data.text1Point), + anchorXOffset = 0, + anchorYOffset = 0, + text_shadowColor = { 0, 0, 0, 1}, + text_shadowXOffset = 0, + text_shadowYOffset = 0, + } + + local usetext2 = data.text2Enabled + + local text2 = { + ["type"] = "subtext", + text_visible = data.text2Enabled or false, + text_color = data.text2Color, + text_text = data.text2, + text_font = data.text2Font, + text_fontSize = data.text2FontSize, + text_fontType = data.text2FontFlags, + text_selfPoint = "AUTO", + text_anchorPoint = convertPoint(data.text2Containment, data.text2Point), + anchorXOffset = 0, + anchorYOffset = 0, + text_shadowColor = { 0, 0, 0, 1}, + text_shadowXOffset = 0, + text_shadowYOffset = 0, + } + + data.text1Enabled = nil + data.text1Color = nil + data.text1 = nil + data.text1Font = nil + data.text1FontSize = nil + data.text1FontFlags = nil + data.text1Containment = nil + data.text1Point = nil + + data.text2Enabled = nil + data.text2Color = nil + data.text2 = nil + data.text2Font = nil + data.text2FontSize = nil + data.text2FontFlags = nil + data.text2Containment = nil + data.text2Point = nil + + local propertyRenames = { + text1Color = "sub.1.text_color", + text1FontSize = "sub.1.text_fontSize", + text2Color = "sub.2.text_color", + text2FontSize = "sub.2.text_fontSize" + } + + tinsert(data.subRegions, text1) + if (usetext2) then + tinsert(data.subRegions, text2) + end + + if (data.conditions) then + for conditionIndex, condition in ipairs(data.conditions) do + for changeIndex, change in ipairs(condition.changes) do + if propertyRenames[change.property] then + change.property = propertyRenames[change.property] + end + end + end + end + end + end + + -- Version 20 was introduced May 2019 in BFA + if data.internalVersion < 20 then + if data.regionType == "aurabar" then + local orientationToPostion = { + HORIZONTAL_INVERSE = { "INNER_LEFT", "INNER_RIGHT" }, + HORIZONTAL = { "INNER_RIGHT", "INNER_LEFT" }, + VERTICAL_INVERSE = { "INNER_BOTTOM", "INNER_TOP" }, + VERTICAL = {"INNER_TOP", "INNER_BOTTOM"} + } + + local positions = orientationToPostion[data.orientation] or { "INNER_LEFT", "INNER_RIGHT" } + + local text1 = { + ["type"] = "subtext", + text_visible = data.timer, + text_color = data.timerColor, + text_text = data.displayTextRight, + text_font = data.timerFont, + text_fontSize = data.timerSize, + text_fontType = data.timerFlags, + text_selfPoint = "AUTO", + text_anchorPoint = positions[1], + anchorXOffset = 0, + anchorYOffset = 0, + text_shadowColor = { 0, 0, 0, 1}, + text_shadowXOffset = 1, + text_shadowYOffset = -1, + rotateText = data.rotateText + } + + local text2 = { + ["type"] = "subtext", + text_visible = data.text, + text_color = data.textColor, + text_text = data.displayTextLeft, + text_font = data.textFont, + text_fontSize = data.textSize, + text_fontType = data.textFlags, + text_selfPoint = "AUTO", + text_anchorPoint = positions[2], + anchorXOffset = 0, + anchorYOffset = 0, + text_shadowColor = { 0, 0, 0, 1}, + text_shadowXOffset = 1, + text_shadowYOffset = -1, + rotateText = data.rotateText + } + + local text3 = { + ["type"] = "subtext", + text_visible = data.stacks, + text_color = data.stacksColor, + text_text = "%s", + text_font = data.stacksFont, + text_fontSize = data.stacksSize, + text_fontType = data.stacksFlags, + text_selfPoint = "AUTO", + text_anchorPoint = "ICON_CENTER", + anchorXOffset = 0, + anchorYOffset = 0, + text_shadowColor = { 0, 0, 0, 1}, + text_shadowXOffset = 1, + text_shadowYOffset = -1, + rotateText = data.rotateText + } + + data.timer = nil + data.textColor = nil + data.displayTextRight = nil + data.textFont = nil + data.textSize = nil + data.textFlags = nil + data.text = nil + data.timerColor = nil + data.displayTextLeft = nil + data.timerFont = nil + data.timerSize = nil + data.timerFlags = nil + data.stacks = nil + data.stacksColor = nil + data.stacksFont = nil + data.stacksSize = nil + data.stacksFlags = nil + data.rotateText = nil + + local propertyRenames = { + timerColor = "sub.1.text_color", + timerSize = "sub.1.text_fontSize", + textColor = "sub.2.text_color", + textSize = "sub.2.text_fontSize", + stacksColor = "sub.3.text_color", + stacksSize = "sub.3.text_fontSize", + } + + data.subRegions = data.subRegions or {} + tinsert(data.subRegions, text1) + tinsert(data.subRegions, text2) + tinsert(data.subRegions, text3) + + if (data.conditions) then + for conditionIndex, condition in ipairs(data.conditions) do + for changeIndex, change in ipairs(condition.changes) do + if propertyRenames[change.property] then + change.property = propertyRenames[change.property] + end + end + end + end + + end + end + + if data.internalVersion < 21 then + if data.regionType == "dynamicgroup" then + data.border = data.background and data.background ~= "None" + data.borderEdge = data.border + data.borderBackdrop = data.background ~= "None" and data.background + data.borderInset = data.backgroundInset + data.background = nil + data.backgroundInset = nil + end + end + + if data.internalVersion < 22 then + if data.regionType == "aurabar" then + data.subRegions = data.subRegions or {} + + local border = { + ["type"] = "subborder", + border_visible = data.border, + border_color = data.borderColor, + border_edge = data.borderEdge, + border_offset = data.borderOffset, + border_size = data.borderSize, + border_anchor = "bar", + } + + data.border = nil + data.borderColor = nil + data.borderEdge = nil + data.borderOffset = nil + data.borderInset = nil + data.borderSize = nil + if data.borderInFront then + tinsert(data.subRegions, border) + else + tinsert(data.subRegions, 1, border) + end + + local propertyRenames = { + borderColor = "sub.".. #data.subRegions..".border_color", + } + + if (data.conditions) then + for conditionIndex, condition in ipairs(data.conditions) do + for changeIndex, change in ipairs(condition.changes) do + if propertyRenames[change.property] then + change.property = propertyRenames[change.property] + end + end + end + end + end + end + + if data.internalVersion < 23 then + if data.triggers then + for triggerId, triggerData in ipairs(data.triggers) do + local trigger = triggerData.trigger + -- Stance/Form/Aura form field type changed from type="select" to type="multiselect" + if trigger and trigger.type == "status" and trigger.event == "Stance/Form/Aura" then + local value = trigger.form + if type(value) ~= "table" then + if trigger.use_form == false then + if value then + trigger.form = { multi = { [value] = true } } + else + trigger.form = { multi = { } } + end + elseif trigger.use_form then + trigger.form = { single = value } + end + end + end + end + end + end + + if data.internalVersion < 24 then + if data.triggers then + for triggerId, triggerData in ipairs(data.triggers) do + local trigger = triggerData.trigger + if trigger and trigger.type == "status" and trigger.event == "Weapon Enchant" then + if trigger.use_inverse then + trigger.showOn = "showOnMissing" + else + trigger.showOn = "showOnActive" + end + trigger.use_inverse = nil + if not trigger.use_weapon then + trigger.use_weapon = "true" + trigger.weapon = "main" + end + end + end + end + end + + if data.internalVersion < 25 then + if data.regionType == "icon" then + data.subRegions = data.subRegions or {} + -- Need to check if glow is needed + + local prefix = "sub.".. #data.subRegions + 1 .. "." + -- For Conditions + local propertyRenames = { + glow = prefix .. "glow", + useGlowColor = prefix .. "useGlowColor", + glowColor = prefix .. "glowColor", + glowType = prefix .. "glowType", + glowLines = prefix .. "glowLines", + glowFrequency = prefix .. "glowFrequency", + glowLength = prefix .. "glowLength", + glowThickness = prefix .. "glowThickness", + glowScale = prefix .. "glowScale", + glowBorder = prefix .. "glowBorder", + glowXOffset = prefix .. "glowXOffset", + glowYOffset = prefix .. "glowYOffset", + } + + local needsGlow = data.glow + if (not needsGlow and data.conditions) then + for conditionIndex, condition in ipairs(data.conditions) do + for changeIndex, change in ipairs(condition.changes) do + if propertyRenames[change.property] then + needsGlow = true + break + end + end + end + end + + if needsGlow then + local glow = { + ["type"] = "subglow", + glow = data.glow, + useGlowColor = data.useGlowColor, + glowColor = data.glowColor, + glowType = data.glowType, + glowLines = data.glowLines, + glowFrequency = data.glowFrequency, + glowLength = data.glowLength, + glowThickness = data.glowThickness, + glowScale = data.glowScale, + glowBorder = data.glowBorder, + glowXOffset = data.glowXOffset, + glowYOffset = data.glowYOffset, + } + tinsert(data.subRegions, glow) + end + + data.glow = nil + data.useglowColor = nil + data.useGlowColor = nil + data.glowColor = nil + data.glowType = nil + data.glowLines = nil + data.glowFrequency = nil + data.glowLength = nil + data.glowThickness = nil + data.glowScale = nil + data.glowBorder = nil + data.glowXOffset = nil + data.glowYOffset = nil + + if (data.conditions) then + for conditionIndex, condition in ipairs(data.conditions) do + for changeIndex, change in ipairs(condition.changes) do + if propertyRenames[change.property] then + change.property = propertyRenames[change.property] + end + end + end + end + end + end + + if data.internalVersion < 26 then + if data.conditions then + for conditionIndex, condition in ipairs(data.conditions) do + for changeIndex, change in ipairs(condition.changes) do + if change.property == "xOffset" or change.property == "yOffset" then + change.value = (change.value or 0) - (data[change.property] or 0) + change.property = change.property .. "Relative" + end + end + end + end + end + + if data.internalVersion < 28 then + if data.actions then + if data.actions.start and data.actions.start.do_glow then + data.actions.start.glow_frame_type = "FRAMESELECTOR" + end + if data.actions.finish and data.actions.finish.do_glow then + data.actions.finish.glow_frame_type = "FRAMESELECTOR" + end + end + end + + if data.internalVersion < 29 then + if data.actions then + if data.actions.start + and data.actions.start.do_glow + and data.actions.start.glow_type == nil + then + data.actions.start.glow_type = "buttonOverlay" + end + if data.actions.finish + and data.actions.finish.do_glow + and data.actions.finish.glow_type == nil + then + data.actions.finish.glow_type = "buttonOverlay" + end + end + end + + if data.internalVersion < 30 then + local convertLegacyPrecision = function(precision) + if not precision then + return 1 + end + if precision < 4 then + return precision, false + else + return precision - 3, true + end + end + + local progressPrecision = data.progressPrecision + local totalPrecision = data.totalPrecision + if data.regionType == "text" then + local seenSymbols = {} + WeakAuras.ParseTextStr(data.displayText, function(symbol) + if not seenSymbols[symbol] then + local triggerNum, sym = string.match(symbol, "(.+)%.(.+)") + sym = sym or symbol + if sym == "p" or sym == "t" then + data["displayText_format_" .. symbol .. "_format"] = "timed" + data["displayText_format_" .. symbol .. "_time_precision"], data["displayText_format_" .. symbol .. "_time_dynamic"] + = convertLegacyPrecision(sym == "p" and progressPrecision or totalPrecision) + end + end + seenSymbols[symbol] = symbol + end) + end + + if data.subRegions then + for index, subRegionData in ipairs(data.subRegions) do + if subRegionData.type == "subtext" then + local seenSymbols = {} + WeakAuras.ParseTextStr(subRegionData.text_text, function(symbol) + if not seenSymbols[symbol] then + local triggerNum, sym = string.match(symbol, "(.+)%.(.+)") + sym = sym or symbol + if sym == "p" or sym == "t" then + subRegionData["text_text_format_" .. symbol .. "_format"] = "timed" + subRegionData["text_text_format_" .. symbol .. "_time_precision"], subRegionData["text_text_format_" .. symbol .. "_time_dynamic"] + = convertLegacyPrecision(sym == "p" and progressPrecision or totalPrecision) + end + end + seenSymbols[symbol] = symbol + end) + end + end + end + + if data.actions then + for _, when in ipairs{ "start", "finish" } do + if data.actions[when] then + local seenSymbols = {} + WeakAuras.ParseTextStr(data.actions[when].message, function(symbol) + if not seenSymbols[symbol] then + local triggerNum, sym = string.match(symbol, "(.+)%.(.+)") + sym = sym or symbol + if sym == "p" or sym == "t" then + data.actions[when]["message_format_" .. symbol .. "_format"] = "timed" + data.actions[when]["message_format_" .. symbol .. "_time_precision"], data.actions[when]["message_format_" .. symbol .. "_time_dynamic"] + = convertLegacyPrecision(sym == "p" and progressPrecision or totalPrecision) + end + end + seenSymbols[symbol] = symbol + end) + end + end + end + + if data.conditions then + for conditionIndex, condition in ipairs(data.conditions) do + for changeIndex, change in ipairs(condition.changes) do + if change.property == "chat" and change.value then + local seenSymbols = {} + WeakAuras.ParseTextStr(change.value.message, function(symbol) + if not seenSymbols[symbol] then + local triggerNum, sym = string.match(symbol, "(.+)%.(.+)") + sym = sym or symbol + if sym == "p" or sym == "t" then + change.value["message_format_" .. symbol .. "_format"] = "timed" + change.value["message_format_" .. symbol .. "_time_precision"], change.value["message_format_" .. symbol .. "_time_dynamic"] + = convertLegacyPrecision(sym == "p" and progressPrecision or totalPrecision) + end + end + seenSymbols[symbol] = symbol + end) + end + end + end + end + + -- To convert: + -- * actions + -- * conditions + data.progressPrecision = nil + data.totalPrecision = nil + end + + -- Introduced in June 2020 in Bfa + if data.internalVersion < 31 then + local allowedNames + local ignoredNames + if data.load.use_name == true and data.load.name then + allowedNames = data.load.name + elseif data.load.use_name == false and data.load.name then + ignoredNames = data.load.name + end + + if data.load.use_realm == true and data.load.realm then + allowedNames = (allowedNames or "") .. "-" .. data.load.realm + elseif data.load.use_realm == false and data.load.realm then + ignoredNames = (ignoredNames or "") .. "-" .. data.load.realm + end + + if allowedNames then + data.load.use_namerealm = true + data.load.namerealm = allowedNames + end + + if ignoredNames then + data.load.use_namerealmblack = true + data.load.namerealmblack = ignoredNames + end + + data.load.use_name = nil + data.load.name = nil + data.load.use_realm = nil + data.load.realm = nil + end + +-- Introduced in June 2020 in Bfa + if data.internalVersion < 32 then + local replacements = {} + local function repairCheck(replacements, check) + if check and check.trigger then + if replacements[check.trigger] then + if replacements[check.trigger][check.variable] then + check.variable = replacements[check.trigger][check.variable] + end + end + end + end + + if data.triggers then + for triggerId, triggerData in ipairs(data.triggers) do + if triggerData.trigger.type == "status" then + local event = triggerData.trigger.event + if event == "Unit Characteristics" or event == "Health" or event == "Power" then + replacements[triggerId] = {} + replacements[triggerId]["use_name"] = "use_namerealm" + replacements[triggerId]["name"] = "namerealm" + elseif event == "Cast" then + replacements[triggerId] = {} + replacements[triggerId]["use_sourceName"] = "use_sourceNameRealm" + replacements[triggerId]["sourceName"] = "sourceNameRealm" + replacements[triggerId]["use_destName"] = "use_destNameRealm" + replacements[triggerId]["destName"] = "destNameRealm" + end + + if replacements[triggerId] then + for old, new in pairs(replacements[triggerId]) do + triggerData.trigger[new] = triggerData.trigger[old] + triggerData.trigger[old] = nil + end + + local function recurseRepairChecks(replacements, checks) + if not checks then return end + for _, check in pairs(checks) do + repairCheck(replacements, check); + recurseRepairChecks(replacements, check.checks); + end + end + for _, condition in pairs(data.conditions) do + repairCheck(replacements, condition.check); + recurseRepairChecks(replacements, condition.check.checks); + end + end + end + end + end + end + + -- Introduced in July 2020 in Bfa + if data.internalVersion < 33 then + data.load.use_ignoreNameRealm = data.load.use_namerealmblack + data.load.ignoreNameRealm = data.load.namerealmblack + data.load.use_namerealmblack = nil + data.load.namerealmblack = nil + + -- trigger.useBlackExactSpellId and trigger.blackauraspellids + if data.triggers then + for triggerId, triggerData in ipairs(data.triggers) do + triggerData.trigger.useIgnoreName = triggerData.trigger.useBlackName + triggerData.trigger.ignoreAuraNames = triggerData.trigger.blackauranames + triggerData.trigger.useIgnoreExactSpellId = triggerData.trigger.useBlackExactSpellId + triggerData.trigger.ignoreAuraSpellids = triggerData.trigger.blackauraspellids + + triggerData.trigger.useBlackName = nil + triggerData.trigger.blackauranames = nil + triggerData.trigger.useBlackExactSpellId = nil + triggerData.trigger.blackauraspellids = nil + end + end + + end + + -- Introduced in July 2020 in Shadolands + if data.internalVersion < 34 then + if data.regionType == 'dynamicgroup' + and (data.grow == "CIRCLE" or data.grow == "COUNTERCIRCLE") then + if data.arcLength == 360 then + data.fullCircle = true + else + data.fullCircle = false + end + end + end + + + data.internalVersion = max(data.internalVersion or 0, WeakAuras.InternalVersion()); +end diff --git a/WeakAuras/RegionTypes/DynamicGroup.lua b/WeakAuras/RegionTypes/DynamicGroup.lua index 4134ca5..6afc73f 100644 --- a/WeakAuras/RegionTypes/DynamicGroup.lua +++ b/WeakAuras/RegionTypes/DynamicGroup.lua @@ -27,6 +27,7 @@ local default = { yOffset = 0, radius = 200, rotation = 0, + fullCircle = true, arcLength = 360, constantFactor = "RADIUS", frameStrata = 1, @@ -574,7 +575,7 @@ local growers = { local radius = data.radius or 0 local limit = data.useLimit and data.limit or math.huge local sAngle = (data.rotation or 0) * math.pi / 180 - local arc = (data.arcLength or 0) * math.pi / 180 + local arc = (data.fullCircle and 360 or data.arcLength or 0) * math.pi / 180 local anchorPerUnitFunc = data.useAnchorPerUnit and createAnchorPerUnitFunc(data) return function(newPositions, activeRegions) local frames = {} @@ -596,7 +597,14 @@ local growers = { end end local theta = sAngle - local dAngle = arc / numVisible + local dAngle + if numVisible == 1 then + dAngle = 0 + elseif not data.fullCircle then + dAngle = arc / (numVisible - 1) + else + dAngle = arc / numVisible + end newPositions[frame] = {} for i, regionData in ipairs(regionDatas) do if i <= numVisible then @@ -615,7 +623,7 @@ local growers = { local radius = data.radius or 0 local limit = data.useLimit and data.limit or math.huge local sAngle = (data.rotation or 0) * math.pi / 180 - local arc = (data.arcLength or 0) * math.pi / 180 + local arc = (data.fullCircle and 360 or data.arcLength or 0) * math.pi / 180 local anchorPerUnitFunc = data.useAnchorPerUnit and createAnchorPerUnitFunc(data) return function(newPositions, activeRegions) local frames = {} @@ -637,7 +645,14 @@ local growers = { end end local theta = sAngle - local dAngle = arc / -numVisible + local dAngle + if numVisible == 1 then + dAngle = 0 + elseif not data.fullCircle then + dAngle = arc / (1 - numVisible) + else + dAngle = arc / -numVisible + end newPositions[frame] = {} for i, regionData in ipairs(regionDatas) do if i <= numVisible then @@ -1010,7 +1025,7 @@ local function modify(parent, region, data) self.needToPosition = false if #self.sortedChildren > 0 then if animate then - WeakAuras.RegisterGroupForPositioning(data.id, self) + Private.RegisterGroupForPositioning(data.id, self) else self:DoPositionChildren() end diff --git a/WeakAuras/RegionTypes/RegionPrototype.lua b/WeakAuras/RegionTypes/RegionPrototype.lua index 445a676..2c8588e 100644 --- a/WeakAuras/RegionTypes/RegionPrototype.lua +++ b/WeakAuras/RegionTypes/RegionPrototype.lua @@ -740,12 +740,11 @@ function WeakAuras.regionPrototype.AddExpandFunction(data, region, cloneId, pare if region.PreHide then region:PreHide() end - if WeakAuras.checkConditions[id] then - WeakAuras.checkConditions[id](region, true); - end + + Private.RunConditions(region, id, true) region:Hide(); if (cloneId) then - WeakAuras.ReleaseClone(region.id, cloneId, data.regionType); + Private.ReleaseClone(region.id, cloneId, data.regionType); parent:RemoveChild(id, cloneId) else parent:DeactivateChild(id, cloneId); @@ -756,12 +755,10 @@ function WeakAuras.regionPrototype.AddExpandFunction(data, region, cloneId, pare if region.PreHide then region:PreHide() end - if WeakAuras.checkConditions[id] then - WeakAuras.checkConditions[id](region, true); - end + Private.RunConditions(region, id, true) region:Hide(); if (cloneId) then - WeakAuras.ReleaseClone(region.id, cloneId, data.regionType); + Private.ReleaseClone(region.id, cloneId, data.regionType); end end end diff --git a/WeakAuras/WeakAuras.lua b/WeakAuras/WeakAuras.lua index 3ba68d3..3655964 100644 --- a/WeakAuras/WeakAuras.lua +++ b/WeakAuras/WeakAuras.lua @@ -190,40 +190,21 @@ local loadFuncsForOptions = {}; -- Mapping of events to ids, contains true if a aura should be checked for a certain event local loadEvents = {} --- Check Conditions Functions, keyed on id -local checkConditions = {}; -WeakAuras.checkConditions = checkConditions - --- Dynamic Condition functions to run. keyed on event and id -local dynamicConditions = {}; - --- Global Dynamic Condition Funcs, keyed on the event -local globalDynamicConditionFuncs = {}; - -- All regions keyed on id, has properties: region, regionType, also see clones WeakAuras.regions = {}; local regions = WeakAuras.regions; -WeakAuras.auras = {}; -local auras = WeakAuras.auras; -WeakAuras.events = {}; -local events = WeakAuras.events; -- keyed on id, contains bool indicating whether the aura is loaded WeakAuras.loaded = {}; local loaded = WeakAuras.loaded; -WeakAuras.specificBosses = {}; -local specificBosses = WeakAuras.specificBosses; -WeakAuras.specificUnits = {}; -local specificUnits = WeakAuras.specificUnits; - -- contains regions for clones WeakAuras.clones = {}; local clones = WeakAuras.clones; -- Unused regions that are kept around for clones -WeakAuras.clonePool = {}; -local clonePool = WeakAuras.clonePool; +Private.clonePool = {}; +local clonePool = Private.clonePool; -- One table per regionType, see RegisterRegionType, notable properties: create, modify and default WeakAuras.regionTypes = {}; @@ -245,8 +226,6 @@ local triggerTypes = WeakAuras.triggerTypes; -- Maps from trigger type to a functin that can create options for the trigger WeakAuras.triggerTypesOptions = {}; -local triggerTypesOptions = WeakAuras.triggerTypesOptions; - -- Trigger State, updated by trigger systems, then applied to regions by UpdatedTriggerState -- keyed on id, triggernum, cloneid @@ -271,33 +250,20 @@ local triggerTypesOptions = WeakAuras.triggerTypesOptions; -- index: The index of the buff/debuff for the buff trigger system, used to set the tooltip -- spellId: spellId of the buff/debuff, used to set the tooltip -WeakAuras.triggerState = {} -local triggerState = WeakAuras.triggerState; +local triggerState = {} -- Fallback states local fallbacksStates = {}; -- List of all trigger systems, contains each system once -WeakAuras.triggerSystems = {} -local triggerSystems = WeakAuras.triggerSystems; - -WeakAuras.forceable_events = {}; +local triggerSystems = {} local from_files = {}; local timers = {}; -- Timers for autohiding, keyed on id, triggernum, cloneid WeakAuras.timers = timers; -local loaded_events = {}; -WeakAuras.loaded_events = loaded_events; -local loaded_auras = {}; -WeakAuras.loaded_auras = loaded_auras; --- Animations -WeakAuras.animations = {}; -local animations = WeakAuras.animations; -WeakAuras.pending_controls = {}; -local pending_controls = WeakAuras.pending_controls; WeakAuras.raidUnits = {}; WeakAuras.partyUnits = {}; @@ -311,10 +277,8 @@ do end local playerLevel = UnitLevel("player"); -WeakAuras.currentInstanceType = "none" - -- Custom Action Functions, keyed on id, "init" / "start" / "finish" -WeakAuras.customActionsFunctions = {}; +Private.customActionsFunctions = {}; -- Custom Functions used in conditions, keyed on id, condition number, "changes", property number WeakAuras.customConditionsFunctions = {}; @@ -325,8 +289,6 @@ WeakAuras.conditionTextFormatters = {} -- keyed on UID not on id! WeakAuras.conditionHelpers = {} -local anim_function_strings = WeakAuras.anim_function_strings; -local anim_presets = WeakAuras.anim_presets; local load_prototype = WeakAuras.load_prototype; local levelColors = { @@ -731,724 +693,43 @@ function WeakAuras.GetActiveConditions(id, cloneId) return triggerState[id].activatedConditions[cloneId]; end -local function formatValueForAssignment(vType, value, pathToCustomFunction, pathToFormatters) - if (value == nil) then - value = false; - end - if (vType == "bool") then - return value and tostring(value) or "false"; - elseif(vType == "number") then - return value and tostring(value) or "0"; - elseif (vType == "list") then - return type(value) == "string" and string.format("%q", value) or "nil"; - elseif(vType == "color") then - if (value and type(value) == "table") then - return string.format("{%s, %s, %s, %s}", tostring(value[1]), tostring(value[2]), tostring(value[3]), tostring(value[4])); - end - return "{1, 1, 1, 1}"; - elseif(vType == "chat") then - if (value and type(value) == "table") then - local serialized = string.format("{message_type = %q, message = %q, message_dest = %q, message_channel = %q, message_custom = %s, message_formaters = %s}", - tostring(value.message_type), tostring(value.message or ""), - tostring(value.message_dest), tostring(value.message_channel), - pathToCustomFunction, - pathToFormatters) - return serialized - end - elseif(vType == "sound") then - if (value and type(value) == "table") then - return string.format("{ sound = %q, sound_channel = %q, sound_path = %q, sound_kit_id = %q, sound_type = %q, %s}", - tostring(value.sound or ""), tostring(value.sound_channel or ""), tostring(value.sound_path or ""), - tostring(value.sound_kit_id or ""), tostring(value.sound_type or ""), - value.sound_repeat and "sound_repeat = " .. tostring(value.sound_repeat) or "nil"); - end - elseif(vType == "customcode") then - return string.format("%s", pathToCustomFunction); - elseif vType == "glowexternal" then - if (value and type(value) == "table") then - return ([[{ glow_action = %q, glow_frame_type = %q, glow_type = %q, - glow_frame = %q, use_glow_color = %s, glow_color = {%s, %s, %s, %s}, - glow_lines = %d, glow_frequency = %f, glow_length = %f, glow_thickness = %f, glow_XOffset = %f, glow_YOffset = %f, - glow_scale = %f, glow_border = %s }]]):format( - value.glow_action or "", - value.glow_frame_type or "", - value.glow_type or "", - value.glow_frame or "", - value.use_glow_color and "true" or "false", - type(value.glow_color) == "table" and tostring(value.glow_color[1]) or "1", - type(value.glow_color) == "table" and tostring(value.glow_color[2]) or "1", - type(value.glow_color) == "table" and tostring(value.glow_color[3]) or "1", - type(value.glow_color) == "table" and tostring(value.glow_color[4]) or "1", - value.glow_lines or 8, - value.glow_frequency or 0.25, - value.glow_length or 10, - value.glow_thickness or 1, - value.glow_XOffset or 0, - value.glow_YOffset or 0, - value.glow_scale or 1, - value.glow_border and "true" or "false" - ) - end - end - return "nil"; -end - -local function formatValueForCall(type, property) - if (type == "bool" or type == "number" or type == "list") then - return "propertyChanges['" .. property .. "']"; - elseif (type == "color") then - local pcp = "propertyChanges['" .. property .. "']"; - return pcp .. "[1], " .. pcp .. "[2], " .. pcp .. "[3], " .. pcp .. "[4]"; - end - return "nil"; -end - -local conditionChecksTimers = {}; -conditionChecksTimers.recheckTime = {}; -conditionChecksTimers.recheckHandle = {}; - -function WeakAuras.scheduleConditionCheck(time, id, cloneId) - conditionChecksTimers.recheckTime[id] = conditionChecksTimers.recheckTime[id] or {} - conditionChecksTimers.recheckHandle[id] = conditionChecksTimers.recheckHandle[id] or {}; - - if (conditionChecksTimers.recheckTime[id][cloneId] and conditionChecksTimers.recheckTime[id][cloneId] > time) then - timer:CancelTimer(conditionChecksTimers.recheckHandle[id][cloneId]); - conditionChecksTimers.recheckHandle[id][cloneId] = nil; - end - - if (conditionChecksTimers.recheckHandle[id][cloneId] == nil) then - conditionChecksTimers.recheckHandle[id][cloneId] = timer:ScheduleTimerFixed(function() - conditionChecksTimers.recheckHandle[id][cloneId] = nil; - local region; - if(cloneId and cloneId ~= "") then - region = clones[id] and clones[id][cloneId]; - else - region = WeakAuras.regions[id].region; - end - if (region and region.toShow) then - checkConditions[id](region); - end - end, time - GetTime()) - conditionChecksTimers.recheckTime[id][cloneId] = time; - end -end - -function WeakAuras.CallCustomConditionTest(uid, testFunctionNumber, ...) - local ok, result = pcall(WeakAuras.conditionHelpers[uid].customTestFunctions[testFunctionNumber], ...) - if not ok then - geterrorhandler()(result) - elseif (ok) then - return result - end -end - -local function CreateTestForCondition(uid, input, allConditionsTemplate, usedStates) - local trigger = input and input.trigger; - local variable = input and input.variable; - local op = input and input.op; - local value = input and input.value; - - local check = nil; - local recheckCode = nil; - - if (variable == "AND" or variable == "OR") then - local test = {}; - if (input.checks) then - for i, subcheck in ipairs(input.checks) do - local subtest, subrecheckCode = CreateTestForCondition(uid, subcheck, allConditionsTemplate, usedStates); - if (subtest) then - tinsert(test, "(" .. subtest .. ")"); - end - if (subrecheckCode) then - recheckCode = recheckCode or ""; - recheckCode = recheckCode .. subrecheckCode; - end - end - end - if (next(test)) then - if (variable == "AND") then - check = table.concat(test, " and "); - else - check = table.concat(test, " or "); - end - end - end - - if (trigger and variable and value) then - usedStates[trigger] = true; - - local conditionTemplate = allConditionsTemplate[trigger] and allConditionsTemplate[trigger][variable]; - local cType = conditionTemplate and conditionTemplate.type; - local test = conditionTemplate and conditionTemplate.test; - local preamble = conditionTemplate and conditionTemplate.preamble; - - local stateCheck = "state[" .. trigger .. "] and state[" .. trigger .. "].show and "; - local stateVariableCheck = "state[" .. trigger .. "]." .. variable .. "~= nil and "; - - local preambleString - - if preamble then - WeakAuras.conditionHelpers[uid] = WeakAuras.conditionHelpers[uid] or {} - WeakAuras.conditionHelpers[uid].preambles = WeakAuras.conditionHelpers[uid].preambles or {} - tinsert(WeakAuras.conditionHelpers[uid].preambles, preamble(value)); - local preambleNumber = #WeakAuras.conditionHelpers[uid].preambles - preambleString = string.format("WeakAuras.conditionHelpers[%q].preambles[%s]", uid, preambleNumber) - end - - if (test) then - if (value) then - WeakAuras.conditionHelpers[uid] = WeakAuras.conditionHelpers[uid] or {} - WeakAuras.conditionHelpers[uid].customTestFunctions = WeakAuras.conditionHelpers[uid].customTestFunctions or {} - tinsert(WeakAuras.conditionHelpers[uid].customTestFunctions, test); - local testFunctionNumber = #(WeakAuras.conditionHelpers[uid].customTestFunctions); - local valueString = type(value) == "string" and string.format("%q", value) or value; - local opString = type(op) == "string" and string.format("%q", op) or op; - check = string.format("state and WeakAuras.CallCustomConditionTest(%q, %s, state[%s], %s, %s, %s)", - uid, testFunctionNumber, trigger, valueString, (opString or "nil"), preambleString or "nil"); - end - elseif (cType == "customcheck") then - if value then - local customCheck = WeakAuras.LoadFunction("return " .. value, "custom check") - if customCheck then - WeakAuras.conditionHelpers[uid] = WeakAuras.conditionHelpers[uid] or {} - WeakAuras.conditionHelpers[uid].customTestFunctions = WeakAuras.conditionHelpers[uid].customTestFunctions or {} - tinsert(WeakAuras.conditionHelpers[uid].customTestFunctions, customCheck); - local testFunctionNumber = #(WeakAuras.conditionHelpers[uid].customTestFunctions); - - check = string.format("state and WeakAuras.CallCustomConditionTest(%q, %s, state)", - uid, testFunctionNumber, trigger); - end - end - elseif (cType == "number" and op) then - local v = tonumber(value) - if (v) then - check = stateCheck .. stateVariableCheck .. "state[" .. trigger .. "]." .. variable .. op .. v; - end - elseif (cType == "timer" and op) then - if (op == "==") then - check = stateCheck .. stateVariableCheck .. "abs(state[" .. trigger .. "]." ..variable .. "- now -" .. value .. ") < 0.05"; - else - check = stateCheck .. stateVariableCheck .. "state[" .. trigger .. "]." .. variable .. "- now" .. op .. value; - end - elseif (cType == "select" and op) then - if (tonumber(value)) then - check = stateCheck .. stateVariableCheck .. "state[" .. trigger .. "]." .. variable .. op .. tonumber(value); - else - check = stateCheck .. stateVariableCheck .. "state[" .. trigger .. "]." .. variable .. op .. "'" .. value .. "'"; - end - elseif (cType == "bool") then - local rightSide = value == 0 and "false" or "true"; - check = stateCheck .. stateVariableCheck .. "state[" .. trigger .. "]." .. variable .. "==" .. rightSide - elseif (cType == "string") then - if(op == "==") then - check = stateCheck .. stateVariableCheck .. "state[" .. trigger .. "]." .. variable .. " == [[" .. value .. "]]"; - elseif (op == "find('%s')") then - check = stateCheck .. stateVariableCheck .. "state[" .. trigger .. "]." .. variable .. ":find([[" .. value .. "]], 1, true)"; - elseif (op == "match('%s')") then - check = stateCheck .. stateVariableCheck .. "state[" .. trigger .. "]." .. variable .. ":match([[" .. value .. "]], 1, true)"; - end - end - - if (cType == "timer" and value) then - recheckCode = " nextTime = state[" .. trigger .. "] and state[" .. trigger .. "]." .. variable .. " and (state[" .. trigger .. "]." .. variable .. " -" .. value .. ")\n"; - recheckCode = recheckCode .. " if (nextTime and (not recheckTime or nextTime < recheckTime) and nextTime >= now) then\n" - recheckCode = recheckCode .. " recheckTime = nextTime\n"; - recheckCode = recheckCode .. " end\n" - end - end - - return check, recheckCode; -end - -local function CreateCheckCondition(uid, ret, condition, conditionNumber, allConditionsTemplate, debug) - local usedStates = {}; - local check, recheckCode = CreateTestForCondition(uid, condition.check, allConditionsTemplate, usedStates); - if (check) then - ret = ret .. " state = region.states\n" - ret = ret .. " if (" .. check .. ") then\n"; - ret = ret .. " newActiveConditions[" .. conditionNumber .. "] = true;\n"; - ret = ret .. " end\n"; - end - if (recheckCode) then - ret = ret .. recheckCode; - end - if (check or recheckCode) then - ret = ret .. "\n"; - end - return ret; -end - -local function ParseProperty(property) - local subIndex, prop = string.match(property, "^sub%.(%d*).(.*)") - if subIndex then - return tonumber(subIndex), prop - else - return nil, property - end -end - -local function GetBaseProperty(data, property, start) - if (not data) then - return nil; - end - - local subIndex, prop = ParseProperty(property) - if subIndex then - return GetBaseProperty(data.subRegions[subIndex], prop, start) - end - - start = start or 1; - local next = string.find(property, ".", start, true); - if (next) then - return GetBaseProperty(data[string.sub(property, start, next - 1)], property, next + 1); - end - - local key = string.sub(property, start); - return data[key] or data[tonumber(key)]; -end - -local function CreateDeactivateCondition(ret, condition, conditionNumber, data, properties, usedProperties, debug) - if (condition.changes) then - ret = ret .. " if (activatedConditions[".. conditionNumber .. "] and not newActiveConditions[" .. conditionNumber .. "]) then\n" - if (debug) then ret = ret .. " print('Deactivating condition " .. conditionNumber .. "' )\n"; end - for changeNum, change in ipairs(condition.changes) do - if (change.property) then - local propertyData = properties and properties[change.property] - if (propertyData and propertyData.type and propertyData.setter) then - usedProperties[change.property] = true; - ret = ret .. " propertyChanges['" .. change.property .. "'] = " .. formatValueForAssignment(propertyData.type, GetBaseProperty(data, change.property)) .. "\n"; - if (debug) then ret = ret .. " print('- " .. change.property .. " " ..formatValueForAssignment(propertyData.type, GetBaseProperty(data, change.property)) .. "')\n"; end - end - end - end - ret = ret .. " end\n" - end - - return ret; -end - -local function CreateActivateCondition(ret, id, condition, conditionNumber, properties, debug) - if (condition.changes) then - ret = ret .. " if (newActiveConditions[" .. conditionNumber .. "]) then\n" - ret = ret .. " if (not activatedConditions[".. conditionNumber .. "]) then\n" - if (debug) then ret = ret .. " print('Activating condition " .. conditionNumber .. "' )\n"; end - -- non active => active - for changeNum, change in ipairs(condition.changes) do - if (change.property) then - local propertyData = properties and properties[change.property] - if (propertyData and propertyData.type) then - if (propertyData.setter) then - ret = ret .. " propertyChanges['" .. change.property .. "'] = " .. formatValueForAssignment(propertyData.type, change.value) .. "\n"; - if (debug) then ret = ret .. " print('- " .. change.property .. " " .. formatValueForAssignment(propertyData.type, change.value) .. "')\n"; end - elseif (propertyData.action) then - local pathToCustomFunction = "nil"; - local pathToFormatter = "nil" - if (WeakAuras.customConditionsFunctions[id] - and WeakAuras.customConditionsFunctions[id][conditionNumber] - and WeakAuras.customConditionsFunctions[id][conditionNumber].changes - and WeakAuras.customConditionsFunctions[id][conditionNumber].changes[changeNum]) then - pathToCustomFunction = string.format("WeakAuras.customConditionsFunctions[%q][%s].changes[%s]", id, conditionNumber, changeNum); - end - if WeakAuras.conditionTextFormatters[id] - and WeakAuras.conditionTextFormatters[id][conditionNumber] - and WeakAuras.conditionTextFormatters[id][conditionNumber].changes - and WeakAuras.conditionTextFormatters[id][conditionNumber].changes[changeNum] then - pathToFormatter = string.format("WeakAuras.conditionTextFormatters[%q][%s].changes[%s]", id, conditionNumber, changeNum); - end - ret = ret .. " region:" .. propertyData.action .. "(" .. formatValueForAssignment(propertyData.type, change.value, pathToCustomFunction, pathToFormatter) .. ")" .. "\n"; - if (debug) then ret = ret .. " print('# " .. propertyData.action .. "(" .. formatValueForAssignment(propertyData.type, change.value, pathToCustomFunction, pathToFormatter) .. "')\n"; end - end - end - end - end - ret = ret .. " else\n" - -- active => active, only override properties - for changeNum, change in ipairs(condition.changes) do - if (change.property) then - local propertyData = properties and properties[change.property] - if (propertyData and propertyData.type and propertyData.setter) then - ret = ret .. " if(propertyChanges['" .. change.property .. "'] ~= nil) then\n" - ret = ret .. " propertyChanges['" .. change.property .. "'] = " .. formatValueForAssignment(propertyData.type, change.value) .. "\n"; - if (debug) then ret = ret .. " print('- " .. change.property .. " " .. formatValueForAssignment(propertyData.type, change.value) .. "')\n"; end - ret = ret .. " end\n" - end - end - end - ret = ret .. " end\n" - ret = ret .. " end\n" - ret = ret .. "\n"; - ret = ret .. " activatedConditions[".. conditionNumber .. "] = newActiveConditions[" .. conditionNumber .. "]\n"; - end - - return ret; -end - -function WeakAuras.LoadCustomActionFunctions(data) +local function LoadCustomActionFunctions(data) local id = data.id; - WeakAuras.customActionsFunctions[id] = {}; + Private.customActionsFunctions[id] = {}; if (data.actions) then if (data.actions.init and data.actions.init.do_custom and data.actions.init.custom) then local func = WeakAuras.LoadFunction("return function() "..(data.actions.init.custom).."\n end", id, "initialization"); - WeakAuras.customActionsFunctions[id]["init"] = func; + Private.customActionsFunctions[id]["init"] = func; end if (data.actions.start) then if (data.actions.start.do_custom and data.actions.start.custom) then local func = WeakAuras.LoadFunction("return function() "..(data.actions.start.custom).."\n end", id, "show action"); - WeakAuras.customActionsFunctions[id]["start"] = func; + Private.customActionsFunctions[id]["start"] = func; end if (data.actions.start.do_message and data.actions.start.message_custom) then local func = WeakAuras.LoadFunction("return "..(data.actions.start.message_custom), id, "show message"); - WeakAuras.customActionsFunctions[id]["start_message"] = func; + Private.customActionsFunctions[id]["start_message"] = func; end end if (data.actions.finish) then if (data.actions.finish.do_custom and data.actions.finish.custom) then local func = WeakAuras.LoadFunction("return function() "..(data.actions.finish.custom).."\n end", id, "hide action"); - WeakAuras.customActionsFunctions[id]["finish"] = func; + Private.customActionsFunctions[id]["finish"] = func; end if (data.actions.finish.do_message and data.actions.finish.message_custom) then local func = WeakAuras.LoadFunction("return "..(data.actions.finish.message_custom), id, "hide message"); - WeakAuras.customActionsFunctions[id]["finish_message"] = func; + Private.customActionsFunctions[id]["finish_message"] = func; end end end end -function WeakAuras.GetProperties(data) - local properties; - local propertiesFunction = WeakAuras.regionTypes[data.regionType] and WeakAuras.regionTypes[data.regionType].properties; - if (type(propertiesFunction) == "function") then - properties = propertiesFunction(data); - elseif propertiesFunction then - properties = CopyTable(propertiesFunction); - else - properties = {} - end - if data.subRegions then - local subIndex = {} - for index, subRegion in ipairs(data.subRegions) do - local subRegionTypeData = WeakAuras.subRegionTypes[subRegion.type]; - local propertiesFunction = subRegionTypeData and subRegionTypeData.properties - local subProperties; - if (type(propertiesFunction) == "function") then - subProperties = propertiesFunction(data, subRegion); - elseif propertiesFunction then - subProperties = CopyTable(propertiesFunction) - end - - if subProperties then - for key, property in pairs(subProperties) do - subIndex[key] = subIndex[key] and subIndex[key] + 1 or 1 - property.display = { subIndex[key] .. ". " .. subRegionTypeData.displayName, property.display, property.defaultProperty } - properties["sub." .. index .. "." .. key ] = property; - end - end - end - end - - return properties; -end - -function WeakAuras.LoadConditionPropertyFunctions(data) - local id = data.id; - if (data.conditions) then - WeakAuras.customConditionsFunctions[id] = {}; - for conditionNumber, condition in ipairs(data.conditions) do - if (condition.changes) then - for changeIndex, change in ipairs(condition.changes) do - if ( (change.property == "chat" or change.property == "customcode") and type(change.value) == "table" and change.value.custom) then - local custom = change.value.custom; - local prefix, suffix; - if (change.property == "chat") then - prefix, suffix = "return ", ""; - else - prefix, suffix = "return function()", "\nend"; - end - local customFunc = WeakAuras.LoadFunction(prefix .. custom .. suffix, id, "condition"); - if (customFunc) then - WeakAuras.customConditionsFunctions[id][conditionNumber] = WeakAuras.customConditionsFunctions[id][conditionNumber] or {}; - WeakAuras.customConditionsFunctions[id][conditionNumber].changes = WeakAuras.customConditionsFunctions[id][conditionNumber].changes or {}; - WeakAuras.customConditionsFunctions[id][conditionNumber].changes[changeIndex] = customFunc; - end - end - if change.property == "chat" then - local getter = function(key, default) - local fullKey = "message_format_" .. key - if change.value[fullKey] == nil then - change.value[fullKey] = default - end - return change.value[fullKey] - end - local formatters = change.value and WeakAuras.CreateFormatters(change.value.message, getter) - WeakAuras.conditionTextFormatters[id] = WeakAuras.conditionTextFormatters[id] or {} - WeakAuras.conditionTextFormatters[id][conditionNumber] = WeakAuras.conditionTextFormatters[id][conditionNumber] or {}; - WeakAuras.conditionTextFormatters[id][conditionNumber].changes = WeakAuras.conditionTextFormatters[id][conditionNumber].changes or {}; - WeakAuras.conditionTextFormatters[id][conditionNumber].changes[changeIndex] = formatters; - end - end - end - end - end -end - -local globalConditions = -{ - ["incombat"] = { - display = L["In Combat"], - type = "bool", - events = {"PLAYER_REGEN_ENABLED", "PLAYER_REGEN_DISABLED"}, - globalStateUpdate = function(state) - state.incombat = UnitAffectingCombat("player") == 1 and true or false; - end - }, - ["hastarget"] = { - display = L["Has Target"], - type = "bool", - events = {"PLAYER_TARGET_CHANGED", "PLAYER_ENTERING_WORLD"}, - globalStateUpdate = function(state) - state.hastarget = UnitExists("target") == 1 and true or false; - end - }, - ["attackabletarget"] = { - display = L["Attackable Target"], - type = "bool", - events = {"PLAYER_TARGET_CHANGED", "UNIT_FACTION"}, - globalStateUpdate = function(state) - state.attackabletarget = UnitCanAttack("player", "target") == 1 and true or false; - end - }, - ["customcheck"] = { - display = L["Custom Check"], - type = "customcheck" - } -} - -function WeakAuras.GetGlobalConditions() - return globalConditions; -end - -function WeakAuras.ConstructConditionFunction(data) - local debug = false; - if (not data.conditions or #data.conditions == 0) then - return nil; - end - - local usedProperties = {}; - - local allConditionsTemplate = WeakAuras.GetTriggerConditions(data); - allConditionsTemplate[-1] = WeakAuras.GetGlobalConditions(); - - local ret = ""; - ret = ret .. "local newActiveConditions = {};\n" - ret = ret .. "local propertyChanges = {};\n" - ret = ret .. "local nextTime;\n" - ret = ret .. "return function(region, hideRegion)\n"; - if (debug) then ret = ret .. " print('check conditions for:', region.id, region.cloneId)\n"; end - ret = ret .. " local id = region.id\n"; - ret = ret .. " local cloneId = region.cloneId or ''\n"; - ret = ret .. " local activatedConditions = WeakAuras.GetActiveConditions(id, cloneId)\n"; - ret = ret .. " wipe(newActiveConditions)\n"; - ret = ret .. " local recheckTime;\n" - ret = ret .. " local now = GetTime();\n" - - local normalConditionCount = data.conditions and #data.conditions; - -- First Loop gather which conditions are active - ret = ret .. " if (not hideRegion) then\n" - if (data.conditions) then - WeakAuras.conditionHelpers[data.uid] = nil - for conditionNumber, condition in ipairs(data.conditions) do - ret = CreateCheckCondition(data.uid, ret, condition, conditionNumber, allConditionsTemplate, debug) - end - end - ret = ret .. " end\n"; - - ret = ret .. " if (recheckTime) then\n" - ret = ret .. " WeakAuras.scheduleConditionCheck(recheckTime, id, cloneId);\n" - ret = ret .. " end\n" - - local properties = WeakAuras.GetProperties(data); - - -- Now build a property + change list - -- Second Loop deals with conditions that are no longer active - ret = ret .. " wipe(propertyChanges)\n" - if (data.conditions) then - for conditionNumber, condition in ipairs(data.conditions) do - ret = CreateDeactivateCondition(ret, condition, conditionNumber, data, properties, usedProperties, debug) - end - end - ret = ret .. "\n"; - - -- Third Loop deals with conditions that are newly active - if (data.conditions) then - for conditionNumber, condition in ipairs(data.conditions) do - ret = CreateActivateCondition(ret, data.id, condition, conditionNumber, properties, debug) - end - end - - -- Last apply changes to region - for property, _ in pairs(usedProperties) do - ret = ret .. " if(propertyChanges['" .. property .. "'] ~= nil) then\n" - local arg1 = ""; - if (properties[property].arg1) then - if (type(properties[property].arg1) == "number") then - arg1 = tostring(properties[property].arg1) .. ", "; - else - arg1 = "'" .. properties[property].arg1 .. "', "; - end - end - - local base = "region:" - local subIndex = ParseProperty(property) - if subIndex then - base = "region.subRegions[" .. subIndex .. "]:" - end - - ret = ret .. " " .. base .. properties[property].setter .. "(" .. arg1 .. formatValueForCall(properties[property].type, property) .. ")\n"; - if (debug) then ret = ret .. " print('Calling " .. properties[property].setter .. " with', " .. arg1 .. formatValueForCall(properties[property].type, property) .. ")\n"; end - ret = ret .. " end\n"; - end - ret = ret .. "end\n"; - - return ret; -end - - -local dynamicConditionsFrame = nil; - -local globalConditionAllState = { - [""] = { - show = true; - } -}; - -local globalConditionState = globalConditionAllState[""]; - -function WeakAuras.GetGlobalConditionState() - return globalConditionAllState; -end - -local function runDynamicConditionFunctions(funcs) - for id in pairs(funcs) do - if (triggerState[id] and triggerState[id].show and checkConditions[id]) then - local activeTriggerState = WeakAuras.GetTriggerStateForTrigger(id, triggerState[id].activeTrigger); - for cloneId, state in pairs(activeTriggerState) do - local region = WeakAuras.GetRegion(id, cloneId); - checkConditions[id](region, false); - end - end - end -end - -local function handleDynamicConditions(self, event) - if (globalDynamicConditionFuncs[event]) then - for i, func in ipairs(globalDynamicConditionFuncs[event]) do - func(globalConditionState); - end - end - if (dynamicConditions[event]) then - runDynamicConditionFunctions(dynamicConditions[event]); - end -end - -local lastDynamicConditionsUpdateCheck; -local function handleDynamicConditionsOnUpdate(self) - handleDynamicConditions(self, "FRAME_UPDATE"); - if (not lastDynamicConditionsUpdateCheck or GetTime() - lastDynamicConditionsUpdateCheck > 0.2) then - lastDynamicConditionsUpdateCheck = GetTime(); - handleDynamicConditions(self, "WA_SPELL_RANGECHECK"); - end -end - -local registeredGlobalFunctions = {}; - -local function EvaluateCheckForRegisterForGlobalConditions(id, check, allConditionsTemplate, register) - local trigger = check and check.trigger; - local variable = check and check.variable; - - if (trigger == -2) then - if (check.checks) then - for _, subcheck in ipairs(check.checks) do - EvaluateCheckForRegisterForGlobalConditions(id, subcheck, allConditionsTemplate, register); - end - end - elseif trigger == -1 and variable == "customcheck" then - if check.op then - for event in string.gmatch(check.op, "[%w_]+") do - if (not dynamicConditions[event]) then - register[event] = true; - dynamicConditions[event] = {}; - end - dynamicConditions[event][id] = true; - end - end - elseif (trigger and variable) then - local conditionTemplate = allConditionsTemplate[trigger] and allConditionsTemplate[trigger][variable]; - if (conditionTemplate and conditionTemplate.events) then - for _, event in ipairs(conditionTemplate.events) do - if (not dynamicConditions[event]) then - register[event] = true; - dynamicConditions[event] = {}; - end - dynamicConditions[event][id] = true; - end - - if (conditionTemplate.globalStateUpdate and not registeredGlobalFunctions[variable]) then - registeredGlobalFunctions[variable] = true; - for _, event in ipairs(conditionTemplate.events) do - globalDynamicConditionFuncs[event] = globalDynamicConditionFuncs[event] or {}; - tinsert(globalDynamicConditionFuncs[event], conditionTemplate.globalStateUpdate); - end - conditionTemplate.globalStateUpdate(globalConditionState); - end - end - end -end - -function WeakAuras.RegisterForGlobalConditions(id) - local data = WeakAuras.GetData(id); - for event, conditionFunctions in pairs(dynamicConditions) do - conditionFunctions.id = nil; - end - - local register = {}; - if (data.conditions) then - local allConditionsTemplate = WeakAuras.GetTriggerConditions(data); - allConditionsTemplate[-1] = WeakAuras.GetGlobalConditions(); - - for conditionNumber, condition in ipairs(data.conditions) do - EvaluateCheckForRegisterForGlobalConditions(id, condition.check, allConditionsTemplate, register); - end - end - - if (next(register) and not dynamicConditionsFrame) then - dynamicConditionsFrame = CreateFrame("FRAME"); - dynamicConditionsFrame:SetScript("OnEvent", handleDynamicConditions); - WeakAuras.frames["Rerun Conditions Frame"] = dynamicConditionsFrame - end - - for event in pairs(register) do - if (event == "FRAME_UPDATE" or event == "WA_SPELL_RANGECHECK") then - if (not dynamicConditionsFrame.onUpdate) then - dynamicConditionsFrame:SetScript("OnUpdate", handleDynamicConditionsOnUpdate); - dynamicConditionsFrame.onUpdate = true; - end - else - pcall(dynamicConditionsFrame.RegisterEvent, dynamicConditionsFrame, event); - end - end -end - -function WeakAuras.UnregisterForGlobalConditions(id) - for event, condFuncs in pairs(dynamicConditions) do - condFuncs[id] = nil; - end -end WeakAuras.talent_types_specific = {} function WeakAuras.CreateTalentCache() @@ -1746,7 +1027,6 @@ loadedFrame:SetScript("OnEvent", function(self, event, addon) db.displays = db.displays or {}; db.registered = db.registered or {}; - WeakAuras.UpdateCurrentInstanceType(); WeakAuras.SyncParentChildRelationships(); local isFirstUIDValidation = db.dbVersion == nil or db.dbVersion < 26; WeakAuras.ValidateUniqueDataIds(isFirstUIDValidation); @@ -1792,7 +1072,6 @@ loadedFrame:SetScript("OnEvent", function(self, event, addon) if remainingSquelch > 0 then timer:ScheduleTimer(function() squelch_actions = false; end, remainingSquelch); -- No sounds while loading end - WeakAuras.UpdateCurrentInstanceType(); end elseif(event == "SPELL_UPDATE_USABLE") then callback = WeakAuras.CreateTalentCache; @@ -1899,14 +1178,6 @@ function WeakAuras.ScanAll() WeakAuras.ReloadAll(); end -function WeakAuras.UpdateCurrentInstanceType(instanceType) - if (not IsInInstance()) then - WeakAuras.currentInstanceType = "none" - else - WeakAuras.currentInstanceType = instanceType or select (2, GetInstanceInfo()) - end -end - local pausedOptionsProcessing = false; function WeakAuras.pauseOptionsProcessing(enable) pausedOptionsProcessing = enable; @@ -1997,8 +1268,6 @@ local function scanForLoadsImpl(toCheck, event, arg1, ...) local vehicleUi = UnitHasVehicleUI("player") local size, difficulty, instanceType = GetInstanceTypeAndSize() - WeakAuras.UpdateCurrentInstanceType(instanceType) - local group = WeakAuras.GroupType() local changed = 0; @@ -2151,15 +1420,7 @@ function WeakAuras.UnloadAll() end wipe(timers); - for id in pairs(conditionChecksTimers.recheckTime) do - if (conditionChecksTimers.recheckHandle[id]) then - for _, v in pairs(conditionChecksTimers.recheckHandle[id]) do - timer:CancelTimer(v); - end - end - end - wipe(conditionChecksTimers.recheckTime); - wipe(conditionChecksTimers.recheckHandle); + Private.UnloadAllConditions() for _, triggerSystem in pairs(triggerSystems) do triggerSystem.UnloadAll(); @@ -2169,7 +1430,7 @@ end function WeakAuras.LoadDisplays(toLoad, ...) for id in pairs(toLoad) do - WeakAuras.RegisterForGlobalConditions(id); + Private.RegisterForGlobalConditions(id); triggerState[id].triggers = {}; triggerState[id].triggerCount = 0; triggerState[id].show = false; @@ -2214,14 +1475,7 @@ function WeakAuras.UnloadDisplays(toUnload, ...) timers[id] = nil; end - conditionChecksTimers.recheckTime[id] = nil; - if (conditionChecksTimers.recheckHandle[id]) then - for _, v in pairs(conditionChecksTimers.recheckHandle[id]) do - timer:CancelTimer(v); - end - end - conditionChecksTimers.recheckHandle[id] = nil; - WeakAuras.UnregisterForGlobalConditions(id); + Private.UnloadConditions(id) WeakAuras.regions[id].region:Collapse(); WeakAuras.CollapseAllClones(id); @@ -2310,14 +1564,8 @@ function WeakAuras.Delete(data) for event, eventData in pairs(loadEvents) do eventData[id] = nil end - checkConditions[id] = nil; - conditionChecksTimers.recheckTime[id] = nil; - if (conditionChecksTimers.recheckHandle[id]) then - for cloneId, v in pairs(conditionChecksTimers.recheckHandle[id]) do - timer:CancelTimer(v); - end - end - conditionChecksTimers.recheckHandle[id] = nil; + + Private.DeleteConditions(id) db.displays[id] = nil; @@ -2328,14 +1576,10 @@ function WeakAuras.Delete(data) WeakAuras.mouseFrame:delete(id); end - WeakAuras.customActionsFunctions[id] = nil; + Private.customActionsFunctions[id] = nil; WeakAuras.customConditionsFunctions[id] = nil; WeakAuras.conditionTextFormatters[id] = nil - for event, funcs in pairs(dynamicConditions) do - funcs[id] = nil; - end - WeakAuras.frameLevels[id] = nil; WeakAuras.conditionHelpers[data.uid] = nil @@ -2386,14 +1630,7 @@ function WeakAuras.Rename(data, newid) eventData[oldid] = nil end - checkConditions[newid] = checkConditions[oldid]; - checkConditions[oldid] = nil; - - conditionChecksTimers.recheckTime[newid] = conditionChecksTimers.recheckTime[oldid]; - conditionChecksTimers.recheckTime[oldid] = nil; - - conditionChecksTimers.recheckHandle[newid] = conditionChecksTimers.recheckHandle[oldid]; - conditionChecksTimers.recheckHandle[oldid] = nil; + Private.RenameConditions(oldid, newid) timers[newid] = timers[oldid]; timers[oldid] = nil; @@ -2428,18 +1665,14 @@ function WeakAuras.Rename(data, newid) end end - for key, animation in pairs(animations) do - if animation.name == oldid then - animation.name = newid; - end - end + Private.RenameAnimations(oldid, newid) if (WeakAuras.mouseFrame) then WeakAuras.mouseFrame:rename(oldid, newid); end - WeakAuras.customActionsFunctions[newid] = WeakAuras.customActionsFunctions[oldid]; - WeakAuras.customActionsFunctions[oldid] = nil; + Private.customActionsFunctions[newid] = Private.customActionsFunctions[oldid]; + Private.customActionsFunctions[oldid] = nil; WeakAuras.customConditionsFunctions[newid] = WeakAuras.customConditionsFunctions[oldid]; WeakAuras.customConditionsFunctions[oldid] = nil; @@ -2447,11 +1680,6 @@ function WeakAuras.Rename(data, newid) WeakAuras.conditionTextFormatters[newid] = WeakAuras.conditionTextFormatters[oldid] WeakAuras.conditionTextFormatters[oldid] = nil - for event, funcs in pairs(dynamicConditions) do - funcs[newid] = funcs[oldid] - funcs[oldid] = nil; - end - WeakAuras.frameLevels[newid] = WeakAuras.frameLevels[oldid]; WeakAuras.frameLevels[oldid] = nil; @@ -2714,1159 +1942,6 @@ function WeakAuras.RepairDatabase(loginAfter) WeakAuras.dynFrame:AddAction("repair", coro) end -local function ModernizeAnimation(animation) - if (type(animation) ~= "string") then - return nil; - end - return animation:gsub("^%s*return%s*", ""); -end - -local function ModernizeAnimations(animations) - if (not animations) then - return; - end - animations.alphaFunc = ModernizeAnimation(animations.alphaFunc); - animations.translateFunc = ModernizeAnimation(animations.translateFunc); - animations.scaleFunc = ModernizeAnimation(animations.scaleFunc); - animations.rotateFunc = ModernizeAnimation(animations.rotateFunc); - animations.colorFunc = ModernizeAnimation(animations.colorFunc); -end - -local modelMigration = CreateFrame("PlayerModel") - --- Takes as input a table of display data and attempts to update it to be compatible with the current version -function WeakAuras.Modernize(data) - if (not data.internalVersion) then - data.internalVersion = 1; - end - - -- Version 2 was introduced April 2018 in Legion - if (data.internalVersion < 2) then - -- Add trigger count - if not data.numTriggers then - data.numTriggers = 1 + (data.additional_triggers and #data.additional_triggers or 0) - end - - local load = data.load; - - if (not load.ingroup) then - load.ingroup = {}; - if (load.use_ingroup == true) then - load.ingroup.single = nil; - load.ingroup.multi = { - ["group"] = true, - ["raid"] = true - }; - load.use_ingroup = false; - elseif (load.use_ingroup == false) then - load.ingroup.single = "solo"; - load.ingroup.multi = {}; - load.use_ingroup = true; - end - end - - - -- Convert load options into single/multi format - for index, prototype in pairs(WeakAuras.load_prototype.args) do - local protoname = prototype.name; - if(prototype.type == "multiselect") then - if(not load[protoname] or type(load[protoname]) ~= "table") then - local value = load[protoname]; - load[protoname] = {}; - if(value) then - load[protoname].single = value; - end - end - load[protoname].multi = load[protoname].multi or {}; - elseif(load[protoname] and type(load[protoname]) == "table") then - load[protoname] = nil; - end - end - - -- upgrade from singleselecting talents to multi select, see ticket 52 - if (type(load.talent) == "number") then - local talent = load.talent; - load.talent = {}; - load.talent.single = talent; - load.talent.multi = {} - end - - - --upgrade to support custom trigger combination logic - if (data.disjunctive == true) then - data.disjunctive = "any"; - end - if(data.disjunctive == false) then - data.disjunctive = "all"; - end - - -- Change English-language class tokens to locale-agnostic versions - local class_agnosticize = { - ["Death Knight"] = "DEATHKNIGHT", - ["Druid"] = "DRUID", - ["Hunter"] = "HUNTER", - ["Mage"] = "MAGE", - ["Paladin"] = "PALADIN", - ["Priest"] = "PRIEST", - ["Rogue"] = "ROGUE", - ["Shaman"] = "SHAMAN", - ["Warlock"] = "WARLOCK", - ["Warrior"] = "WARRIOR" - }; - - if(load.class.single) then - load.class.single = class_agnosticize[load.class.single] or load.class.single; - end - - if(load.class.multi) then - for i,v in pairs(load.class.multi) do - if(class_agnosticize[i]) then - load.class.multi[class_agnosticize[i]] = true; - load.class.multi[i] = nil; - end - end - end - - -- Add dynamic text info to Progress Bars - -- Also convert custom displayText to new displayText - if(data.regionType == "aurabar") then - data.displayTextLeft = data.displayTextLeft or (not data.auto and data.displayText) or "%n"; - data.displayTextRight = data.displayTextRight or "%p"; - - if (data.barInFront ~= nil) then - data.borderInFront = not data.barInFront; - data.backdropInFront = not data.barInFront; - data.barInFront = nil; - end - end - - if(data.regionType == "icon") then - if (data.cooldownTextEnabled == nil) then - data.cooldownTextEnabled = true; - end - if (data.displayStacks) then - data.text1Enabled = true; - data.text1 = data.displayStacks; - data.displayStacks = nil; - data.text1Color = data.textColor; - data.textColor = nil; - data.text1Point = data.stacksPoint; - data.stacksPoint = nil; - data.text1Containment = data.stacksContainment; - data.stacksContainment = nil; - data.text1Font = data.font; - data.font = nil; - data.text1FontSize = data.fontSize; - data.fontSize = nil; - data.text1FontFlags = data.fontFlags; - data.fontFlags = nil; - - data.text2Enabled = false; - data.text2 = "%p"; - data.text2Color = {1, 1, 1, 1}; - data.text2Point = "CENTER"; - data.text2Containment = "INSIDE"; - data.text2Font = "Friz Quadrata TT"; - data.text2FontSize = 24; - data.text2FontFlags = "OUTLINE"; - end - end - - -- Upgrade some old variables - if data.regionType == "aurabar" then - -- "border" changed to "borderEdge" - if data.border and type(data.border) ~= "boolean" then - data.borderEdge = data.border; - data.border = data.borderEdge ~= "None"; - end - -- Multiple text settings - if data.textColor then - if not data.timerColor then - data.timerColor = {}; - data.timerColor[1] = data.textColor[1]; - data.timerColor[2] = data.textColor[2]; - data.timerColor[3] = data.textColor[3]; - data.timerColor[4] = data.textColor[4]; - end - if not data.stacksColor then - data.stacksColor = {}; - data.stacksColor[1] = data.textColor[1]; - data.stacksColor[2] = data.textColor[2]; - data.stacksColor[3] = data.textColor[3]; - data.stacksColor[4] = data.textColor[4]; - end - end - -- Multiple text settings - if data.font then - if not data.textFont then - data.textFont = data.font; - end - if not data.timerFont then - data.timerFont = data.font; - end - if not data.stacksFont then - data.stacksFont = data.font; - end - - data.font = nil; - end - -- Multiple text settings - if data.fontSize then - if not data.textSize then - data.textSize = data.fontSize; - end - if not data.timerSize then - data.timerSize = data.fontSize; - end - if not data.stacksSize then - data.stacksSize = data.fontSize; - end - - data.fontSize = nil; - end - - -- fontFlags (outline) - if not data.fontFlags then - data.fontFlags = "OUTLINE"; - end - end - - if data.regionType == "text" then - if (type(data.outline) == "boolean") then - data.outline = data.outline and "OUTLINE" or "None"; - end - end - - if data.regionType == "model" then - if (data.api == nil) then - data.api = false; - end - end - - if (not data.activeTriggerMode) then - data.activeTriggerMode = 0; - end - - if (data.sort == "hybrid") then - if (not data.hybridPosition) then - data.hybridPosition = "hybridLast"; - end - if (not data.hybridSortMode) then - data.hybridSortMode = "descending"; - end - end - - if (data.conditions) then - for conditionIndex, condition in ipairs(data.conditions) do - if (not condition.check) then - condition.check = { - ["trigger"] = condition.trigger, - ["variable"] = condition.condition, - ["op"] = condition.op, - ["value"] = condition.value - }; - condition.trigger = nil; - condition.condition = nil; - condition.op = nil; - condition.value = nil; - end - end - end - ModernizeAnimations(data.animation and data.animation.start); - ModernizeAnimations(data.animation and data.animation.main); - ModernizeAnimations(data.animation and data.animation.finish); - end -- End of V1 => V2 - - -- Version 3 was introduced April 2018 in Legion - if (data.internalVersion < 3) then - if (data.parent) then - local parentData = WeakAuras.GetData(data.parent); - if(parentData and parentData.regionType == "dynamicgroup") then - -- Version 3 allowed for offsets for dynamic groups, before that they were ignored - -- Thus reset them in the V2 to V3 upgrade - data.xOffset = 0; - data.yOffset = 0; - end - end - end - - -- Version 4 was introduced July 2018 in BfA - if (data.internalVersion < 4) then - if (data.conditions) then - for conditionIndex, condition in ipairs(data.conditions) do - if (condition.check) then - local triggernum = condition.check.trigger; - if (triggernum) then - local trigger; - if (triggernum == 0) then - trigger = data.trigger; - elseif(data.additional_triggers and data.additional_triggers[triggernum]) then - trigger = data.additional_triggers[triggernum].trigger; - end - if (trigger and trigger.event == "Cooldown Progress (Spell)") then - if (condition.check.variable == "stacks") then - condition.check.variable = "charges"; - end - end - end - end - end - end - end - - -- Version 5 was introduced July 2018 in BFA - if data.internalVersion < 5 then - -- this is to fix hybrid sorting - if data.sortHybridTable then - if data.controlledChildren then - local newSortTable = {} - for index, isHybrid in pairs(data.sortHybridTable) do - local childID = data.controlledChildren[index] - if childID then - newSortTable[childID] = isHybrid - end - end - data.sortHybridTable = newSortTable - end - end - end - - -- Version 6 was introduced July 30, 2018 in BFA - -- Changes were entirely within triggers, so no code runs here - - -- Version 7 was introduced September 1, 2018 in BFA - -- Triggers were cleaned up into a 1-indexed array - - if data.internalVersion < 7 then - - -- migrate trigger data - data.triggers = data.additional_triggers or {} - tinsert(data.triggers, 1, { - trigger = data.trigger or {}, - untrigger = data.untrigger or {}, - }) - data.additional_triggers = nil - data.trigger = nil - data.untrigger = nil - data.numTriggers = nil - data.triggers.customTriggerLogic = data.customTriggerLogic - data.customTriggerLogic = nil - local activeTriggerMode = data.activeTriggerMode or WeakAuras.trigger_modes.first_active - if activeTriggerMode ~= WeakAuras.trigger_modes.first_active then - activeTriggerMode = activeTriggerMode + 1 - end - data.triggers.activeTriggerMode = activeTriggerMode - data.activeTriggerMode = nil - data.triggers.disjunctive = data.disjunctive - data.disjunctive = nil - -- migrate condition trigger references - local function recurseRepairChecks(checks) - if not checks then return end - for _, check in pairs(checks) do - if check.trigger and check.trigger >= 0 then - check.trigger = check.trigger + 1 - end - recurseRepairChecks(check.checks) - end - end - for _, condition in pairs(data.conditions) do - if condition.check.trigger and condition.check.trigger >= 0 then - condition.check.trigger = condition.check.trigger + 1 - end - recurseRepairChecks(condition.check.checks) - end - end - - -- Version 8 was introduced in September 2018 - -- Changes are in PreAdd - - -- Version 9 was introduced in September 2018 - if data.internalVersion < 9 then - local function repairCheck(check) - if check and check.variable == "buffed" then - local trigger = check.trigger and data.triggers[check.trigger] and data.triggers[check.trigger].trigger; - if (trigger) then - if(trigger.buffShowOn == "showOnActive") then - check.variable = "show"; - elseif (trigger.buffShowOn == "showOnMissing") then - check.variable = "show"; - check.value = check.value == 0 and 1 or 0; - end - end - end - end - - local function recurseRepairChecks(checks) - if not checks then return end - for _, check in pairs(checks) do - repairCheck(check); - recurseRepairChecks(check.checks); - end - end - for _, condition in pairs(data.conditions) do - repairCheck(condition.check); - recurseRepairChecks(condition.check.checks); - end - end - - -- Version 10 is skipped, due to a bad migration script (see https://github.com/WeakAuras/WeakAuras2/pull/1091) - - -- Version 11 was introduced in January 2019 - if data.internalVersion < 11 then - if data.url and data.url ~= "" then - local slug, version = data.url:match("wago.io/([^/]+)/([0-9]+)") - if not slug and not version then - version = 1 - end - if version and tonumber(version) then - data.version = tonumber(version) - end - end - end - - -- Version 13 was introduced March 2019 in BFA - if data.internalVersion < 13 then - if data.regionType == "dynamicgroup" then - local selfPoints = { - default = "CENTER", - RIGHT = function(data) - if data.align == "LEFT" then - return "TOPLEFT" - elseif data.align == "RIGHT" then - return "BOTTOMLEFT" - else - return "LEFT" - end - end, - LEFT = function(data) - if data.align == "LEFT" then - return "TOPRIGHT" - elseif data.align == "RIGHT" then - return "BOTTOMRIGHT" - else - return "RIGHT" - end - end, - UP = function(data) - if data.align == "LEFT" then - return "BOTTOMLEFT" - elseif data.align == "RIGHT" then - return "BOTTOMRIGHT" - else - return "BOTTOM" - end - end, - DOWN = function(data) - if data.align == "LEFT" then - return "TOPLEFT" - elseif data.align == "RIGHT" then - return "TOPRIGHT" - else - return "TOP" - end - end, - HORIZONTAL = function(data) - if data.align == "LEFT" then - return "TOP" - elseif data.align == "RIGHT" then - return "BOTTOM" - else - return "CENTER" - end - end, - VERTICAL = function(data) - if data.align == "LEFT" then - return "LEFT" - elseif data.align == "RIGHT" then - return "RIGHT" - else - return "CENTER" - end - end, - CIRCLE = "CENTER", - COUNTERCIRCLE = "CENTER", - } - local selfPoint = selfPoints[data.grow or "DOWN"] or selfPoints.DOWN - if type(selfPoint) == "function" then - selfPoint = selfPoint(data) - end - data.selfPoint = selfPoint - end - end - - -- Version 14 was introduced March 2019 in BFA - if data.internalVersion < 14 then - if data.triggers then - for triggerId, triggerData in pairs(data.triggers) do - if type(triggerData) == "table" - and triggerData.trigger - and triggerData.trigger.debuffClass - and type(triggerData.trigger.debuffClass) == "string" - and triggerData.trigger.debuffClass ~= "" - then - local idx = triggerData.trigger.debuffClass - data.triggers[triggerId].trigger.debuffClass = { [idx] = true } - end - end - end - end - - -- Version 15 was introduced April 2019 in BFA - if data.internalVersion < 15 then - if data.triggers then - for triggerId, triggerData in ipairs(data.triggers) do - if triggerData.trigger.type == "status" and triggerData.trigger.event == "Spell Known" then - triggerData.trigger.use_exact_spellName = true - end - end - end - end - - -- Version 16 was introduced May 2019 in BFA - if data.internalVersion < 16 then - -- second conversion: migrate name/realm conditions to tristate - if data.load.use_name == false then - data.load.use_name = nil - end - if data.load.use_realm == false then - data.load.use_realm = nil - end - end - - -- Version 18 was a migration for stance/form trigger, but deleted later because of migration issue - - -- Version 20 was introduced July 2019 in BFA - if data.internalVersion < 20 then - if data.regionType == "icon" then - local convertPoint = function(containment, point) - if not point or point == "CENTER" then - return "CENTER" - elseif containment == "INSIDE" then - return "INNER_" .. point - elseif containment == "OUTSIDE" then - return "OUTER_" .. point - end - end - - local text1 = { - ["type"] = "subtext", - text_visible = data.text1Enabled ~= false, - text_color = data.text1Color, - text_text = data.text1, - text_font = data.text1Font, - text_fontSize = data.text1FontSize, - text_fontType = data.text1FontFlags, - text_selfPoint = "AUTO", - text_anchorPoint = convertPoint(data.text1Containment, data.text1Point), - anchorXOffset = 0, - anchorYOffset = 0, - text_shadowColor = { 0, 0, 0, 1}, - text_shadowXOffset = 0, - text_shadowYOffset = 0, - } - - local usetext2 = data.text2Enabled - - local text2 = { - ["type"] = "subtext", - text_visible = data.text2Enabled or false, - text_color = data.text2Color, - text_text = data.text2, - text_font = data.text2Font, - text_fontSize = data.text2FontSize, - text_fontType = data.text2FontFlags, - text_selfPoint = "AUTO", - text_anchorPoint = convertPoint(data.text2Containment, data.text2Point), - anchorXOffset = 0, - anchorYOffset = 0, - text_shadowColor = { 0, 0, 0, 1}, - text_shadowXOffset = 0, - text_shadowYOffset = 0, - } - - data.text1Enabled = nil - data.text1Color = nil - data.text1 = nil - data.text1Font = nil - data.text1FontSize = nil - data.text1FontFlags = nil - data.text1Containment = nil - data.text1Point = nil - - data.text2Enabled = nil - data.text2Color = nil - data.text2 = nil - data.text2Font = nil - data.text2FontSize = nil - data.text2FontFlags = nil - data.text2Containment = nil - data.text2Point = nil - - local propertyRenames = { - text1Color = "sub.1.text_color", - text1FontSize = "sub.1.text_fontSize", - text2Color = "sub.2.text_color", - text2FontSize = "sub.2.text_fontSize" - } - - tinsert(data.subRegions, text1) - if (usetext2) then - tinsert(data.subRegions, text2) - end - - if (data.conditions) then - for conditionIndex, condition in ipairs(data.conditions) do - for changeIndex, change in ipairs(condition.changes) do - if propertyRenames[change.property] then - change.property = propertyRenames[change.property] - end - end - end - end - end - end - - -- Version 20 was introduced May 2019 in BFA - if data.internalVersion < 20 then - if data.regionType == "aurabar" then - local orientationToPostion = { - HORIZONTAL_INVERSE = { "INNER_LEFT", "INNER_RIGHT" }, - HORIZONTAL = { "INNER_RIGHT", "INNER_LEFT" }, - VERTICAL_INVERSE = { "INNER_BOTTOM", "INNER_TOP" }, - VERTICAL = {"INNER_TOP", "INNER_BOTTOM"} - } - - local positions = orientationToPostion[data.orientation] or { "INNER_LEFT", "INNER_RIGHT" } - - local text1 = { - ["type"] = "subtext", - text_visible = data.timer, - text_color = data.timerColor, - text_text = data.displayTextRight, - text_font = data.timerFont, - text_fontSize = data.timerSize, - text_fontType = data.timerFlags, - text_selfPoint = "AUTO", - text_anchorPoint = positions[1], - anchorXOffset = 0, - anchorYOffset = 0, - text_shadowColor = { 0, 0, 0, 1}, - text_shadowXOffset = 1, - text_shadowYOffset = -1, - rotateText = data.rotateText - } - - local text2 = { - ["type"] = "subtext", - text_visible = data.text, - text_color = data.textColor, - text_text = data.displayTextLeft, - text_font = data.textFont, - text_fontSize = data.textSize, - text_fontType = data.textFlags, - text_selfPoint = "AUTO", - text_anchorPoint = positions[2], - anchorXOffset = 0, - anchorYOffset = 0, - text_shadowColor = { 0, 0, 0, 1}, - text_shadowXOffset = 1, - text_shadowYOffset = -1, - rotateText = data.rotateText - } - - local text3 = { - ["type"] = "subtext", - text_visible = data.stacks, - text_color = data.stacksColor, - text_text = "%s", - text_font = data.stacksFont, - text_fontSize = data.stacksSize, - text_fontType = data.stacksFlags, - text_selfPoint = "AUTO", - text_anchorPoint = "ICON_CENTER", - anchorXOffset = 0, - anchorYOffset = 0, - text_shadowColor = { 0, 0, 0, 1}, - text_shadowXOffset = 1, - text_shadowYOffset = -1, - rotateText = data.rotateText - } - - data.timer = nil - data.textColor = nil - data.displayTextRight = nil - data.textFont = nil - data.textSize = nil - data.textFlags = nil - data.text = nil - data.timerColor = nil - data.displayTextLeft = nil - data.timerFont = nil - data.timerSize = nil - data.timerFlags = nil - data.stacks = nil - data.stacksColor = nil - data.stacksFont = nil - data.stacksSize = nil - data.stacksFlags = nil - data.rotateText = nil - - local propertyRenames = { - timerColor = "sub.1.text_color", - timerSize = "sub.1.text_fontSize", - textColor = "sub.2.text_color", - textSize = "sub.2.text_fontSize", - stacksColor = "sub.3.text_color", - stacksSize = "sub.3.text_fontSize", - } - - data.subRegions = data.subRegions or {} - tinsert(data.subRegions, text1) - tinsert(data.subRegions, text2) - tinsert(data.subRegions, text3) - - if (data.conditions) then - for conditionIndex, condition in ipairs(data.conditions) do - for changeIndex, change in ipairs(condition.changes) do - if propertyRenames[change.property] then - change.property = propertyRenames[change.property] - end - end - end - end - - end - end - - if data.internalVersion < 21 then - if data.regionType == "dynamicgroup" then - data.border = data.background and data.background ~= "None" - data.borderEdge = data.border - data.borderBackdrop = data.background ~= "None" and data.background - data.borderInset = data.backgroundInset - data.background = nil - data.backgroundInset = nil - end - end - - if data.internalVersion < 22 then - if data.regionType == "aurabar" then - data.subRegions = data.subRegions or {} - - local border = { - ["type"] = "subborder", - border_visible = data.border, - border_color = data.borderColor, - border_edge = data.borderEdge, - border_offset = data.borderOffset, - border_size = data.borderSize, - border_anchor = "bar", - } - - data.border = nil - data.borderColor = nil - data.borderEdge = nil - data.borderOffset = nil - data.borderInset = nil - data.borderSize = nil - if data.borderInFront then - tinsert(data.subRegions, border) - else - tinsert(data.subRegions, 1, border) - end - - local propertyRenames = { - borderColor = "sub.".. #data.subRegions..".border_color", - } - - if (data.conditions) then - for conditionIndex, condition in ipairs(data.conditions) do - for changeIndex, change in ipairs(condition.changes) do - if propertyRenames[change.property] then - change.property = propertyRenames[change.property] - end - end - end - end - end - end - - if data.internalVersion < 23 then - if data.triggers then - for triggerId, triggerData in ipairs(data.triggers) do - local trigger = triggerData.trigger - -- Stance/Form/Aura form field type changed from type="select" to type="multiselect" - if trigger and trigger.type == "status" and trigger.event == "Stance/Form/Aura" then - local value = trigger.form - if type(value) ~= "table" then - if trigger.use_form == false then - if value then - trigger.form = { multi = { [value] = true } } - else - trigger.form = { multi = { } } - end - elseif trigger.use_form then - trigger.form = { single = value } - end - end - end - end - end - end - - if data.internalVersion < 24 then - if data.triggers then - for triggerId, triggerData in ipairs(data.triggers) do - local trigger = triggerData.trigger - if trigger and trigger.type == "status" and trigger.event == "Weapon Enchant" then - if trigger.use_inverse then - trigger.showOn = "showOnMissing" - else - trigger.showOn = "showOnActive" - end - trigger.use_inverse = nil - if not trigger.use_weapon then - trigger.use_weapon = "true" - trigger.weapon = "main" - end - end - end - end - end - - if data.internalVersion < 25 then - if data.regionType == "icon" then - data.subRegions = data.subRegions or {} - -- Need to check if glow is needed - - local prefix = "sub.".. #data.subRegions + 1 .. "." - -- For Conditions - local propertyRenames = { - glow = prefix .. "glow", - useGlowColor = prefix .. "useGlowColor", - glowColor = prefix .. "glowColor", - glowType = prefix .. "glowType", - glowLines = prefix .. "glowLines", - glowFrequency = prefix .. "glowFrequency", - glowLength = prefix .. "glowLength", - glowThickness = prefix .. "glowThickness", - glowScale = prefix .. "glowScale", - glowBorder = prefix .. "glowBorder", - glowXOffset = prefix .. "glowXOffset", - glowYOffset = prefix .. "glowYOffset", - } - - local needsGlow = data.glow - if (not needsGlow and data.conditions) then - for conditionIndex, condition in ipairs(data.conditions) do - for changeIndex, change in ipairs(condition.changes) do - if propertyRenames[change.property] then - needsGlow = true - break - end - end - end - end - - if needsGlow then - local glow = { - ["type"] = "subglow", - glow = data.glow, - useGlowColor = data.useGlowColor, - glowColor = data.glowColor, - glowType = data.glowType, - glowLines = data.glowLines, - glowFrequency = data.glowFrequency, - glowLength = data.glowLength, - glowThickness = data.glowThickness, - glowScale = data.glowScale, - glowBorder = data.glowBorder, - glowXOffset = data.glowXOffset, - glowYOffset = data.glowYOffset, - } - tinsert(data.subRegions, glow) - end - - data.glow = nil - data.useglowColor = nil - data.useGlowColor = nil - data.glowColor = nil - data.glowType = nil - data.glowLines = nil - data.glowFrequency = nil - data.glowLength = nil - data.glowThickness = nil - data.glowScale = nil - data.glowBorder = nil - data.glowXOffset = nil - data.glowYOffset = nil - - if (data.conditions) then - for conditionIndex, condition in ipairs(data.conditions) do - for changeIndex, change in ipairs(condition.changes) do - if propertyRenames[change.property] then - change.property = propertyRenames[change.property] - end - end - end - end - end - end - - if data.internalVersion < 26 then - if data.conditions then - for conditionIndex, condition in ipairs(data.conditions) do - for changeIndex, change in ipairs(condition.changes) do - if change.property == "xOffset" or change.property == "yOffset" then - change.value = (change.value or 0) - (data[change.property] or 0) - change.property = change.property .. "Relative" - end - end - end - end - end - - if data.internalVersion < 28 then - if data.actions then - if data.actions.start and data.actions.start.do_glow then - data.actions.start.glow_frame_type = "FRAMESELECTOR" - end - if data.actions.finish and data.actions.finish.do_glow then - data.actions.finish.glow_frame_type = "FRAMESELECTOR" - end - end - end - - if data.internalVersion < 29 then - if data.actions then - if data.actions.start - and data.actions.start.do_glow - and data.actions.start.glow_type == nil - then - data.actions.start.glow_type = "buttonOverlay" - end - if data.actions.finish - and data.actions.finish.do_glow - and data.actions.finish.glow_type == nil - then - data.actions.finish.glow_type = "buttonOverlay" - end - end - end - - if data.internalVersion < 30 then - local convertLegacyPrecision = function(precision) - if not precision then - return 1 - end - if precision < 4 then - return precision, false - else - return precision - 3, true - end - end - - local progressPrecision = data.progressPrecision - local totalPrecision = data.totalPrecision - if data.regionType == "text" then - local seenSymbols = {} - WeakAuras.ParseTextStr(data.displayText, function(symbol) - if not seenSymbols[symbol] then - local triggerNum, sym = string.match(symbol, "(.+)%.(.+)") - sym = sym or symbol - if sym == "p" or sym == "t" then - data["displayText_format_" .. symbol .. "_format"] = "timed" - data["displayText_format_" .. symbol .. "_time_precision"], data["displayText_format_" .. symbol .. "_time_dynamic"] - = convertLegacyPrecision(sym == "p" and progressPrecision or totalPrecision) - end - end - seenSymbols[symbol] = symbol - end) - end - - if data.subRegions then - for index, subRegionData in ipairs(data.subRegions) do - if subRegionData.type == "subtext" then - local seenSymbols = {} - WeakAuras.ParseTextStr(subRegionData.text_text, function(symbol) - if not seenSymbols[symbol] then - local triggerNum, sym = string.match(symbol, "(.+)%.(.+)") - sym = sym or symbol - if sym == "p" or sym == "t" then - subRegionData["text_text_format_" .. symbol .. "_format"] = "timed" - subRegionData["text_text_format_" .. symbol .. "_time_precision"], subRegionData["text_text_format_" .. symbol .. "_time_dynamic"] - = convertLegacyPrecision(sym == "p" and progressPrecision or totalPrecision) - end - end - seenSymbols[symbol] = symbol - end) - end - end - end - - if data.actions then - for _, when in ipairs{ "start", "finish" } do - if data.actions[when] then - local seenSymbols = {} - WeakAuras.ParseTextStr(data.actions[when].message, function(symbol) - if not seenSymbols[symbol] then - local triggerNum, sym = string.match(symbol, "(.+)%.(.+)") - sym = sym or symbol - if sym == "p" or sym == "t" then - data.actions[when]["message_format_" .. symbol .. "_format"] = "timed" - data.actions[when]["message_format_" .. symbol .. "_time_precision"], data.actions[when]["message_format_" .. symbol .. "_time_dynamic"] - = convertLegacyPrecision(sym == "p" and progressPrecision or totalPrecision) - end - end - seenSymbols[symbol] = symbol - end) - end - end - end - - if data.conditions then - for conditionIndex, condition in ipairs(data.conditions) do - for changeIndex, change in ipairs(condition.changes) do - if change.property == "chat" and change.value then - local seenSymbols = {} - WeakAuras.ParseTextStr(change.value.message, function(symbol) - if not seenSymbols[symbol] then - local triggerNum, sym = string.match(symbol, "(.+)%.(.+)") - sym = sym or symbol - if sym == "p" or sym == "t" then - change.value["message_format_" .. symbol .. "_format"] = "timed" - change.value["message_format_" .. symbol .. "_time_precision"], change.value["message_format_" .. symbol .. "_time_dynamic"] - = convertLegacyPrecision(sym == "p" and progressPrecision or totalPrecision) - end - end - seenSymbols[symbol] = symbol - end) - end - end - end - end - - -- To convert: - -- * actions - -- * conditions - data.progressPrecision = nil - data.totalPrecision = nil - end - - -- Introduced in June 2020 in Bfa - if data.internalVersion < 31 then - local allowedNames - local ignoredNames - if data.load.use_name == true and data.load.name then - allowedNames = data.load.name - elseif data.load.use_name == false and data.load.name then - ignoredNames = data.load.name - end - - if data.load.use_realm == true and data.load.realm then - allowedNames = (allowedNames or "") .. "-" .. data.load.realm - elseif data.load.use_realm == false and data.load.realm then - ignoredNames = (ignoredNames or "") .. "-" .. data.load.realm - end - - if allowedNames then - data.load.use_namerealm = true - data.load.namerealm = allowedNames - end - - if ignoredNames then - data.load.use_namerealmblack = true - data.load.namerealmblack = ignoredNames - end - - data.load.use_name = nil - data.load.name = nil - data.load.use_realm = nil - data.load.realm = nil - end - --- Introduced in June 2020 in Bfa - if data.internalVersion < 32 then - local replacements = {} - local function repairCheck(replacements, check) - if check and check.trigger then - if replacements[check.trigger] then - if replacements[check.trigger][check.variable] then - check.variable = replacements[check.trigger][check.variable] - end - end - end - end - - if data.triggers then - for triggerId, triggerData in ipairs(data.triggers) do - if triggerData.trigger.type == "status" then - local event = triggerData.trigger.event - if event == "Unit Characteristics" or event == "Health" or event == "Power" then - replacements[triggerId] = {} - replacements[triggerId]["use_name"] = "use_namerealm" - replacements[triggerId]["name"] = "namerealm" - elseif event == "Cast" then - replacements[triggerId] = {} - replacements[triggerId]["use_sourceName"] = "use_sourceNameRealm" - replacements[triggerId]["sourceName"] = "sourceNameRealm" - replacements[triggerId]["use_destName"] = "use_destNameRealm" - replacements[triggerId]["destName"] = "destNameRealm" - end - - if replacements[triggerId] then - for old, new in pairs(replacements[triggerId]) do - triggerData.trigger[new] = triggerData.trigger[old] - triggerData.trigger[old] = nil - end - - local function recurseRepairChecks(replacements, checks) - if not checks then return end - for _, check in pairs(checks) do - repairCheck(replacements, check); - recurseRepairChecks(replacements, check.checks); - end - end - for _, condition in pairs(data.conditions) do - repairCheck(replacements, condition.check); - recurseRepairChecks(replacements, condition.check.checks); - end - end - end - end - end - end - - -- Introduced in July 2020 in Bfa - if data.internalVersion < 33 then - data.load.use_ignoreNameRealm = data.load.use_namerealmblack - data.load.ignoreNameRealm = data.load.namerealmblack - data.load.use_namerealmblack = nil - data.load.namerealmblack = nil - - -- trigger.useBlackExactSpellId and trigger.blackauraspellids - if data.triggers then - for triggerId, triggerData in ipairs(data.triggers) do - triggerData.trigger.useIgnoreName = triggerData.trigger.useBlackName - triggerData.trigger.ignoreAuraNames = triggerData.trigger.blackauranames - triggerData.trigger.useIgnoreExactSpellId = triggerData.trigger.useBlackExactSpellId - triggerData.trigger.ignoreAuraSpellids = triggerData.trigger.blackauraspellids - - triggerData.trigger.useBlackName = nil - triggerData.trigger.blackauranames = nil - triggerData.trigger.useBlackExactSpellId = nil - triggerData.trigger.blackauraspellids = nil - end - end - end - - for _, triggerSystem in pairs(triggerSystems) do - triggerSystem.Modernize(data); - end - - data.internalVersion = max(data.internalVersion or 0, internalVersion); -end - function WeakAuras.ValidateUniqueDataIds(silent) -- ensure that there are no duplicated uids anywhere in the database local seenUIDs = {} @@ -4339,7 +2414,7 @@ function WeakAuras.PreAdd(data) data.subRegions = result end - WeakAuras.Modernize(data); + Private.Modernize(data); WeakAuras.validate(data, WeakAuras.data_stub); validateUserConfig(data, data.authorOptions, data.config) removeSpellNames(data) @@ -4422,16 +2497,15 @@ local function pAdd(data, simpleChange) if data.triggers.disjunctive == "custom" then triggerLogicFunc = WeakAuras.LoadFunction("return "..(data.triggers.customTriggerLogic or ""), id, "trigger combination"); end - WeakAuras.LoadCustomActionFunctions(data); - WeakAuras.LoadConditionPropertyFunctions(data); - local checkConditionsFuncStr = WeakAuras.ConstructConditionFunction(data); - local checkCondtionsFunc = checkConditionsFuncStr and WeakAuras.LoadFunction(checkConditionsFuncStr, id, "condition checks"); + LoadCustomActionFunctions(data); + Private.LoadConditionPropertyFunctions(data); + + Private.LoadConditionFunction(data) debug(id.." - Load", 1); debug(loadFuncStr); loadFuncs[id] = loadFunc; loadFuncsForOptions[id] = loadForOptionsFunc; - checkConditions[id] = checkCondtionsFunc; clones[id] = clones[id] or {}; if (timers[id]) then @@ -4605,7 +2679,7 @@ function WeakAuras.CollapseAllClones(id, triggernum) end end -function WeakAuras.SetAllStatesHidden(id, triggernum) +function Private.SetAllStatesHidden(id, triggernum) local triggerState = WeakAuras.GetTriggerStateForTrigger(id, triggernum); local changed = false for id, state in pairs(triggerState) do @@ -4616,7 +2690,7 @@ function WeakAuras.SetAllStatesHidden(id, triggernum) return changed end -function WeakAuras.SetAllStatesHiddenExcept(id, triggernum, list) +function Private.SetAllStatesHiddenExcept(id, triggernum, list) local triggerState = WeakAuras.GetTriggerStateForTrigger(id, triggernum); for cloneId, state in pairs(triggerState) do if (not (list[cloneId])) then @@ -4626,7 +2700,7 @@ function WeakAuras.SetAllStatesHiddenExcept(id, triggernum, list) end end -function WeakAuras.ReleaseClone(id, cloneId, regionType) +function Private.ReleaseClone(id, cloneId, regionType) if (not clones[id]) then return; end @@ -4870,7 +2944,7 @@ function WeakAuras.PerformActions(data, when, region) end if(actions.do_message and actions.message_type and actions.message) then - local customFunc = WeakAuras.customActionsFunctions[data.id][when .. "_message"]; + local customFunc = Private.customActionsFunctions[data.id][when .. "_message"]; WeakAuras.HandleChatAction(actions.message_type, actions.message, actions.message_dest, actions.message_channel, actions.r, actions.g, actions.b, region, customFunc, when, formatters); end @@ -4881,7 +2955,7 @@ function WeakAuras.PerformActions(data, when, region) end if(actions.do_custom and actions.custom) then - local func = WeakAuras.customActionsFunctions[data.id][when] + local func = Private.customActionsFunctions[data.id][when] if func then WeakAuras.ActivateAuraEnvironment(region.id, region.cloneId, region.state, region.states); local ok, ret = pcall(func); @@ -4909,458 +2983,6 @@ function WeakAuras.PerformActions(data, when, region) end end -local function noopErrorHandler() end - -local updatingAnimations; -local last_update = GetTime(); -function WeakAuras.UpdateAnimations() - WeakAuras.StartProfileSystem("animations"); - local errorHandler = WeakAuras.IsOptionsOpen() and noopErrorHandler or geterrorhandler() - for groupId, groupRegion in pairs(pending_controls) do - pending_controls[groupId] = nil; - groupRegion:DoPositionChildren(); - end - local time = GetTime(); - local elapsed = time - last_update; - last_update = time; - local num = 0; - for id, anim in pairs(animations) do - WeakAuras.StartProfileAura(anim.name); - num = num + 1; - local finished = false; - if(anim.duration_type == "seconds") then - if anim.duration > 0 then - anim.progress = anim.progress + (elapsed / anim.duration); - else - anim.progress = anim.progress + (elapsed / 1); - end - if(anim.progress >= 1) then - anim.progress = 1; - finished = true; - end - elseif(anim.duration_type == "relative") then - local state = anim.region.state; - if (not state - or (state.progressType == "timed" and state.duration < 0.01) - or (state.progressType == "static" and state.value < 0.01)) then - anim.progress = 0; - if(anim.type == "start" or anim.type == "finish") then - finished = true; - end - else - local relativeProgress = 0; - if(state.progressType == "static") then - relativeProgress = state.value / state.total; - elseif (state.progressType == "timed") then - relativeProgress = 1 - ((state.expirationTime - time) / state.duration); - end - relativeProgress = state.inverse and (1 - relativeProgress) or relativeProgress; - anim.progress = relativeProgress / anim.duration - local iteration = math.floor(anim.progress); - --anim.progress = anim.progress - iteration; - if not(anim.iteration) then - anim.iteration = iteration; - elseif(anim.iteration ~= iteration) then - anim.iteration = nil; - finished = true; - end - end - else - anim.progress = 1; - end - local progress = anim.inverse and (1 - anim.progress) or anim.progress; - progress = anim.easeFunc(progress, anim.easeStrength or 3) - WeakAuras.ActivateAuraEnvironment(anim.name, anim.cloneId, anim.region.state, anim.region.states); - if(anim.translateFunc) then - if (anim.region.SetOffsetAnim) then - local ok, x, y = pcall(anim.translateFunc, progress, 0, 0, anim.dX, anim.dY); - if not ok then - errorHandler(x) - else - anim.region:SetOffsetAnim(x, y); - end - else - anim.region:ClearAllPoints(); - local ok, x, y = xpcall(anim.translateFunc, progress, anim.startX, anim.startY, anim.dX, anim.dY); - if not ok then - errorHandler(x) - else - anim.region:SetPoint(anim.selfPoint, anim.anchor, anim.anchorPoint, x, y); - end - end - end - if(anim.alphaFunc) then - local ok, alpha = pcall(anim.alphaFunc, progress, anim.startAlpha, anim.dAlpha); - if not ok then - errorHandler(alpha) - else - if (anim.region.SetAnimAlpha) then - anim.region:SetAnimAlpha(alpha); - else - anim.region:SetAlpha(alpha); - end - end - end - if(anim.scaleFunc) then - local ok, scaleX, scaleY = pcall(anim.scaleFunc, progress, 1, 1, anim.scaleX, anim.scaleY); - if not ok then - errorHandler(scaleX) - else - if(anim.region.Scale) then - anim.region:Scale(scaleX, scaleY); - else - anim.region:SetWidth(anim.startWidth * scaleX); - anim.region:SetHeight(anim.startHeight * scaleY); - end - end - end - if(anim.rotateFunc and anim.region.Rotate) then - local ok, rotate = pcall(anim.rotateFunc, progress, anim.startRotation, anim.rotate); - if not ok then - errorHandler(rotate) - else - anim.region:Rotate(rotate); - end - end - if(anim.colorFunc and anim.region.ColorAnim) then - local startR, startG, startB, startA = anim.region:GetColor(); - startR, startG, startB, startA = startR or 1, startG or 1, startB or 1, startA or 1; - local ok, r, g, b, a = pcall(anim.colorFunc, progress, startR, startG, startB, startA, anim.colorR, anim.colorG, anim.colorB, anim.colorA); - if not ok then - errorHandler(r) - else - anim.region:ColorAnim(r, g, b, a); - end - end - WeakAuras.ActivateAuraEnvironment(nil); - if(finished) then - if not(anim.loop) then - if (anim.region.SetOffsetAnim) then - anim.region:SetOffsetAnim(0, 0); - else - if(anim.startX) then - anim.region:SetPoint(anim.selfPoint, anim.anchor, anim.anchorPoint, anim.startX, anim.startY); - end - end - if (anim.region.SetAnimAlpha) then - anim.region:SetAnimAlpha(nil); - elseif(anim.startAlpha) then - anim.region:SetAlpha(anim.startAlpha); - end - if(anim.startWidth) then - if(anim.region.Scale) then - anim.region:Scale(1, 1); - else - anim.region:SetWidth(anim.startWidth); - anim.region:SetHeight(anim.startHeight); - end - end - if(anim.startRotation) then - if(anim.region.Rotate) then - anim.region:Rotate(anim.startRotation); - end - end - if(anim.region.ColorAnim) then - anim.region:ColorAnim(nil); - end - animations[id] = nil; - end - - if(anim.loop) then - WeakAuras.Animate(anim.namespace, anim.data, anim.type, anim.anim, anim.region, anim.inverse, anim.onFinished, anim.loop, anim.cloneId); - elseif(anim.onFinished) then - anim.onFinished(); - end - end - WeakAuras.StopProfileAura(anim.name); - end - -- XXX: I tried to have animations only update if there are actually animation data to animate upon. - -- This caused all start animations to break, and I couldn't figure out why. - -- May revisit at a later time. - --[[ - if(num == 0) then - WeakAuras.debug("Animation stopped", 3); - frame:SetScript("OnUpdate", nil); - updatingAnimations = nil; - updatingAnimations = nil; - end - ]]-- - - WeakAuras.StopProfileSystem("animations"); -end - -function WeakAuras.RegisterGroupForPositioning(id, region) - pending_controls[id] = region - updatingAnimations = true - frame:SetScript("OnUpdate", WeakAuras.UpdateAnimations) -end - -function WeakAuras.Animate(namespace, data, type, anim, region, inverse, onFinished, loop, cloneId) - local id = data.id; - local key = tostring(region); - local valid; - if(anim and anim.type == "custom" and (anim.use_translate or anim.use_alpha or (anim.use_scale and region.Scale) or (anim.use_rotate and region.Rotate) or (anim.use_color and region.Color))) then - valid = true; - elseif(anim and anim.type == "preset" and anim.preset and anim_presets[anim.preset]) then - anim = anim_presets[anim.preset]; - valid = true; - end - if(valid) then - local progress, duration, selfPoint, anchor, anchorPoint, startX, startY, startAlpha, startWidth, startHeight, startRotation, easeType, easeStrength; - local translateFunc, alphaFunc, scaleFunc, rotateFunc, colorFunc, easeFunc; - if(animations[key]) then - if(animations[key].type == type and not loop) then - return "no replace"; - end - anim.x = anim.x or 0; - anim.y = anim.y or 0; - selfPoint, anchor, anchorPoint, startX, startY = animations[key].selfPoint, animations[key].anchor, animations[key].anchorPoint, animations[key].startX, animations[key].startY; - anim.alpha = anim.alpha or 0; - startAlpha = animations[key].startAlpha; - anim.scalex = anim.scalex or 1; - anim.scaley = anim.scaley or 1; - startWidth, startHeight = animations[key].startWidth, animations[key].startHeight; - anim.rotate = anim.rotate or 0; - startRotation = animations[key].startRotation; - anim.colorR = anim.colorR or 1; - anim.colorG = anim.colorG or 1; - anim.colorB = anim.colorB or 1; - anim.colorA = anim.colorA or 1; - else - anim.x = anim.x or 0; - anim.y = anim.y or 0; - if not region.SetOffsetAnim then - selfPoint, anchor, anchorPoint, startX, startY = region:GetPoint(1); - end - anim.alpha = anim.alpha or 0; - startAlpha = region:GetAlpha(); - anim.scalex = anim.scalex or 1; - anim.scaley = anim.scaley or 1; - startWidth, startHeight = region:GetWidth(), region:GetHeight(); - anim.rotate = anim.rotate or 0; - startRotation = region.GetRotation and region:GetRotation() or 0; - anim.colorR = anim.colorR or 1; - anim.colorG = anim.colorG or 1; - anim.colorB = anim.colorB or 1; - anim.colorA = anim.colorA or 1; - end - - if(anim.use_translate) then - if not(anim.translateType == "custom" and anim.translateFunc) then - anim.translateType = anim.translateType or "straightTranslate"; - anim.translateFunc = anim_function_strings[anim.translateType] - end - if (anim.translateFunc) then - translateFunc = WeakAuras.LoadFunction("return " .. anim.translateFunc, id, "translate animation"); - else - if (region.SetOffsetAnim) then - region:SetOffsetAnim(0, 0); - else - region:SetPoint(selfPoint, anchor, anchorPoint, startX, startY); - end - end - else - if (region.SetOffsetAnim) then - region:SetOffsetAnim(0, 0); - else - region:SetPoint(selfPoint, anchor, anchorPoint, startX, startY); - end - end - if(anim.use_alpha) then - if not(anim.alphaType == "custom" and anim.alphaFunc) then - anim.alphaType = anim.alphaType or "straight"; - anim.alphaFunc = anim_function_strings[anim.alphaType] - end - if (anim.alphaFunc) then - alphaFunc = WeakAuras.LoadFunction("return " .. anim.alphaFunc, id, "alpha animation"); - else - if (region.SetAnimAlpha) then - region:SetAnimAlpha(nil); - else - region:SetAlpha(startAlpha); - end - end - else - if (region.SetAnimAlpha) then - region:SetAnimAlpha(nil); - else - region:SetAlpha(startAlpha); - end - end - if(anim.use_scale) then - if not(anim.scaleType == "custom" and anim.scaleFunc) then - anim.scaleType = anim.scaleType or "straightScale"; - anim.scaleFunc = anim_function_strings[anim.scaleType] - end - if (anim.scaleFunc) then - scaleFunc = WeakAuras.LoadFunction("return " .. anim.scaleFunc, id, "scale animation"); - else - region:Scale(1, 1); - end - elseif(region.Scale) then - region:Scale(1, 1); - end - if(anim.use_rotate) then - if not(anim.rotateType == "custom" and anim.rotateFunc) then - anim.rotateType = anim.rotateType or "straight"; - anim.rotateFunc = anim_function_strings[anim.rotateType] - end - if (anim.rotateFunc) then - rotateFunc = WeakAuras.LoadFunction("return " .. anim.rotateFunc, id, "rotate animation"); - else - region:Rotate(startRotation); - end - elseif(region.Rotate) then - region:Rotate(startRotation); - end - if(anim.use_color) then - if not(anim.colorType == "custom" and anim.colorFunc) then - anim.colorType = anim.colorType or "straightColor"; - anim.colorFunc = anim_function_strings[anim.colorType] - end - if (anim.colorFunc) then - colorFunc = WeakAuras.LoadFunction("return " .. anim.colorFunc, id, "color animation"); - else - region:ColorAnim(nil); - end - elseif(region.ColorAnim) then - region:ColorAnim(nil); - end - easeFunc = WeakAuras.anim_ease_functions[anim.easeType or "none"] - - duration = WeakAuras.ParseNumber(anim.duration) or 0; - progress = 0; - if(namespace == "display" and type == "main" and not onFinished and not anim.duration_type == "relative") then - local data = WeakAuras.GetData(id); - if(data and data.parent) then - local parentRegion = regions[data.parent].region; - if(parentRegion and parentRegion.controlledRegions) then - for index, regionData in pairs(parentRegion.controlledRegions) do - local childRegion = regionData.region; - local childKey = regionData.key; - if(childKey and childKey ~= tostring(region) and animations[childKey] and animations[childKey].type == "main" and duration == animations[childKey].duration) then - progress = animations[childKey].progress; - break; - end - end - end - end - end - - local animation = animations[key] or {} - animations[key] = animation - - animation.progress = progress - animation.startX = startX - animation.startY = startY - animation.startAlpha = startAlpha - animation.startWidth = startWidth - animation.startHeight = startHeight - animation.startRotation = startRotation - animation.dX = (anim.use_translate and anim.x) - animation.dY = (anim.use_translate and anim.y) - animation.dAlpha = (anim.use_alpha and (anim.alpha - startAlpha)) - animation.scaleX = (anim.use_scale and anim.scalex) - animation.scaleY = (anim.use_scale and anim.scaley) - animation.rotate = anim.rotate - animation.colorR = (anim.use_color and anim.colorR) - animation.colorG = (anim.use_color and anim.colorG) - animation.colorB = (anim.use_color and anim.colorB) - animation.colorA = (anim.use_color and anim.colorA) - animation.translateFunc = translateFunc - animation.alphaFunc = alphaFunc - animation.scaleFunc = scaleFunc - animation.rotateFunc = rotateFunc - animation.colorFunc = colorFunc - animation.region = region - animation.selfPoint = selfPoint - animation.anchor = anchor - animation.anchorPoint = anchorPoint - animation.duration = duration - animation.duration_type = anim.duration_type or "seconds" - animation.inverse = inverse - animation.easeType = anim.easeType - animation.easeFunc = easeFunc - animation.easeStrength = anim.easeStrength - animation.type = type - animation.loop = loop - animation.onFinished = onFinished - animation.name = id - animation.cloneId = cloneId or "" - animation.namespace = namespace; - animation.data = data; - animation.anim = anim; - - if not(updatingAnimations) then - frame:SetScript("OnUpdate", WeakAuras.UpdateAnimations); - updatingAnimations = true; - end - return true; - else - if(animations[key]) then - if(animations[key].type ~= type or loop) then - WeakAuras.CancelAnimation(region, true, true, true, true, true); - end - end - return false; - end -end - -function WeakAuras.IsAnimating(region) - local key = tostring(region); - local anim = animations[key]; - if(anim) then - return anim.type; - else - return nil; - end -end - -function WeakAuras.CancelAnimation(region, resetPos, resetAlpha, resetScale, resetRotation, resetColor, doOnFinished) - local key = tostring(region); - local anim = animations[key]; - - if(anim) then - if(resetPos) then - if (anim.region.SetOffsetAnim) then - anim.region:SetOffsetAnim(0, 0); - else - anim.region:ClearAllPoints(); - anim.region:SetPoint(anim.selfPoint, anim.anchor, anim.anchorPoint, anim.startX, anim.startY); - end - end - if(resetAlpha) then - if (anim.region.SetAnimAlpha) then - anim.region:SetAnimAlpha(nil); - else - anim.region:SetAlpha(anim.startAlpha); - end - end - if(resetScale) then - if(anim.region.Scale) then - anim.region:Scale(1, 1); - else - anim.region:SetWidth(anim.startWidth); - anim.region:SetHeight(anim.startHeight); - end - end - if(resetRotation and anim.region.Rotate) then - anim.region:Rotate(anim.startRotation); - end - if(resetColor and anim.region.ColorAnim) then - anim.region:ColorAnim(nil); - end - - animations[key] = nil; - if(doOnFinished and anim.onFinished) then - anim.onFinished(); - end - return true; - else - return false; - end -end - function WeakAuras.GetData(id) return id and db.displays[id]; end @@ -5544,44 +3166,6 @@ function WeakAuras.CanShowStackInfo(data) end end -function WeakAuras.CorrectSpellName(input) - local inputId = tonumber(input); - if(inputId) then - local name = GetSpellInfo(inputId); - if(name) then - return inputId; - else - return nil; - end - elseif(input) then - local link; - if(input:sub(1,1) == "\124") then - link = input; - else - link = GetSpellLink(input); - end - if(link) and link ~= "" then - local itemId = link:match("spell:(%d+)"); - return tonumber(itemId); - end - end -end - -function WeakAuras.CorrectItemName(input) - local inputId = tonumber(input); - if(inputId) then - return inputId; - elseif(input) then - local _, link = GetItemInfo(input); - if(link) then - local itemId = link:match("item:(%d+)"); - return tonumber(itemId); - else - return nil; - end - end -end - local currentTooltipRegion; local currentTooltipOwner; function WeakAuras.UpdateMouseoverTooltip(region) @@ -5909,7 +3493,7 @@ do for id, states in pairs(triggerState) do local changed for triggernum in ipairs(states) do - changed = WeakAuras.SetAllStatesHidden(id, triggernum) or changed + changed = Private.SetAllStatesHidden(id, triggernum) or changed end if changed then WeakAuras.UpdatedTriggerState(id) @@ -5937,7 +3521,7 @@ do if triggerState[id] then local changed = false for triggernum, state in ipairs(triggerState[id]) do - changed = WeakAuras.SetAllStatesHidden(id, triggernum) or changed + changed = Private.SetAllStatesHidden(id, triggernum) or changed end if changed then WeakAuras.UpdatedTriggerState(id) @@ -5952,7 +3536,7 @@ do local data = WeakAuras.GetData(id) if (data) then for triggernum, trigger in ipairs(data.triggers) do - WeakAuras.SetAllStatesHidden(id, triggernum) + Private.SetAllStatesHidden(id, triggernum) local triggerSystem = WeakAuras.GetTriggerSystem(data, triggernum) if triggerSystem and triggerSystem.CreateFakeStates then triggerSystem.CreateFakeStates(id, triggernum) @@ -6110,9 +3694,7 @@ local function ApplyStatesToRegions(id, activeTrigger, states) if (applyChanges) then ApplyStateToRegion(id, cloneId, region, parent); - if (checkConditions[id]) then - checkConditions[id](region, not state.show); - end + Private.RunConditions(region, id, not state.show) end end end @@ -6588,11 +4170,15 @@ function WeakAuras.CreateFormatters(input, getter) return formatters end -function WeakAuras.IsTriggerActive(id) +function Private.IsAuraActive(id) local active = triggerState[id]; return active and active.show; end +function Private.ActiveTrigger(id) + return triggerState[id] and triggerState[id].activeTrigger +end + -- Attach to Cursor/Frames code -- Very simple function to convert a hsv angle to a color with -- value hardcoded to 1 and saturation hardcoded to 0.75 diff --git a/WeakAuras/WeakAuras.toc b/WeakAuras/WeakAuras.toc index 52280ea..dacf8f8 100644 --- a/WeakAuras/WeakAuras.toc +++ b/WeakAuras/WeakAuras.toc @@ -34,6 +34,9 @@ Profiling.lua WeakAuras.lua History.lua Transmission.lua +Modernize.lua +Animations.lua +Conditions.lua # trigger systems BuffTrigger.lua diff --git a/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasToolbarButton.lua b/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasToolbarButton.lua index 9dfda0a..5adf881 100644 --- a/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasToolbarButton.lua +++ b/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasToolbarButton.lua @@ -37,7 +37,7 @@ local methods = { ["OnAcquire"] = function(self) -- restore default values self:SetHeight(16) - self:SetWidth(32) + self:SetWidth(16) self:SetDisabled(false) self:SetText() self.hTex:SetVertexColor(1, 1, 1, 0.1) @@ -47,7 +47,11 @@ local methods = { ["SetText"] = function(self, text) self.text:SetText(text) - self:SetWidth(self.text:GetStringWidth() + 24) + if text ~= "" then + self:SetWidth(self.text:GetStringWidth() + 28) + else + self:SetWidth(16) + end end, ["SetDisabled"] = function(self, disabled) diff --git a/WeakAurasOptions/BuffTrigger.lua b/WeakAurasOptions/BuffTrigger.lua index 5522388..7b0dba0 100644 --- a/WeakAurasOptions/BuffTrigger.lua +++ b/WeakAurasOptions/BuffTrigger.lua @@ -56,11 +56,19 @@ local function getAuraMatchesList(name) end end +local noop = function() end + local function GetBuffTriggerOptions(data, triggernum) local trigger= data.triggers[triggernum].trigger local spellCache = WeakAuras.spellCache; local ValidateNumeric = WeakAuras.ValidateNumeric; local aura_options = { + deleteNote = { + type = "description", + order = 8, + name = L["Note: The legacy buff trigger is now permanently disabled. It will be removed in the near future."], + fontSize = "large" + }, convertToBuffTrigger2SpaceBeforeDesc = { type = "description", width = 0.4, @@ -90,20 +98,12 @@ local function GetBuffTriggerOptions(data, triggernum) width = 0.3, order = 8.4, name = "", - hidden = function() - -- For those that update without restarting - return not WeakAuras.CanConvertBuffTrigger2 - end, }, convertToBuffTrigger2 = { type = "execute", width = WeakAuras.doubleWidth - 0.6, name = L["Convert to New Aura Trigger"], order = 8.5, - hidden = function() - -- For those that update without restarting - return not WeakAuras.CanConvertBuffTrigger2 - end, disabled = function() if (not WeakAuras.CanConvertBuffTrigger2) then return true; @@ -136,20 +136,15 @@ local function GetBuffTriggerOptions(data, triggernum) name = L["Use Full Scan (High CPU)"], width = WeakAuras.doubleWidth, order = 9, + set = noop }, autoclone = { type = "toggle", name = L["Show all matches (Auto-clone)"], width = WeakAuras.doubleWidth, hidden = function() return not (trigger.type == "aura" and trigger.fullscan); end, - set = function(info, v) - trigger.autoclone = v; - if(v == true) then - WeakAuras.ShowCloneDialog(data); - end - WeakAuras.Add(data); - end, - order = 9.5 + order = 9.5, + set = noop }, useName = { type = "toggle", @@ -158,14 +153,16 @@ local function GetBuffTriggerOptions(data, triggernum) order = 10, hidden = function() return not (trigger.type == "aura" and not trigger.fullscan and trigger.unit ~= "multi"); end, disabled = true, - get = function() return true end + get = function() return true end, + set = noop }, use_name = { type = "toggle", width = WeakAuras.normalWidth, name = L["Aura Name"], order = 10, - hidden = function() return not (trigger.type == "aura" and trigger.fullscan); end + hidden = function() return not (trigger.type == "aura" and trigger.fullscan); end, + set = noop }, name_operator = { type = "select", @@ -174,7 +171,8 @@ local function GetBuffTriggerOptions(data, triggernum) order = 11, disabled = function() return not trigger.use_name end, hidden = function() return not (trigger.type == "aura" and trigger.fullscan); end, - values = WeakAuras.string_operator_types + values = WeakAuras.string_operator_types, + set = noop }, name = { type = "input", @@ -183,25 +181,15 @@ local function GetBuffTriggerOptions(data, triggernum) order = 12, disabled = function() return not trigger.use_name end, hidden = function() return not (trigger.type == "aura" and trigger.fullscan); end, - set = function(info, v) - if (tonumber(v)) then - trigger.spellId = tonumber(v); - trigger.name = nil; - else - trigger.spellId = nil; - trigger.name = v; - end - WeakAuras.Add(data); - WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - end, + set = noop }, use_tooltip = { type = "toggle", width = WeakAuras.normalWidth, name = L["Tooltip"], order = 13, - hidden = function() return not (trigger.type == "aura" and trigger.fullscan and trigger.unit ~= "multi"); end + hidden = function() return not (trigger.type == "aura" and trigger.fullscan and trigger.unit ~= "multi"); end, + set = noop }, tooltip_operator = { type = "select", @@ -210,7 +198,8 @@ local function GetBuffTriggerOptions(data, triggernum) order = 14, disabled = function() return not trigger.use_tooltip end, hidden = function() return not (trigger.type == "aura" and trigger.fullscan and trigger.unit ~= "multi"); end, - values = WeakAuras.string_operator_types + values = WeakAuras.string_operator_types, + set = noop }, tooltip = { type = "input", @@ -218,7 +207,8 @@ local function GetBuffTriggerOptions(data, triggernum) name = L["Tooltip"], order = 15, disabled = function() return not trigger.use_tooltip end, - hidden = function() return not (trigger.type == "aura" and trigger.fullscan and trigger.unit ~= "multi"); end + hidden = function() return not (trigger.type == "aura" and trigger.fullscan and trigger.unit ~= "multi"); end, + set = noop }, use_stealable = { type = "toggle", @@ -237,23 +227,15 @@ local function GetBuffTriggerOptions(data, triggernum) elseif(value == false) then return "false"; else return "true"; end end, - set = function(info, v) - if(v) then - trigger.use_stealable = true; - else - local value = trigger.use_stealable; - if(value == false) then trigger.use_stealable = nil; - else trigger.use_stealable = false end - end - WeakAuras.Add(data); - end + set = noop }, use_spellId = { type = "toggle", width = WeakAuras.normalWidth, name = L["Spell ID"], order = 17, - hidden = function() return not (trigger.type == "aura" and trigger.fullscan and trigger.unit ~= "multi"); end + hidden = function() return not (trigger.type == "aura" and trigger.fullscan and trigger.unit ~= "multi"); end, + set = noop }, spellId = { type = "input", @@ -261,14 +243,16 @@ local function GetBuffTriggerOptions(data, triggernum) name = L["Spell ID"], order = 18, disabled = function() return not trigger.use_spellId end, - hidden = function() return not (trigger.type == "aura" and trigger.fullscan and trigger.unit ~= "multi"); end + hidden = function() return not (trigger.type == "aura" and trigger.fullscan and trigger.unit ~= "multi"); end, + set = noop }, use_debuffClass = { type = "toggle", width = WeakAuras.normalWidth, name = L["Debuff Type"], order = 19, - hidden = function() return not (trigger.type == "aura" and trigger.fullscan); end + hidden = function() return not (trigger.type == "aura" and trigger.fullscan); end, + set = noop }, debuffClass = { type = "select", @@ -277,7 +261,8 @@ local function GetBuffTriggerOptions(data, triggernum) order = 20, disabled = function() return not trigger.use_debuffClass end, hidden = function() return not (trigger.type == "aura" and trigger.fullscan); end, - values = WeakAuras.debuff_class_types + values = WeakAuras.debuff_class_types, + set = noop }, multiuse_name = { type = "toggle", @@ -286,7 +271,8 @@ local function GetBuffTriggerOptions(data, triggernum) order = 10, hidden = function() return not (trigger.type == "aura" and not trigger.fullscan and trigger.unit == "multi"); end, disabled = true, - get = function() return true end + get = function() return true end, + set = noop }, multiicon = { type = "execute", @@ -298,7 +284,8 @@ local function GetBuffTriggerOptions(data, triggernum) return icon and tostring(icon) or "", 18, 18 end, order = 11, disabled = function() return not trigger.name and spellCache.GetIcon(trigger.name) end, - hidden = function() return not (trigger.type == "aura" and not trigger.fullscan and trigger.unit == "multi"); end + hidden = function() return not (trigger.type == "aura" and not trigger.fullscan and trigger.unit == "multi"); end, + set = noop }, multiname = { type = "input", @@ -308,17 +295,7 @@ local function GetBuffTriggerOptions(data, triggernum) order = 12, hidden = function() return not (trigger.type == "aura" and not trigger.fullscan and trigger.unit == "multi"); end, get = function(info) return trigger.spellId and tostring(trigger.spellId) or trigger.name end, - set = function(info, v) - if(v == "") then - trigger.name = nil; - trigger.spellId = nil; - else - trigger.name, trigger.spellId = WeakAuras.spellCache.CorrectAuraName(v); - end - WeakAuras.Add(data); - WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - end, + set = noop }, name1icon = { type = "execute", @@ -331,7 +308,8 @@ local function GetBuffTriggerOptions(data, triggernum) end, order = 11, disabled = function() return not spellCache.GetIcon(trigger.names[1]) end, - hidden = function() return not (trigger.type == "aura" and not trigger.fullscan and trigger.unit ~= "multi"); end + hidden = function() return not (trigger.type == "aura" and not trigger.fullscan and trigger.unit ~= "multi"); end, + set = noop }, name1 = { type = "input", @@ -341,19 +319,7 @@ local function GetBuffTriggerOptions(data, triggernum) order = 12, hidden = function() return not (trigger.type == "aura" and not trigger.fullscan and trigger.unit ~= "multi"); end, get = function(info) return trigger.spellIds[1] and tostring(trigger.spellIds[1]) or trigger.names[1] end, - set = function(info, v) - if(v == "") then - if(trigger.names[1]) then - tremove(trigger.names, 1); - spellId_tremove(trigger.spellIds, 1); - end - else - trigger.names[1], trigger.spellIds[1] = WeakAuras.spellCache.CorrectAuraName(v); - end - WeakAuras.Add(data); - WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - end, + set = noop }, name2space = { type = "execute", @@ -383,19 +349,7 @@ local function GetBuffTriggerOptions(data, triggernum) name = "", hidden = function() return not (trigger.type == "aura" and trigger.names[1] and not trigger.fullscan and trigger.unit ~= "multi"); end, get = function(info) return trigger.spellIds[2] and tostring(trigger.spellIds[2]) or trigger.names[2] end, - set = function(info, v) - if(v == "") then - if(trigger.names[2]) then - tremove(trigger.names, 2); - spellId_tremove(trigger.spellIds, 2); - end - else - trigger.names[2], trigger.spellIds[2] = WeakAuras.spellCache.CorrectAuraName(v); - end - WeakAuras.Add(data); - WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - end, + set = noop }, name3space = { type = "execute", @@ -425,19 +379,7 @@ local function GetBuffTriggerOptions(data, triggernum) name = "", hidden = function() return not (trigger.type == "aura" and trigger.names[2] and not trigger.fullscan and trigger.unit ~= "multi"); end, get = function(info) return trigger.spellIds[3] and tostring(trigger.spellIds[3]) or trigger.names[3] end, - set = function(info, v) - if(v == "") then - if(trigger.names[3]) then - tremove(trigger.names, 3); - spellId_tremove(trigger.spellIds, 3); - end - else - trigger.names[3], trigger.spellIds[3] = WeakAuras.spellCache.CorrectAuraName(v); - end - WeakAuras.Add(data); - WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - end, + set = noop }, name4space = { type = "execute", @@ -466,19 +408,7 @@ local function GetBuffTriggerOptions(data, triggernum) name = "", hidden = function() return not (trigger.type == "aura" and trigger.names[3] and not trigger.fullscan and trigger.unit ~= "multi"); end, get = function(info) return trigger.spellIds[4] and tostring(trigger.spellIds[4]) or trigger.names[4] end, - set = function(info, v) - if(v == "") then - if(trigger.names[4]) then - tremove(trigger.names, 4); - spellId_tremove(trigger.spellIds, 4); - end - else - trigger.names[4], trigger.spellIds[4] = WeakAuras.spellCache.CorrectAuraName(v); - end - WeakAuras.Add(data); - WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - end, + set = noop }, name5space = { type = "execute", @@ -508,19 +438,7 @@ local function GetBuffTriggerOptions(data, triggernum) name = "", hidden = function() return not (trigger.type == "aura" and trigger.names[4] and not trigger.fullscan and trigger.unit ~= "multi"); end, get = function(info) return trigger.spellIds[5] and tostring(trigger.spellIds[5]) or trigger.names[5] end, - set = function(info, v) - if(v == "") then - if(trigger.names[5]) then - tremove(trigger.names, 5); - spellId_tremove(trigger.spellIds, 5); - end - else - trigger.names[5], trigger.spellIds[5] = WeakAuras.spellCache.CorrectAuraName(v); - end - WeakAuras.Add(data); - WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - end, + set = noop }, name6space = { type = "execute", @@ -550,19 +468,7 @@ local function GetBuffTriggerOptions(data, triggernum) name = "", hidden = function() return not (trigger.type == "aura" and trigger.names[5] and not trigger.fullscan and trigger.unit ~= "multi"); end, get = function(info) return trigger.spellIds[6] and tostring(trigger.spellIds[6]) or trigger.names[6] end, - set = function(info, v) - if(v == "") then - if(trigger.names[6]) then - tremove(trigger.names, 6); - spellId_tremove(trigger.spellIds, 6); - end - else - trigger.names[6], trigger.spellIds[6] = WeakAuras.spellCache.CorrectAuraName(v); - end - WeakAuras.Add(data); - WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - end, + set = noop }, name7space = { type = "execute", @@ -592,19 +498,7 @@ local function GetBuffTriggerOptions(data, triggernum) name = "", hidden = function() return not (trigger.type == "aura" and trigger.names[6] and not trigger.fullscan and trigger.unit ~= "multi"); end, get = function(info) return trigger.spellIds[7] and tostring(trigger.spellIds[7]) or trigger.names[7] end, - set = function(info, v) - if(v == "") then - if(trigger.names[7]) then - tremove(trigger.names, 7); - spellId_tremove(trigger.spellIds, 7); - end - else - trigger.names[7], trigger.spellIds[7] = WeakAuras.spellCache.CorrectAuraName(v); - end - WeakAuras.Add(data); - WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - end, + set = noop }, name8space = { type = "execute", @@ -634,19 +528,7 @@ local function GetBuffTriggerOptions(data, triggernum) name = "", hidden = function() return not (trigger.type == "aura" and trigger.names[7] and not trigger.fullscan and trigger.unit ~= "multi"); end, get = function(info) return trigger.spellIds[8] and tostring(trigger.spellIds[8]) or trigger.names[8] end, - set = function(info, v) - if(v == "") then - if(trigger.names[8]) then - tremove(trigger.names, 8); - spellId_tremove(trigger.spellIds, 8); - end - else - trigger.names[8], trigger.spellIds[8] = WeakAuras.spellCache.CorrectAuraName(v); - end - WeakAuras.Add(data); - WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - end, + set = noop }, name9space = { type = "execute", @@ -676,19 +558,7 @@ local function GetBuffTriggerOptions(data, triggernum) name = "", hidden = function() return not (trigger.type == "aura" and trigger.names[8] and not trigger.fullscan and trigger.unit ~= "multi"); end, get = function(info) return trigger.spellIds[9] and tostring(trigger.spellIds[9]) or trigger.names[9] end, - set = function(info, v) - if(v == "") then - if(trigger.names[9]) then - tremove(trigger.names, 9); - spellId_tremove(trigger.spellIds, 9); - end - else - trigger.names[9], trigger.spellIds[9] = WeakAuras.spellCache.CorrectAuraName(v); - end - WeakAuras.Add(data); - WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - end, + set = noop }, useUnit = { type = "toggle", @@ -697,7 +567,8 @@ local function GetBuffTriggerOptions(data, triggernum) order = 40, disabled = true, hidden = function() return not (trigger.type == "aura"); end, - get = function() return true end + get = function() return true end, + set = noop }, unit = { type = "select", @@ -712,20 +583,13 @@ local function GetBuffTriggerOptions(data, triggernum) end end, hidden = function() return not (trigger.type == "aura"); end, - set = function(info, v) - trigger.unit = v; - trigger.use_specific_unit = (v == "member"); - if(v == "multi") then - WeakAuras.ShowCloneDialog(data); - end - WeakAuras.Add(data); - end, get = function() if(trigger.fullscan and (trigger.unit == "group" or trigger.unit == "multi")) then trigger.unit = "player"; end return trigger.unit; - end + end, + set = noop }, useSpecificUnit = { type = "toggle", @@ -734,7 +598,8 @@ local function GetBuffTriggerOptions(data, triggernum) order = 42, disabled = true, hidden = function() return not (trigger.type == "aura" and trigger.unit == "member") end, - get = function() return true end + get = function() return true end, + set = noop }, specificUnit = { type = "input", @@ -742,7 +607,8 @@ local function GetBuffTriggerOptions(data, triggernum) name = L["Specific Unit"], order = 43, desc = L["Can be a Name or a Unit ID (e.g. party1). A name only works on friendly players in your group."], - hidden = function() return not (trigger.type == "aura" and trigger.unit == "member") end + hidden = function() return not (trigger.type == "aura" and trigger.unit == "member") end, + set = noop }, useGroup_count = { type = "toggle", @@ -751,7 +617,8 @@ local function GetBuffTriggerOptions(data, triggernum) disabled = true, hidden = function() return not (trigger.type == "aura" and trigger.unit == "group"); end, get = function() return true; end, - order = 45 + order = 45, + set = noop }, group_countOperator = { type = "select", @@ -760,7 +627,8 @@ local function GetBuffTriggerOptions(data, triggernum) width = WeakAuras.halfWidth, values = operator_types, hidden = function() return not (trigger.type == "aura" and trigger.unit == "group"); end, - get = function() return trigger.group_countOperator; end + get = function() return trigger.group_countOperator; end, + set = noop }, group_count = { type = "input", @@ -773,15 +641,7 @@ local function GetBuffTriggerOptions(data, triggernum) width = WeakAuras.halfWidth, hidden = function() return not (trigger.type == "aura" and trigger.unit == "group"); end, get = function() return trigger.group_count; end, - set = function(info, v) - if(WeakAuras.ParseNumber(v)) then - trigger.group_count = v; - else - trigger.group_count = ""; - end - WeakAuras.Add(data); - WeakAuras.UpdateDisplayButton(data); - end + set = noop }, ignoreSelf = { type = "toggle", @@ -789,20 +649,15 @@ local function GetBuffTriggerOptions(data, triggernum) order = 47.3, width = WeakAuras.doubleWidth, hidden = function() return not (trigger.type == "aura" and trigger.unit == "group"); end, + set = noop }, groupclone = { type = "toggle", name = L["Show all matches (Auto-clone)"], width = WeakAuras.doubleWidth, hidden = function() return not (trigger.type == "aura" and trigger.unit == "group"); end, - set = function(info, v) - trigger.groupclone = v; - if(v == true) then - WeakAuras.ShowCloneDialog(data); - end - WeakAuras.Add(data); - end, - order = 47.4 + order = 47.4, + set = noop }, name_info = { type = "select", @@ -818,7 +673,8 @@ local function GetBuffTriggerOptions(data, triggernum) return nil; end end, - values = group_aura_name_info_types + values = group_aura_name_info_types, + set = noop }, stack_info = { type = "select", @@ -834,7 +690,8 @@ local function GetBuffTriggerOptions(data, triggernum) return nil; end end, - values = group_aura_stack_info_types + values = group_aura_stack_info_types, + set = noop }, hideAlone = { type = "toggle", @@ -842,6 +699,7 @@ local function GetBuffTriggerOptions(data, triggernum) order = 47.7, width = WeakAuras.doubleWidth, hidden = function() return not (trigger.type == "aura" and trigger.unit == "group"); end, + set = noop }, useDebuffType = { type = "toggle", @@ -850,7 +708,8 @@ local function GetBuffTriggerOptions(data, triggernum) order = 50, disabled = true, hidden = function() return not (trigger.type == "aura"); end, - get = function() return true end + get = function() return true end, + set = noop }, debuffType = { type = "select", @@ -858,14 +717,16 @@ local function GetBuffTriggerOptions(data, triggernum) name = L["Aura Type"], order = 51, values = debuff_types, - hidden = function() return not (trigger.type == "aura"); end + hidden = function() return not (trigger.type == "aura"); end, + set = noop }, subcount = { type = "toggle", width = WeakAuras.doubleWidth, name = L["Use tooltip \"size\" instead of stacks"], hidden = function() return not (trigger.type == "aura" and trigger.fullscan) end, - order = 55 + order = 55, + set = noop }, subcountCount = { type = "select", @@ -873,14 +734,16 @@ local function GetBuffTriggerOptions(data, triggernum) width = WeakAuras.doubleWidth, name = L["Use nth value from tooltip:"], hidden = function() return not (trigger.type == "aura" and trigger.fullscan and trigger.subcount) end, - order = 55.5 + order = 55.5, + set = noop }, useRem = { type = "toggle", width = WeakAuras.normalWidth, name = L["Remaining Time"], hidden = function() return not (trigger.type == "aura" and trigger.unit ~= "multi"); end, - order = 56 + order = 56, + set = noop }, remOperator = { type = "select", @@ -890,7 +753,8 @@ local function GetBuffTriggerOptions(data, triggernum) values = operator_types, disabled = function() return not trigger.useRem; end, hidden = function() return not (trigger.type == "aura" and trigger.unit ~= "multi"); end, - get = function() return trigger.useRem and trigger.remOperator or nil end + get = function() return trigger.useRem and trigger.remOperator or nil end, + set = noop }, rem = { type = "input", @@ -900,14 +764,16 @@ local function GetBuffTriggerOptions(data, triggernum) width = WeakAuras.halfWidth, disabled = function() return not trigger.useRem; end, hidden = function() return not (trigger.type == "aura" and trigger.unit ~= "multi"); end, - get = function() return trigger.useRem and trigger.rem or nil end + get = function() return trigger.useRem and trigger.rem or nil end, + set = noop }, useCount = { type = "toggle", width = WeakAuras.normalWidth, name = L["Stack Count"], hidden = function() return not (trigger.type == "aura" and trigger.unit ~= "multi"); end, - order = 60 + order = 60, + set = noop }, countOperator = { type = "select", @@ -917,7 +783,8 @@ local function GetBuffTriggerOptions(data, triggernum) values = operator_types, disabled = function() return not trigger.useCount; end, hidden = function() return not (trigger.type == "aura" and trigger.unit ~= "multi"); end, - get = function() return trigger.useCount and trigger.countOperator or nil end + get = function() return trigger.useCount and trigger.countOperator or nil end, + set = noop }, count = { type = "input", @@ -927,7 +794,8 @@ local function GetBuffTriggerOptions(data, triggernum) width = WeakAuras.halfWidth, disabled = function() return not trigger.useCount; end, hidden = function() return not (trigger.type == "aura" and trigger.unit ~= "multi"); end, - get = function() return trigger.useCount and trigger.count or nil end + get = function() return trigger.useCount and trigger.count or nil end, + set = noop }, ownOnly = { type = "toggle", @@ -950,18 +818,9 @@ local function GetBuffTriggerOptions(data, triggernum) elseif(value == false) then return "false"; else return "true"; end end, - set = function(info, v) - if(v) then - trigger.ownOnly = true; - else - local value = trigger.ownOnly; - if(value == false) then trigger.ownOnly = nil; - else trigger.ownOnly = false end - end - WeakAuras.Add(data); - end, order = 70, - hidden = function() return not (trigger.type == "aura"); end + hidden = function() return not (trigger.type == "aura"); end, + set = noop }, useBuffShowOn = { type = "toggle", @@ -972,7 +831,8 @@ local function GetBuffTriggerOptions(data, triggernum) hidden = function() return not (trigger.type == "aura" and not(trigger.unit ~= "group" and trigger.fullscan and trigger.autoclone) and trigger.unit ~= "multi" and not(trigger.unit == "group" and not trigger.groupclone)); end, - get = function() return true end + get = function() return true end, + set = noop }, buffShowOn = { type = "select", @@ -983,7 +843,8 @@ local function GetBuffTriggerOptions(data, triggernum) get = function() return trigger.buffShowOn end, hidden = function() return not (trigger.type == "aura" and not(trigger.unit ~= "group" and trigger.fullscan and trigger.autoclone) and trigger.unit ~= "multi" and not(trigger.unit == "group" and not trigger.groupclone)); - end + end, + set = noop }, unitExists = { type = "toggle", @@ -996,7 +857,8 @@ local function GetBuffTriggerOptions(data, triggernum) and trigger.unit ~= "multi" and trigger.unit ~= "group" and trigger.unit ~= "player"); - end + end, + set = noop }, linespacer = { type = "description", diff --git a/WeakAurasOptions/LoadOptions.lua b/WeakAurasOptions/LoadOptions.lua index 0a843d3..7dcba22 100644 --- a/WeakAurasOptions/LoadOptions.lua +++ b/WeakAurasOptions/LoadOptions.lua @@ -34,6 +34,44 @@ local function union(table1, table2) return meta; end +local function CorrectSpellName(input) + local inputId = tonumber(input); + if(inputId) then + local name = GetSpellInfo(inputId); + if(name) then + return inputId; + else + return nil; + end + elseif(input) then + local link; + if(input:sub(1,1) == "\124") then + link = input; + else + link = GetSpellLink(input); + end + if(link) and link ~= "" then + local itemId = link:match("spell:(%d+)"); + return tonumber(itemId); + end + end +end + +local function CorrectItemName(input) + local inputId = tonumber(input); + if(inputId) then + return inputId; + elseif(input) then + local _, link = GetItemInfo(input); + if(link) then + local itemId = link:match("item:(%d+)"); + return tonumber(itemId); + else + return nil; + end + end +end + -- Also used by the GenericTrigger function WeakAuras.ConstructOptions(prototype, data, startorder, triggernum, triggertype, unevent) local trigger, untrigger; @@ -564,9 +602,9 @@ function WeakAuras.ConstructOptions(prototype, data, startorder, triggernum, tri if(arg.type == "aura") then fixedInput = WeakAuras.spellCache.CorrectAuraName(v); elseif(arg.type == "spell") then - fixedInput = WeakAuras.CorrectSpellName(v); + fixedInput = CorrectSpellName(v); elseif(arg.type == "item") then - fixedInput = WeakAuras.CorrectItemName(v); + fixedInput = CorrectItemName(v); end trigger[realname] = fixedInput; WeakAuras.Add(data); diff --git a/WeakAurasOptions/OptionsFrames/OptionsFrame.lua b/WeakAurasOptions/OptionsFrames/OptionsFrame.lua index b05840e..795d7ef 100644 --- a/WeakAurasOptions/OptionsFrames/OptionsFrame.lua +++ b/WeakAurasOptions/OptionsFrames/OptionsFrame.lua @@ -268,8 +268,8 @@ function WeakAuras.CreateFrame() title:SetPoint("BOTTOMLEFT", titleBG, "BOTTOMLEFT", -25, 0) title:SetPoint("TOPRIGHT", titleBG, "TOPRIGHT", 25, 0) - CreateFrameSizer(frame, commitWindowChanges, "BOTTOMLEFT") - CreateFrameSizer(frame, commitWindowChanges, "BOTTOMRIGHT") + frame.bottomLeftResizer = CreateFrameSizer(frame, commitWindowChanges, "BOTTOMLEFT") + frame.bottomRightResizer = CreateFrameSizer(frame, commitWindowChanges, "BOTTOMRIGHT") local minimize = CreateDecoration(frame) minimize:SetPoint("TOPRIGHT", -65, 12) @@ -292,13 +292,27 @@ function WeakAuras.CreateFrame() self.toolbarContainer.frame:Hide() self.filterInput:Hide(); self.filterInputClear:Hide(); + self.tipFrame.frame:Hide() + self.bottomLeftResizer:Hide() + self.bottomRightResizer:Hide() else + if self.tipFrameIsVisible then + self.tipFrame.frame:Show() + end + self.bottomLeftResizer:Show() + self.bottomRightResizer:Show() if self.window == "default" then self.buttonsContainer.frame:Show() self.container.frame:Show() + if self.tipFrameIsVisible then + self.tipFrame.frame:Show() + else + self.tipFrame.frame:Hide() + end else self.buttonsContainer.frame:Hide() self.container.frame:Hide() + self.tipFrame.frame:Hide() end if self.window == "texture" then @@ -394,45 +408,132 @@ function WeakAuras.CreateFrame() frame:UpdateFrameVisible() end) - local _, _, _, enabled, loadable = GetAddOnInfo("WeakAurasTutorials") - if enabled and loadable then - local tutorial = CreateDecoration(frame) - tutorial:SetPoint("TOPRIGHT", -140, 12) + local tipFrame = AceGUI:Create("SimpleGroup") + tipFrame.frame:SetParent(frame) + tipFrame:SetLayout("Flow") + tipFrame.frame:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", 22, 15) + tipFrame.frame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -17, 15) + tipFrame.frame:Hide() + frame.tipFrame = tipFrame - local tutorialbutton = CreateFrame("BUTTON", nil, tutorial) - tutorialbutton:SetWidth(30) - tutorialbutton:SetHeight(30) - tutorialbutton:SetPoint("CENTER", tutorial, "CENTER", 1, -1) - tutorialbutton:SetNormalTexture("Interface\\GossipFrame\\DailyActiveQuestIcon") - tutorialbutton:GetNormalTexture():ClearAllPoints() - tutorialbutton:GetNormalTexture():SetSize(16, 16) - tutorialbutton:GetNormalTexture():SetPoint("center", -2, 0) - tutorialbutton:SetPushedTexture("Interface\\GossipFrame\\DailyActiveQuestIcon") - tutorialbutton:GetPushedTexture():ClearAllPoints() - tutorialbutton:GetPushedTexture():SetSize(16, 16) - tutorialbutton:GetPushedTexture():SetPoint("center", -2, -2) - tutorialbutton:SetHighlightTexture("Interface\\BUTTONS\\UI-Panel-MinimizeButton-Highlight.blp") - tutorialbutton:SetScript("OnClick", function() - if not IsAddOnLoaded("WeakAurasTutorials") then - local loaded, reason = LoadAddOn("WeakAurasTutorials") - if not loaded then - reason = string.lower("|cffff2020" .. _G["ADDON_" .. reason] .. "|r.") - prettyPrint("Tutorials could not be loaded, the addon is " .. reason) - return - end - end - WeakAuras.ToggleTutorials() + local tipPopup = CreateFrame("Frame", nil, frame) + tipPopup:SetFrameStrata("FULLSCREEN") + tipPopup:SetBackdrop({ + bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", + edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", + tile = true, + tileSize = 16, + edgeSize = 16, + insets = { left = 4, right = 4, top = 4, bottom = 4 } + }) + tipPopup:SetBackdropColor(0, 0, 0, 0.8) + --tipPopup:SetHeight(100) + tipPopup:Hide() + + local tipPopupTitle = tipPopup:CreateFontString(nil, "BACKGROUND", "GameFontNormalLarge") + tipPopupTitle:SetPoint("TOPLEFT", tipPopup, "TOPLEFT", 10, -10) + tipPopupTitle:SetPoint("TOPRIGHT", tipPopup, "TOPRIGHT", -10, -10) + tipPopupTitle:SetJustifyH("LEFT") + tipPopupTitle:SetJustifyV("TOP") + + local tipPopupLabel = tipPopup:CreateFontString(nil, "BACKGROUND", "GameFontWhite") + tipPopupLabel:SetPoint("TOPLEFT", tipPopupTitle, "BOTTOMLEFT", 0, -6) + tipPopupLabel:SetPoint("TOPRIGHT", tipPopupTitle, "BOTTOMRIGHT", 0, -6) + tipPopupLabel:SetJustifyH("LEFT") + tipPopupLabel:SetJustifyV("TOP") + + local urlWidget = CreateFrame("EDITBOX", nil, tipPopup, "InputBoxTemplate") + urlWidget:SetFont(STANDARD_TEXT_FONT, 12) + urlWidget:SetPoint("TOPLEFT", tipPopupLabel, "BOTTOMLEFT", 6, 0) + urlWidget:SetPoint("TOPRIGHT", tipPopupLabel, "BOTTOMRIGHT", 0, 0) + urlWidget:SetScript("OnChar", function() urlWidget:SetText(urlWidget.text); urlWidget:HighlightText(); end); + urlWidget:SetScript("OnMouseUp", function() urlWidget:HighlightText(); end); + urlWidget:SetScript("OnEscapePressed", function() tipPopup:Hide() end) + urlWidget:SetHeight(34) + + local tipPopupCtrlC = tipPopup:CreateFontString(nil, "BACKGROUND", "GameFontWhite") + tipPopupCtrlC:SetPoint("TOPLEFT", urlWidget, "BOTTOMLEFT", -6, 0) + tipPopupCtrlC:SetPoint("TOPRIGHT", urlWidget, "BOTTOMRIGHT", 0, 0) + tipPopupCtrlC:SetJustifyH("LEFT") + tipPopupCtrlC:SetJustifyV("TOP") + tipPopupCtrlC:SetText("Press Ctrl+C to copy the URL") + + local function ToggleTip(referenceWidget, url, title, description) + if tipPopup:IsVisible() and urlWidget.text == url then + tipPopup:Hide() + return + end + urlWidget.text = url + urlWidget:SetText(url) + tipPopupTitle:SetText(title) + tipPopupLabel:SetText(description) + urlWidget:HighlightText() + + tipPopup:SetWidth(400) + tipPopup:SetHeight(26 + tipPopupTitle:GetHeight() + tipPopupLabel:GetHeight() + urlWidget:GetHeight() + tipPopupCtrlC:GetHeight()) + + tipPopup:SetPoint("BOTTOMLEFT", referenceWidget.frame, "TOPLEFT", -6, 4) + tipPopup:Show() + end + + local addFooter = function(title, texture, url, description) + local button = AceGUI:Create("WeakAurasToolbarButton") + button:SetText(title) + button:SetTexture(texture) + button:SetCallback("OnClick", function() + ToggleTip(button, url, title, description) end) + tipFrame:AddChild(button) + end + + addFooter(L["Get Help"], [[Interface\AddOns\WeakAuras\Media\Textures\discord.tga]], "https://discord.gg/wa2", + L["Chat with WeakAuras experts on our Discord server."]) + addFooter(L["Documentation"], [[Interface\AddOns\WeakAuras\Media\Textures\GitHub.tga]], "https://github.com/WeakAuras/WeakAuras2/wiki", + L["Check out our wiki for a large collection of examples and snippets."]) + addFooter(L["Find Auras"], [[Interface\AddOns\WeakAuras\Media\Textures\wagoupdate_logo.tga]], "https://wago.io", + L["Browse Wago, the largest collection of auras."]) + + if not WeakAurasCompanion then + addFooter(L["Update Auras"], [[Interface\AddOns\WeakAuras\Media\Textures\wagoupdate_refresh.tga]], "https://weakauras.wtf", + L["Keep your Wago imports up to date with the Companion App."]) + end + addFooter(L["Found a Bug?"], [[Interface\AddOns\WeakAuras\Media\Textures\bug_report.tga]], "https://github.com/WeakAuras/WeakAuras2/issues/new", + L["Report bugs our our issue tracker."]) + + -- Disable for now + --local closeTipButton = CreateFrame("Button", nil, tipFrame.frame, "UIPanelCloseButton") + --closeTipButton:SetScript("OnClick", function() + -- frame:HideTip() + --end) + --closeTipButton:SetPoint("TOPRIGHT", tipFrame.frame, "TOPRIGHT", 0, 6) + --closeTipButton:Show() + + frame.ShowTip = function(self) + self.tipFrameIsVisible = true + self.tipFrame.frame:Show() + self.buttonsContainer.frame:SetPoint("BOTTOMLEFT", self, "BOTTOMLEFT", 17, 30) + self.container.frame:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", -17, 28) + end + + frame.HideTip = function(self) + self.tipFrameIsVisible = false + self.tipFrame.frame:Hide() + self.buttonsContainer.frame:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", 17, 12) + self.container.frame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -17, 10) end -- Right Side Container local container = AceGUI:Create("InlineGroup") container.frame:SetParent(frame) - container.frame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -17, 12) - container.frame:SetPoint("TOPLEFT", frame, "TOPRIGHT", -83 - WeakAuras.normalWidth * 340, -14) + container.frame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -17, 10) + container.frame:SetPoint("TOPLEFT", frame, "TOPRIGHT", -63 - WeakAuras.normalWidth * 340, -14) container.frame:Show() --container.frame:SetClipsChildren(true) container.titletext:Hide() + -- Hide the border + container.content:GetParent():SetBackdrop(nil) + container.content:SetPoint("TOPLEFT", 0, 0) + container.content:SetPoint("BOTTOMRIGHT", 0, 0) frame.container = container frame.texturePicker = WeakAuras.TexturePicker(frame) @@ -451,7 +552,7 @@ function WeakAuras.CreateFrame() filterInput:SetScript("OnEnterPressed", function(...) filterInput:ClearFocus() end) filterInput:SetScript("OnEscapePressed", function(...) filterInput:SetText("") filterInput:ClearFocus() end) filterInput:SetHeight(15) - filterInput:SetPoint("TOP", frame, "TOP", 0, -34) + filterInput:SetPoint("TOP", frame, "TOP", 0, -44) filterInput:SetPoint("LEFT", frame, "LEFT", 24, 0) filterInput:SetPoint("RIGHT", container.frame, "LEFT", -5, 0) filterInput:SetTextInsets(16, 16, 0, 0) @@ -481,7 +582,7 @@ function WeakAuras.CreateFrame() buttonsContainer:SetWidth(170) buttonsContainer.frame:SetParent(frame) buttonsContainer.frame:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", 17, 12) - buttonsContainer.frame:SetPoint("TOP", frame, "TOP", 0, -34) + buttonsContainer.frame:SetPoint("TOP", frame, "TOP", 0, -46) buttonsContainer.frame:SetPoint("RIGHT", container.frame, "LEFT", -17) buttonsContainer.frame:Show() frame.buttonsContainer = buttonsContainer @@ -489,9 +590,9 @@ function WeakAuras.CreateFrame() -- Toolbar local toolbarContainer = AceGUI:Create("SimpleGroup") toolbarContainer.frame:SetParent(buttonsContainer.frame) - toolbarContainer.frame:SetPoint("TOPLEFT", frame, "TOPLEFT", 20, -10) - toolbarContainer.frame:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -17, -10) - toolbarContainer.frame:SetPoint("BOTTOMLEFT", frame, "TOPLEFT", 20, -32) + toolbarContainer.frame:SetPoint("TOPLEFT", frame, "TOPLEFT", 20, -16) + toolbarContainer.frame:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -17, -16) + toolbarContainer.frame:SetPoint("BOTTOMLEFT", frame, "TOPLEFT", 20, -38) toolbarContainer:SetLayout("Flow") local newButton = AceGUI:Create("WeakAurasToolbarButton") @@ -839,6 +940,7 @@ function WeakAuras.CreateFrame() local tabsWidget + container.frame:SetPoint("TOPLEFT", frame, "TOPRIGHT", -63 - WeakAuras.normalWidth * 340, -14) container:ReleaseChildren() container:SetLayout("Fill") tabsWidget = AceGUI:Create("TabGroup") @@ -846,10 +948,10 @@ function WeakAuras.CreateFrame() local tabs = { { value = "region", text = L["Display"]}, { value = "trigger", text = L["Trigger"]}, - { value = "load", text = L["Load"]}, + { value = "conditions", text = L["Conditions"]}, { value = "action", text = L["Actions"]}, { value = "animation", text = L["Animations"]}, - { value = "conditions", text = L["Conditions"]}, + { value = "load", text = L["Load"]}, { value = "authorOptions", text = L["Custom Options"]}, { value = "information", text = L["Information"]}, } @@ -936,10 +1038,15 @@ function WeakAuras.CreateFrame() self.moversizer:Hide() self.pickedOption = "New" + container.frame:SetPoint("TOPLEFT", frame, "TOPRIGHT", -63 - WeakAuras.normalWidth * 340, -8) + container:SetLayout("fill") + local border = AceGUI:Create("InlineGroup") + border:SetLayout("Fill") + container:AddChild(border) + local containerScroll = AceGUI:Create("ScrollFrame") containerScroll:SetLayout("flow") - container:SetLayout("fill") - container:AddChild(containerScroll) + border:AddChild(containerScroll) local _, _, _, enabled = GetAddOnInfo("WeakAurasTemplates") if enabled then diff --git a/WeakAurasOptions/WeakAurasOptions.lua b/WeakAurasOptions/WeakAurasOptions.lua index cddce91..9d10b46 100644 --- a/WeakAurasOptions/WeakAurasOptions.lua +++ b/WeakAurasOptions/WeakAurasOptions.lua @@ -543,6 +543,9 @@ function WeakAuras.ShowOptions(msg) if (frame.window == "codereview") then frame.codereview:Close(); end + + -- TODO check if it should be shown + frame:ShowTip() end function WeakAuras.HideOptions() @@ -1682,4 +1685,4 @@ function WeakAuras.HandleRename(data, oldid, newid) WeakAuras.SetGrouping() WeakAuras.SortDisplayButtons() WeakAuras.PickDisplay(newid) -end \ No newline at end of file +end