if not WeakAuras.IsLibsOK() then return end ---@type string local AddonName = ... ---@class Private local Private = select(2, ...) local L = WeakAuras.L -- Takes as input a table of display data and attempts to update it to be compatible with the current version --- Modernizes the aura data ---@param data auraData function Private.Modernize(data, oldSnapshot) 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 Private.trigger_modes.first_active if activeTriggerMode ~= Private.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 12 was introduced February 2019 in BfA if data.internalVersion < 12 then if data.cooldownTextEnabled ~= nil then data.cooldownTextDisabled = not data.cooldownTextEnabled data.cooldownTextEnabled = nil 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 -- first conversion: attempt to migrate texture paths to file ids if data.regionType == "texture" and type(data.texture) == "string" then local textureId = GetFileIDFromPath(data.texture:gsub("\\\\", "\\")) if textureId and textureId > 0 then data.texture = tostring(textureId) end end if data.regionType == "progresstexture" then if type(data.foregroundTexture) == "string" then local textureId = GetFileIDFromPath(data.foregroundTexture:gsub("\\\\", "\\")) if textureId and textureId > 0 then data.foregroundTexture = tostring(textureId) end end if type(data.backgroundTexture) == "string" then local textureId = GetFileIDFromPath(data.backgroundTexture:gsub("\\\\", "\\")) if textureId and textureId > 0 then data.backgroundTexture = tostring(textureId) end end end -- 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 19 were introduced in July 2019 in BfA if data.internalVersion < 19 then if data.triggers then for triggerId, triggerData in ipairs(data.triggers) do if triggerData.trigger.type == "status" and triggerData.trigger.event == "Cast" and triggerData.trigger.unit == "multi" then triggerData.trigger.unit = "nameplate" end end end end -- 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", } data.subRegions = data.subRegions or {} 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 = {} Private.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 = {} Private.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 = {} Private.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 = {} Private.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 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 == "Alternate Power" then replacements[triggerId] = {} replacements[triggerId]["use_unitname"] = "use_namerealm" replacements[triggerId]["unitname"] = "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 Shadowlands 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 if data.internalVersion < 35 then if data.regionType == "texture" then data.textureWrapMode = "CLAMP" end end if data.internalVersion < 36 then data.ignoreOptionsEventErrors = true end if data.internalVersion < 37 then for triggerId, triggerData in ipairs(data.triggers) do if triggerData.trigger.type == "aura2" then local group_role = triggerData.trigger.group_role if group_role then triggerData.trigger.group_role = {} triggerData.trigger.group_role[group_role] = true end end end end if data.internalVersion < 38 then for triggerId, triggerData in ipairs(data.triggers) do if triggerData.trigger.type == "status" then if triggerData.trigger.event == "Item Type Equipped" then if triggerData.trigger.itemTypeName then if triggerData.trigger.itemTypeName.single then triggerData.trigger.itemTypeName.single = triggerData.trigger.itemTypeName.single + 2 * 256 end if triggerData.trigger.itemTypeName.multi then local converted = {} for v in pairs(triggerData.trigger.itemTypeName.multi) do converted[v + 512] = true end triggerData.trigger.itemTypeName.multi = converted end end end end end if data.load.itemtypeequipped then if data.load.itemtypeequipped.single then data.load.itemtypeequipped.single = data.load.itemtypeequipped.single + 2 * 256 end if data.load.itemtypeequipped.multi then local converted = {} for v in pairs(data.load.itemtypeequipped.multi) do converted[v + 512] = true end data.load.itemtypeequipped.multi = converted end end end if data.internalVersion < 39 then if data.regionType == "icon" or data.regionType == "aurabar" then if data.auto then data.iconSource = -1 else data.iconSource = 0 end end end if data.internalVersion < 40 then data.information = data.information or {} if data.regionType == "group" then data.information.groupOffset = true end data.information.ignoreOptionsEventErrors = data.ignoreOptionsEventErrors data.ignoreOptionsEventErrors = nil end if data.internalVersion < 41 then local newTypes = { ["Cooldown Ready (Spell)"] = "spell", ["Queued Action"] = "spell", ["Charges Changed"] = "spell", ["Action Usable"] = "spell", ["Chat Message"] = "event", ["Unit Characteristics"] = "unit", ["Cooldown Progress (Spell)"] = "spell", ["Power"] = "unit", ["PvP Talent Selected"] = "unit", ["Combat Log"] = "combatlog", ["Item Set"] = "item", ["Health"] = "unit", ["Cooldown Progress (Item)"] = "item", ["Conditions"] = "unit", ["Spell Known"] = "spell", ["Cooldown Ready (Item)"] = "item", ["Faction Reputation"] = "unit", ["Pet Behavior"] = "unit", ["Range Check"] = "unit", ["Character Stats"] = "unit", ["Talent Known"] = "unit", ["Threat Situation"] = "unit", ["Equipment Set"] = "item", ["Death Knight Rune"] = "unit", ["Cast"] = "unit", ["Item Count"] = "item", ["BigWigs Timer"] = "addons", ["Spell Activation Overlay"] = "spell", ["DBM Timer"] = "addons", ["Item Type Equipped"] = "item", ["Alternate Power"] = "unit", ["Item Equipped"] = "item", ["Item Bonus Id Equipped"] = "item", ["DBM Announce"] = "addons", ["Swing Timer"] = "unit", ["Totem"] = "spell", ["Ready Check"] = "event", ["BigWigs Message"] = "addons", ["Class/Spec"] = "unit", ["Stance/Form/Aura"] = "unit", ["Weapon Enchant"] = "item", ["Global Cooldown"] = "spell", ["Experience"] = "unit", ["GTFO"] = "addons", ["Cooldown Ready (Equipment Slot)"] = "item", ["Crowd Controlled"] = "unit", ["Cooldown Progress (Equipment Slot)"] = "item", ["Combat Events"] = "event", } for triggerId, triggerData in ipairs(data.triggers) do if triggerData.trigger.type == "status" or triggerData.trigger.type == "event" then local newType = newTypes[triggerData.trigger.event] if newType then triggerData.trigger.type = newType else WeakAuras.prettyPrint("Unknown trigger type found in, please report: ", data.id, triggerData.trigger.event) end end end end if data.internalVersion < 43 then -- The merging of zone ids and group ids went a bit wrong, -- fortunately that was caught before a actual release -- still try to recover the data if data.internalVersion == 42 then if data.load.zoneIds then local newstring = "" local first = true for id in data.load.zoneIds:gmatch("%d+") do if not first then newstring = newstring .. ", " end -- If the id is potentially a group, assume it is a group if C_Map.GetMapGroupMembersInfo(tonumber(id)) then newstring = newstring .. "g" .. id else newstring = newstring .. id end first = false end data.load.zoneIds = newstring end else if data.load.use_zoneId == data.load.use_zonegroupId then data.load.use_zoneIds = data.load.use_zoneId local zoneIds = strtrim(data.load.zoneId or "") local zoneGroupIds = strtrim(data.load.zonegroupId or "") zoneGroupIds = zoneGroupIds:gsub("(%d+)", "g%1") if zoneIds ~= "" or zoneGroupIds ~= "" then data.load.zoneIds = zoneIds .. ", " .. zoneGroupIds else -- One of them is empty data.load.zoneIds = zoneIds .. zoneGroupIds end elseif data.load.use_zoneId then data.load.use_zoneIds = true data.load.zoneIds = data.load.zoneId elseif data.load.use_zonegroupId then data.load.use_zoneIds = true local zoneGroupIds = strtrim(data.load.zonegroupId or "") zoneGroupIds = zoneGroupIds:gsub("(%d+)", "g%1") data.load.zoneIds = zoneGroupIds end data.load.use_zoneId = nil data.load.use_zonegroupId = nil data.load.zoneId = nil data.load.zonegroupId = nil end end if data.internalVersion < 44 then local function fixUp(data, prefix) local pattern = prefix .. "(.*)_format" local found = false for property in pairs(data) do local symbol = property:match(pattern) if symbol then found = true break end end if not found then return end local old = CopyTable(data) for property in pairs(old) do local symbol = property:match(pattern) if symbol then if data[property] == "timed" then data[prefix .. symbol .. "_time_format"] = 0 local oldDynamic = data[prefix .. symbol .. "_time_dynamic"] data[prefix .. symbol .. "_time_dynamic_threshold"] = oldDynamic and 3 or 60 end data[prefix .. symbol .. "_time_dynamic"] = nil if data[prefix .. symbol .. "_time_precision"] == 0 then data[prefix .. symbol .. "_time_precision"] = 1 data[prefix .. symbol .. "_time_dynamic_threshold"] = 0 end end end end if data.regionType == "text" then fixUp(data, "displayText_format_") end if data.subRegions then for index, subRegionData in ipairs(data.subRegions) do if subRegionData.type == "subtext" then fixUp(subRegionData, "text_text_format_") end end end if data.actions then for _, when in ipairs({ "start", "finish" }) do if data.actions[when] then fixUp(data.actions[when], "message_format_") 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 fixUp(change.value, "message_format_") end end end end end if data.internalVersion < 45 then for triggerId, triggerData in ipairs(data.triggers) do local trigger = triggerData.trigger if trigger.type == "unit" and trigger.event == "Conditions" then if trigger.use_instance_size then -- Single Selection if trigger.instance_size.single then if trigger.instance_size.single == "arena" then trigger.use_instance_size = false trigger.instance_size.multi = { arena = true, ratedarena = true, } elseif trigger.instance_size.single == "pvp" then trigger.use_instance_size = false trigger.instance_size.multi = { pvp = true, ratedpvp = true, } end end elseif trigger.use_instance_size == false then -- Multi selection if trigger.instance_size.multi then if trigger.instance_size.multi.arena then trigger.instance_size.multi.ratedarena = true end if trigger.instance_size.multi.pvp then trigger.instance_size.multi.ratedpvp = true end end end end end if data.load.use_size == true then if data.load.size.single == "arena" then data.load.use_size = false data.load.size.multi = { arena = true, ratedarena = true, } elseif data.load.size.single == "pvp" then data.load.use_size = false data.load.size.multi = { pvp = true, ratedpvp = true, } end elseif data.load.use_size == false then if data.load.size.multi then if data.load.size.multi.arena then data.load.size.multi.ratedarena = true end if data.load.size.multi.pvp then data.load.size.multi.ratedpvp = true end end end end if data.internalVersion < 46 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 = data.triggers[triggernum] if trigger and trigger.trigger and trigger.trigger.event == "Power" then if condition.check.variable == "chargedComboPoint" then condition.check.variable = "chargedComboPoint1" end end end end end end end if data.internalVersion < 49 then if not data.regionType:match("group") then data.subRegions = data.subRegions or {} -- rename aurabar_bar into subforeground, and subbarmodel into submodel for index, subRegionData in ipairs(data.subRegions) do if subRegionData.type == "aurabar_bar" then subRegionData.type = "subforeground" elseif subRegionData.type == "subbarmodel" then subRegionData.type = "submodel" end if subRegionData.bar_model_visible ~= nil then subRegionData.model_visible = subRegionData.bar_model_visible subRegionData.bar_model_visible = nil end if subRegionData.bar_model_alpha ~= nil then subRegionData.model_alpha = subRegionData.bar_model_alpha subRegionData.bar_model_alpha = nil end end -- rename conditions for bar_model_visible and bar_model_alpha if data.conditions then for conditionIndex, condition in ipairs(data.conditions) do if type(condition.changes) == "table" then for changeIndex, change in ipairs(condition.changes) do if change.property then local prefix, property = change.property:match("(sub%.%d+%.)(.*)") if prefix and property then if property == "bar_model_visible" then change.property = prefix .. "model_visible" elseif property == "bar_model_alpha" then change.property = prefix .. "model_alpha" end end end end end end end end end if data.internalVersion == 49 then -- Version 49 was a dud and contained a broken validation. Try to salvage the data, as -- best as we can. local broken = false local properties = {} Private.GetSubRegionProperties(data, properties) if data.conditions then for conditionIndex, condition in ipairs(data.conditions) do if type(condition.changes) == "table" then for changeIndex, change in ipairs(condition.changes) do if change.property then if not properties[change.property] then -- The property does not exist, so maybe it's one that was accidentally not moved local subRegionIndex, property = change.property:match("^sub%.(%d+)%.(.*)") if subRegionIndex and property then broken = true for _, offset in ipairs({ -1, 1 }) do local newProperty = "sub." .. subRegionIndex + offset .. "." .. property if properties[newProperty] then change.property = newProperty end end end end end end end end end if broken then WeakAuras.prettyPrint(L["Trying to repair broken conditions in %s likely caused by a WeakAuras bug."]:format(data.id)) end end if data.internalVersion < 51 then for triggerId, triggerData in ipairs(data.triggers) do if triggerData.trigger.event == "Threat Situation" then triggerData.trigger.unit = triggerData.trigger.threatUnit triggerData.trigger.use_unit = triggerData.trigger.use_threatUnit triggerData.trigger.threatUnit = nil triggerData.trigger.use_threatUnit = nil end end end if data.internalVersion < 52 then local function matchTarget(input) return input == "target" or input == "'target'" or input == '"target"' or input == "%t" or input == "'%t'" or input == '"%t"' end if data.conditions then for _, condition in ipairs(data.conditions) do for changeIndex, change in ipairs(condition.changes) do if change.property == "chat" and change.value then if matchTarget(change.value.message_dest) then change.value.message_dest = "target" change.value.message_dest_isunit = true end end end end end if data.actions.start.do_message and data.actions.start.message_type == "WHISPER" and matchTarget(data.actions.start.message_dest) then data.actions.start.message_dest = "target" data.actions.start.message_dest_isunit = true end if data.actions.finish.do_message and data.actions.finish.message_type == "WHISPER" and matchTarget(data.actions.finish.message_dest) then data.actions.finish.message_dest = "target" data.actions.finish.message_dest_isunit = true end end if data.internalVersion < 53 then local function ReplaceIn(text, table, prefix) local seenSymbols = {} Private.ParseTextStr(text, function(symbol) if not seenSymbols[symbol] then if table[prefix .. symbol .. "_format"] == "timed" and table[prefix .. symbol .. "_time_format"] == 0 then table[prefix .. symbol .. "_time_legacy_floor"] = true end end seenSymbols[symbol] = symbol end) end if data.regionType == "text" then ReplaceIn(data.displayText, data, "displayText_format_") end if data.subRegions then for index, subRegionData in ipairs(data.subRegions) do if subRegionData.type == "subtext" then ReplaceIn(subRegionData.text_text, subRegionData, "text_text_format_") end end end if data.actions then if data.actions.start then ReplaceIn(data.actions.start.message, data.actions.start, "message_format_") end if data.actions.finish then ReplaceIn(data.actions.finish.message, data.actions.finish, "message_format_") 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 ReplaceIn(change.value.message, change.value, "message_format_") end end end end end if data.internalVersion < 54 then for triggerId, triggerData in ipairs(data.triggers) do if triggerData.trigger.type == "aura" then triggerData.trigger.type = "unit" triggerData.trigger.event = "Conditions" triggerData.trigger.use_alwaystrue = false end end end if data.internalVersion < 55 then data.forceEvents = true end -- Internal version 55 contained a incorrect Modernize if data.internalVersion < 56 then data.information.forceEvents = data.forceEvents data.forceEvents = nil end if data.internalVersion < 57 then if WeakAuras.IsRetail() then local function GetField(load, field) local data = {} if load["use_" .. field] == true then if load[field].single then table.insert(data, load[field].single) end elseif load["use_" .. field] == false then for d in pairs(load[field].multi) do table.insert(data, d) end end return data end local function GetClassId(classFile) for classID = 1, GetNumClasses() do local _, thisClassFile = GetClassInfo(classID) if classFile == thisClassFile then return classID end end end local function SetSpec(load, specID) if load.use_class_and_spec == true then load.use_class_and_spec = false -- multi elseif load.use_class_and_spec == nil then load.use_class_and_spec = true -- single end load.class_and_spec = load.class_and_spec or {} load.class_and_spec.single = specID load.class_and_spec.multi = load.class_and_spec.multi or {} load.class_and_spec.multi[specID] = true end local load = data.load if load.use_class_and_spec == nil then local classes = GetField(load, "class") local specs = GetField(load, "spec") for i, class in ipairs(classes) do local classID = GetClassId(class) if #specs == 0 then -- add all specs for specIndex = 1, 4 do local specID = GetSpecializationInfoForClassID(classID, specIndex) if specID then SetSpec(load, specID) end end else for j, specIndex in ipairs(specs) do local specID = GetSpecializationInfoForClassID(classID, specIndex) if specID then SetSpec(load, specID) end end end end end end end if data.internalVersion < 58 then -- convert key use for talent load condition from talent's index to spellId if WeakAuras.IsRetail() then local function migrateTalent(load, specId, field) if load[field] and load[field].multi then local newData = {} for key, value in pairs(load[field].multi) do if value ~= nil then local talentData = Private.GetTalentData(specId) if type(talentData) == "table" and talentData[key] then newData[talentData[key][2]] = value end end end load[field].multi = newData end end local load = data.load local specId = Private.checkForSingleLoadCondition(load, "class_and_spec") if specId then migrateTalent(load, specId, "talent") migrateTalent(load, specId, "talent2") migrateTalent(load, specId, "talent3") end end end if data.internalVersion < 59 then -- convert key use for talent known trigger from talent's index to spellId if WeakAuras.IsRetail() then local function migrateTalent(load, specId, field) if load[field] and load[field].multi then local newData = {} for key, value in pairs(load[field].multi) do if value ~= nil then local talentData = Private.GetTalentData(specId) if type(talentData) == "table" and talentData[key] then newData[talentData[key][2]] = value end end end load[field].multi = newData end end for triggerId, triggerData in ipairs(data.triggers) do if triggerData.trigger.type == "unit" and triggerData.trigger.event == "Talent Known" then local classId for i = 1, GetNumClasses() do if select(2, GetClassInfo(i)) == triggerData.trigger.class then classId = i end end if classId and triggerData.trigger.spec then local specId = GetSpecializationInfoForClassID(classId, triggerData.trigger.spec) if specId then migrateTalent(triggerData.trigger, specId, "talent") end end end end end end if data.internalVersion < 60 then -- convert texture rotation if data.regionType == "texture" then if data.rotate then -- Full Rotate is enabled data.legacyZoomOut = true else -- Discrete Rotation data.rotation = data.discrete_rotation end data.discrete_rotation = nil end end if data.internalVersion < 61 then -- convert texture rotation if data.regionType == "texture" then if data.legacyZoomOut then data.rotate = true else data.rotate = false data.discrete_rotation = data.rotation end data.legacyZoomOut = nil end end -- version 62 became 64 to fix a broken modernize if data.internalVersion < 63 then if data.regionType == "texture" then local GetAtlasInfo = C_Texture and C_Texture.GetAtlasInfo or GetAtlasInfo local function IsAtlas(input) return type(input) == "string" and GetAtlasInfo(input) ~= nil end if not data.rotate or IsAtlas(data.texture) then data.rotation = data.discrete_rotation end end end if data.internalVersion < 64 then if data.regionType == "dynamicgroup" then if data.sort == "custom" and type(data.sortOn) ~= "string" or data.sortOn == "" then data.sortOn = "changed" end if data.grow == "CUSTOM" and type(data.growOn) ~= "string" then data.growOn = "changed" end end end if data.internalVersion < 65 then for triggerId, triggerData in ipairs(data.triggers) do if triggerData.trigger.type == "item" and triggerData.trigger.event == "Item Count" and type(triggerData.trigger.itemName) == "number" then triggerData.trigger.use_exact_itemName = true end end end local function spellIdToTalent(specId, spellId) local talents = Private.GetTalentData(specId) for _, talent in ipairs(talents) do if talent[2] == spellId then return talent[1] end end end if data.internalVersion < 66 then if WeakAuras.IsRetail() then for triggerId, triggerData in ipairs(data.triggers) do if triggerData.trigger.type == "unit" and triggerData.trigger.event == "Talent Known" and triggerData.trigger.talent and triggerData.trigger.talent.multi then local classId for i = 1, GetNumClasses() do if select(2, GetClassInfo(i)) == triggerData.trigger.class then classId = i end end if classId and triggerData.trigger.spec then local specId = GetSpecializationInfoForClassID(classId, triggerData.trigger.spec) if specId then local newMulti = { } for spellId, value in pairs(triggerData.trigger.talent.multi) do local talentId = spellIdToTalent(specId, spellId) if talentId then newMulti[talentId] = value end end triggerData.trigger.talent.multi = newMulti end end end end local specId = Private.checkForSingleLoadCondition(data.load, "class_and_spec") if specId then for _, property in ipairs({"talent", "talent2", "talent3"}) do local use = "use_" .. property if data.load[use] ~= nil and data.load[property] and data.load[property].multi then local newMulti = { } for spellId, value in pairs(data.load[property].multi) do local talentId = spellIdToTalent(specId, spellId) if talentId then newMulti[talentId] = value end end data.load[property].multi = newMulti end end end end end local function migrateToTable(tab, field) local value = tab[field] if value ~= nil and type(value) ~= "table" then tab[field] = { value } end end if data.internalVersion < 67 then do local trigger_migration = { ["Cast"] = { "stage", "stage_operator", }, ["Experience"] = { "level", "level_operator", "currentXP", "currentXP_operator", "totalXP", "totalXP_operator", "percentXP", "percentXP_operator", "restedXP", "restedXP_operator", "percentrested", "percentrested_operator", }, ["Health"] = { "health", "health_operator", "percenthealth", "percenthealth_operator", "deficit", "deficit_operator", "maxhealth", "maxhealth_operator", "absorb", "absorb_operator", "healabsorb", "healabsorb_operator", "healprediction", "healprediction_operator", }, ["Power"] = { "power", "power_operator", "percentpower", "percentpower_operator", "deficit", "deficit_operator", "maxpower", "maxpower_operator", }, ["Character Stats"] = { "mainstat", "mainstat_operator", "strength", "strength_operator", "agility", "agility_operator", "intellect", "intellect_operator", "spirit", "spirit_operator", "stamina", "stamina_operator", "criticalrating", "criticalrating_operator", "criticalpercent", "criticalpercent_operator", "hitrating", "hitrating_operator", "hitpercent", "hitpercent_operator", "hasterating", "hasterating_operator", "hastepercent", "hastepercent_operator", "meleehastepercent", "meleehastepercent_operator", "expertiserating", "expertiserating_operator", "expertisebonus", "expertisebonus_operator", "armorpenrating", "armorpenrating_operator", "armorpenpercent", "armorpenpercent_operator", "resiliencerating", "resiliencerating_operator", "resiliencepercent", "resiliencepercent_operator", "spellpenpercent", "spellpenpercent_operator", "masteryrating", "masteryrating_operator", "masterypercent", "masterypercent_operator", "versatilityrating", "versatilityrating_operator", "versatilitypercent", "versatilitypercent_operator", "attackpower", "attackpower_operator", "resistanceholy", "resistanceholy_operator", "resistancefire", "resistancefire_operator", "resistancenature", "resistancenature_operator", "resistancefrost", "resistancefrost_operator", "resistanceshadow", "resistanceshadow_operator", "resistancearcane", "resistancearcane_operator", "leechrating", "leechrating_operator", "leechpercent", "leechpercent_operator", "movespeedrating", "movespeedrating_operator", "movespeedpercent", "movespeedpercent_operator", "runspeedpercent", "runspeedpercent_operator", "avoidancerating", "avoidancerating_operator", "avoidancepercent", "avoidancepercent_operator", "defense", "defense_operator", "dodgerating", "dodgerating_operator", "dodgepercent", "dodgepercent_operator", "parryrating", "parryrating_operator", "parrypercent", "parrypercent_operator", "blockpercent", "blockpercent_operator", "blocktargetpercent", "blocktargetpercent_operator", "blockvalue", "blockvalue_operator", "staggerpercent", "staggerpercent_operator", "staggertargetpercent", "staggertargetpercent_operator", "armorrating", "armorrating_operator", "armorpercent", "armorpercent_operator", "armortargetpercent", "armortargetpercent_operator", }, ["Threat Situation"] = { "threatpct", "threatpct_operator", "rawthreatpct", "rawthreatpct_operator", "threatvalue", "threatvalue_operator", }, ["Unit Characteristics"] = { "level", "level_operator", }, ["Combat Log"] = { "spellId", "spellName", }, ["Spell Cast Succeeded"] = { "spellId" } } for _, triggerData in ipairs(data.triggers) do local t = triggerData.trigger local fieldsToMigrate = trigger_migration[t.event] if fieldsToMigrate then for _, field in ipairs(fieldsToMigrate) do migrateToTable(t, field) end end -- cast trigger move data from 'spell' & 'spellId' to 'spellIds' & 'spellNames' if t.event == "Cast" and t.type == "unit" then if t.spellId then if t.useExactSpellId then t.use_spellIds = t.use_spellId t.spellIds = t.spellIds or {} tinsert(t.spellIds, t.spellId) else t.use_spellNames = t.use_spellId t.spellNames = t.spellNames or {} tinsert(t.spellNames, t.spellId) end end if t.use_spell and t.spell then t.use_spellNames = true t.spellNames = t.spellNames or {} tinsert(t.spellNames, t.spell) end t.use_spellId = nil t.spellId = nil t.use_spell = nil t.spell = nil end end end do local loadFields = { "level", "effectiveLevel" } for _, field in ipairs(loadFields) do migrateToTable(data.load, field) migrateToTable(data.load, field .. "_operator") end end end if data.internalVersion < 68 then if data.parent then local parentData = WeakAuras.GetData(data.parent) if parentData and parentData.regionType == "dynamicgroup" then if data.anchorFrameParent == nil then data.anchorFrameParent = false end end end end if data.internalVersion < 69 then migrateToTable(data.load, "itemequiped") end if data.internalVersion < 70 then local trigger_migration = { Power = { "power", "power_operator" } } for _, triggerData in ipairs(data.triggers) do local t = triggerData.trigger local fieldsToMigrate = trigger_migration[t.event] if fieldsToMigrate then for _, field in ipairs(fieldsToMigrate) do migrateToTable(t, field) end end end end if data.internalVersion < 71 then if data.regionType == 'icon' or data.regionType == 'aurabar' or data.regionType == 'progresstexture' or data.regionType == 'stopmotion' then data.progressSource = {-1, ""} else data.progressSource = nil end if data.subRegions then for index, subRegionData in ipairs(data.subRegions) do if subRegionData.type == "subtick" then local tick_placement = subRegionData.tick_placement subRegionData.tick_placements = {} subRegionData.tick_placements[1] = tick_placement subRegionData.progressSources = {{-2, ""}} subRegionData.tick_placement = nil end end end end if data.internalVersion < 72 then if WeakAuras.IsClassic() then if data.model_path and data.modelIsUnit then data.model_fileId = data.model_path end end end if data.internalVersion < 73 then if data.conditions then for conditionIndex, condition in ipairs(data.conditions) do for changeIndex, change in ipairs(condition.changes) do if type(change.property) == "string" then change.property = string.gsub(change.property, "(sub.%d.tick_placement)(%d)", "%1s.%2") end end end end end if data.internalVersion < 74 then for _, triggerData in ipairs(data.triggers) do local t = triggerData.trigger if t.type == "spell" and t.event == "Cooldown Progress (Spell)" then if t.use_exact_spellName then t.use_ignoreoverride = true end end end end if data.internalVersion < 75 then -- this commit from nov 2019 https://github.com/WeakAuras/WeakAuras2/commit/6d8f11c17422aeffdb82a0aa05181edfdd137896 -- changed adjustedMin & adjustedMax type from number to string (range => input) -- but didn't include a migration if type(data.adjustedMin) == "number" then data.adjustedMin = tostring(data.adjustedMin) end if type(data.adjustedMax) == "number" then data.adjustedMax = tostring(data.adjustedMax) end -- this commit https://github.com/WeakAuras/WeakAuras2/commit/dbcb70b1e4df262af82f63620b3b0d80741e6df2 -- set a default for adjustedMin & adjustedMax with an empty string -- in Private.validate if type of value is different from type of default, value is set to default -- which had effect to lose data if aura was made before nov 2019 ~ 2020 -- try detect data loss and restore from Archivist if data.internalVersion == 74 and oldSnapshot then local restoreMin = data.useAdjustededMin and data.adjustedMin == "" local restoreMax = data.useAdjustededMax and data.adjustedMax == "" if restoreMin or restoreMax then if restoreMin and type(oldSnapshot.adjustedMin) == "number" then data.adjustedMin = tostring(oldSnapshot.adjustedMin) end if restoreMax and type(oldSnapshot.adjustedMax) == "number" then data.adjustedMax = tostring(oldSnapshot.adjustedMax) end end end end if data.internalVersion < 76 then local function removeHoles(t) local keys = {} for key in pairs(t) do table.insert(keys, key) end if #keys ~= #t then table.sort(keys) local newTable = {} for i, key in ipairs(keys) do newTable[i] = t[key] end return newTable else return t end end local trigger_migration = { ["Spell Cast Succeeded"] = { "spellId", }, ["Unit Characteristics"] = { "level", }, ["Power"] = { "power", "percentpower", "deficit", "maxpower", }, ["Combat Log"] = { "spellId", "spellName", }, ["Health"] = { "health", "percenthealth", "deficit", "maxhealth", "absorb", "healabsorb", "healprediction", }, ["Faction Reputation"] = { "value", "total", "percentRep", }, ["Location"] = { "zone", "subzone", }, ["Threat Situation"] = { "threatpct", "rawthreatpct", "threatvalue", }, ["Character Stats"] = { "mainstat", "strength", "agility", "intellect", "spirit", "stamina", "criticalrating", "criticalpercent", "hitrating", "hitpercent", "hasterating", "hastepercent", "meleehastepercent", "expertiserating", "expertisebonus", "spellpenpercent", "masteryrating", "masterypercent", "versatilityrating", "versatilitypercent", "attackpower", "leechrating", "leechpercent", "movespeedrating", "movespeedpercent", "runspeedpercent", "avoidancerating", "avoidancepercent", "dodgerating", "dodgepercent", "parryrating", "parrypercent", "blockpercent", "blocktargetpercent", "blockvalue", "staggerpercent", "staggertargetpercent", "armorrating", "armorpercent", "armortargetpercent", "resistanceholy", "resistancefire", "resistancenature", "resistancefrost", "resistanceshadow", "resistancearcane", }, ["Cast"] = { "spellNames", "spellIds", "stage", }, ["Alternate Power"] = { "power", }, ["Experience"] = { "level", "currentXP", "totalXP", "percentXP", "restedXP", "percentrested", } } for _, triggerData in ipairs(data.triggers) do local trigger = triggerData.trigger local fieldsToMigrate = trigger_migration[trigger.event] if fieldsToMigrate then for _, field in ipairs(fieldsToMigrate) do if type(trigger[field]) == "table" then trigger[field] = removeHoles(trigger[field]) end end end end end if data.internalVersion < 77 then -- fix data broken by wago export local triggerFix = { talent = { multi = true }, herotalent = { multi = true }, form = { multi = true }, specId = { multi = true }, actualSpec = true, arena_spec = true } local loadFix = { talent = { multi = true }, talent2 = { multi = true }, talent3 = { multi = true }, herotalent = { multi = true }, class_and_spec = { multi = true } } local function fixData(data, fields) for k, v in pairs(fields) do if v == true and type(data[k]) == "table" then -- fix field k local tofix = {} for key in pairs(data[k]) do if type(key) == "string" then table.insert(tofix, key) end end for _, oldkey in ipairs(tofix) do local newkey = tonumber(oldkey) if newkey then data[k][newkey] = data[k][oldkey] end data[k][oldkey] = nil end elseif type(v) == "table" and type(data[k]) == "table" then -- recurse fixData(data[k], fields[k]) end end end for _, triggerData in ipairs(data.triggers) do fixData(triggerData.trigger, triggerFix) end fixData(data.load, loadFix) end if data.internalVersion < 78 then if data.triggers then for triggerId, triggerData in ipairs(data.triggers) do local trigger = triggerData.trigger -- Item Type is now always a multi selection if trigger and trigger.type == "item" and trigger.event == "Item Type Equipped" then local value = trigger.itemTypeName and trigger.itemTypeName.single or nil if trigger.use_itemTypeName and value then trigger.use_itemTypeName = false trigger.itemTypeName = {multi = {[value] = true}} else trigger.itemTypeName = {multi = {}} end end end end end if data.internalVersion < 79 then if data.triggers then for _, triggerData in ipairs(data.triggers) do local trigger = triggerData.trigger if trigger and trigger.type == "unit" and trigger.event == "Unit Characteristics" then if trigger.use_ignoreDead then if trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party" then trigger.use_dead = false else -- since this option was previously only available for group units, -- nil it out if the unit isn't group to avoid surprises from vestigial data trigger.use_dead = nil end end trigger.use_ignoreDead = nil end end end end if data.internalVersion < 80 then -- Use common names for anchor areas/points so -- that up/down of sub regions can adapt that local conversions = { subborder = { border_anchor = "anchor_area", }, subglow = { glow_anchor = "anchor_area" }, subtext = { text_anchorPoint = "anchor_point" } } if data.subRegions then for index, subRegionData in ipairs(data.subRegions) do if conversions[subRegionData.type] then for oldKey, newKey in pairs(conversions[subRegionData.type]) do subRegionData[newKey] = subRegionData[oldKey] subRegionData[oldKey] = nil end end end end end if data.internalVersion < 81 then -- Rename 'progressSources' to 'progressSource' for Linear/CircularProgressTexture/StopMotion sub elements local conversions = { sublineartexture = { progressSources = "progressSource", }, subcirculartexture = { progressSources = "progressSource", }, substopmotion = { progressSources = "progressSource", } } if data.subRegions then for index, subRegionData in ipairs(data.subRegions) do if conversions[subRegionData.type] then for oldKey, newKey in pairs(conversions[subRegionData.type]) do subRegionData[newKey] = subRegionData[oldKey] subRegionData[oldKey] = nil end end end end end if data.internalVersion < 82 then -- noMerge for separator custom option doesn't make sense, -- and groups achieve the desired effect better, -- so drop the feature if data.authorOptions then for _, optionData in ipairs(data.authorOptions) do if optionData.type == "header" then optionData.noMerge = nil end end end end if data.internalVersion < 83 then local propertyRenames = { cooldownText = "cooldownTextDisabled", } 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 if data.internalVersion < 84 then if data.triggers then for _, triggerData in ipairs(data.triggers) do local trigger = triggerData.trigger if trigger and trigger.type == "addons" then if trigger.event == "Boss Mod Timer" or trigger.event == "BigWigs Timer" or trigger.event == "DBM Timer" then -- if trigger don't filter bars, show only those active in the addon config for triggers made before this option was added -- show disabled bars when looking for specific ids/name if not (trigger.use_message or trigger.use_spellId) then trigger.use_isBarEnabled = true end end end end end end if data.internalVersion < 85 then if data.triggers then local eventTypes = { ["Unit Characteristics"] = true, ["Health"] = true, ["Power"] = true, ["Alternate Power"] = true, ["Cast"] = true } for _, triggerData in ipairs(data.triggers) do local trigger = triggerData.trigger if trigger and trigger.type == "unit" then if eventTypes[trigger.event] then local rt = trigger.raidMarkIndex if type(rt) == "number" then trigger.raidMarkIndex = { single = rt } end if trigger.use_raidMarkIndex == false then trigger.use_raidMarkIndex = nil end end end end end end data.internalVersion = max(data.internalVersion or 0, WeakAuras.InternalVersion()) end --- Returns true if Modernize will use data from last snapshot before a new one is done function Private.ModernizeNeedsOldSnapshot(data) if data.internalVersion == 74 then local restoreMin = data.useAdjustededMin and data.adjustedMin == "" local restoreMax = data.useAdjustededMax and data.adjustedMax == "" if restoreMin or restoreMax then return true end end end