From 6a7003b53573361bd2e17e322f8645954c009daf Mon Sep 17 00:00:00 2001 From: Bunny67 Date: Sat, 11 Jul 2020 00:19:17 +0300 Subject: [PATCH] from retail --- WeakAuras/AuraEnvironment.lua | 43 ++ WeakAuras/BuffTrigger2.lua | 49 +- WeakAuras/GenericTrigger.lua | 76 ++- WeakAuras/Init.lua | 2 +- WeakAuras/Prototypes.lua | 519 ++++++++++++++---- WeakAuras/RegionTypes/AuraBar.lua | 408 ++++++++------ WeakAuras/RegionTypes/RegionPrototype.lua | 24 +- WeakAuras/RegionTypes/Text.lua | 13 +- WeakAuras/RegionTypes/Texture.lua | 4 +- WeakAuras/SubRegionTypes/SubText.lua | 16 +- WeakAuras/Transmission.lua | 3 + WeakAuras/Types.lua | 513 ++++++++++++++++- WeakAuras/WeakAuras.lua | 469 ++++++++++++++-- WeakAuras/WeakAuras.toc | 2 +- WeakAuras/compat.lua | 12 + WeakAurasOptions/ActionOptions.lua | 137 ++++- WeakAurasOptions/BuffTrigger.lua | 6 +- WeakAurasOptions/BuffTrigger2.lua | 53 +- WeakAurasOptions/Cache.lua | 127 +++-- WeakAurasOptions/ConditionOptions.lua | 143 +++-- WeakAurasOptions/OptionsFrames/IconPicker.lua | 51 +- .../OptionsFrames/OptionsFrame.lua | 8 +- WeakAurasOptions/OptionsFrames/TextEditor.lua | 33 -- WeakAurasOptions/RegionOptions/Text.lua | 68 ++- WeakAurasOptions/RegionOptions/Texture.lua | 2 +- WeakAurasOptions/SubRegionOptions/SubText.lua | 76 +-- WeakAurasOptions/WeakAurasOptions.lua | 107 +++- 27 files changed, 2317 insertions(+), 647 deletions(-) diff --git a/WeakAuras/AuraEnvironment.lua b/WeakAuras/AuraEnvironment.lua index 08b3c57..a66f7fc 100644 --- a/WeakAuras/AuraEnvironment.lua +++ b/WeakAuras/AuraEnvironment.lua @@ -63,12 +63,55 @@ local WA_ClassColorName = function(unit) end end +WeakAuras.WA_ClassColorName = WA_ClassColorName + +-- UTF-8 Sub is pretty commonly needed +local WA_Utf8Sub = function(input, size) + local output = "" + local i = 1 + while (size > 0) do + local byte = input:byte(i) + if not byte then + return output + end + if byte < 128 then + -- ASCII byte + output = output .. input:sub(i, i) + size = size - 1 + elseif byte < 192 then + -- Continuation bytes + output = output .. input:sub(i, i) + elseif byte < 244 then + -- Start bytes + output = output .. input:sub(i, i) + size = size - 1 + end + i = i + 1 + end + + -- Add any bytes that are part of the sequence + while (true) do + local byte = input:byte(i) + if byte >= 128 and byte < 192 then + output = output .. input:sub(i, i) + else + break + end + i = i + 1 + end + + return output +end + +WeakAuras.WA_Utf8Sub = WA_Utf8Sub + local helperFunctions = { WA_GetUnitAura = WA_GetUnitAura, WA_GetUnitBuff = WA_GetUnitBuff, WA_GetUnitDebuff = WA_GetUnitDebuff, WA_IterateGroupMembers = WA_IterateGroupMembers, WA_ClassColorName = WA_ClassColorName, + WA_Utf8Sub = WA_Utf8Sub, } local LCG = LibStub("LibCustomGlow-1.0") diff --git a/WeakAuras/BuffTrigger2.lua b/WeakAuras/BuffTrigger2.lua index 2cebaf4..2924eec 100644 --- a/WeakAuras/BuffTrigger2.lua +++ b/WeakAuras/BuffTrigger2.lua @@ -82,7 +82,7 @@ local scanFuncNameGroup = {} local scanFuncSpellIdGroup = {} local scanFuncGeneralGroup = {} --- Contains all scanFuncs that should be check if the exitance of a unit changed +-- Contains all scanFuncs that should be check if the existence of a unit changed local unitExistScanFunc = {} -- Which units exist local existingUnits = {} @@ -870,6 +870,10 @@ local function TriggerInfoApplies(triggerInfo, unit) return false end + if triggerInfo.ignoreInvisible and not UnitIsVisible(unit) then + return false + end + if triggerInfo.unit == "group" and triggerInfo.groupSubType == "party" then if IsInRaid() then -- Filter our player/party# while in raid and keep only raid units that are correct @@ -895,6 +899,11 @@ local function TriggerInfoApplies(triggerInfo, unit) if triggerInfo.class and not triggerInfo.class[select(2, UnitClass(unit))] then return false end + + if triggerInfo.nameChecker and not triggerInfo.nameChecker:Check(WeakAuras.UnitNameWithRealm(unit)) then + return false + end + return true end @@ -1012,8 +1021,8 @@ local function UpdateTriggerState(time, id, triggernum) useMatches = SatisfiesGroupMatchCount(triggerInfo, unitCount, maxUnitCount, matchCount) end + local cloneIds = {} if useMatches then - table.sort(auraDatas, SortMatchDataByUnitIndex) local affected, unaffected @@ -1022,7 +1031,6 @@ local function UpdateTriggerState(time, id, triggernum) end local usedCloneIds = {}; - for index, auraData in ipairs(auraDatas) do local cloneId = (auraData.GUID or auraData.unit or "unknown") .. " " .. auraData.spellId if usedCloneIds[cloneId] then @@ -1033,15 +1041,17 @@ local function UpdateTriggerState(time, id, triggernum) end updated = UpdateStateWithMatch(time, auraData, triggerStates, cloneId, matchCount, unitCount, maxUnitCount, matchCountPerUnit[auraData.unit], totalStacks, affected, unaffected) or updated + cloneIds[cloneId] = true end if matchCount == 0 then updated = UpdateStateWithNoMatch(time, triggerStates, triggerInfo, "", nil, 0, 0, maxUnitCount, 0, totalStacks, affected, unaffected) or updated + cloneIds[""] = true end end for cloneId, state in pairs(triggerStates) do - if state.show and state.time < time then + if not cloneIds[cloneId] then updated = RemoveState(triggerStates, cloneId) or updated end end @@ -1069,6 +1079,7 @@ local function UpdateTriggerState(time, id, triggernum) local useMatches = SatisfiesGroupMatchCount(triggerInfo, unitCount, maxUnitCount, matchCount) + local cloneIds = {} if useMatches then local affected, unaffected if triggerInfo.useAffected then @@ -1079,6 +1090,7 @@ local function UpdateTriggerState(time, id, triggernum) for unit, bestMatch in pairs(matches) do if bestMatch then updated = UpdateStateWithMatch(time, bestMatch, triggerStates, unit, matchCount, unitCount, maxUnitCount, matchCountPerUnit[unit], totalStacks, affected, unaffected) or updated + cloneIds[unit] = true end end else @@ -1089,9 +1101,11 @@ local function UpdateTriggerState(time, id, triggernum) if bestMatch then if triggerInfo.perUnitMode == "all" then updated = UpdateStateWithMatch(time, bestMatch, triggerStates, unit, matchCount, unitCount, maxUnitCount, matchCountPerUnit[unit], totalStacks, affected, unaffected) or updated + cloneIds[unit] = true end else updated = UpdateStateWithNoMatch(time, triggerStates, triggerInfo, unit, unit, matchCount, unitCount, maxUnitCount, matchCountPerUnit[unit], totalStacks, affected, unaffected) or updated + cloneIds[unit] = true end end end @@ -1099,7 +1113,7 @@ local function UpdateTriggerState(time, id, triggernum) end for cloneId, state in pairs(triggerStates) do - if state.show and state.time < time then + if not cloneIds[cloneId] then updated = RemoveState(triggerStates, cloneId) or updated end end @@ -1156,11 +1170,12 @@ local function PrepareMatchData(unit, filter) end end -local function CleanUpOutdatedMatchData(time, unit, filter) +local function CleanUpOutdatedMatchData(removeIndex, unit, filter) -- Figure out if any matchData is outdated if matchData[unit] and matchData[unit][filter] then - for index, data in pairs(matchData[unit][filter]) do - if data.time < time or not UnitExists(unit) then + for index = removeIndex, #matchData[unit][filter] do + local data = matchData[unit][filter][index] + if data.index >= removeIndex or not UnitExists(unit) then matchData[unit][filter][index] = nil for id, triggerData in pairs(data.auras) do for triggernum in pairs(triggerData) do @@ -1233,7 +1248,7 @@ local function ScanUnitWithFilter(matchDataChanged, time, unit, filter, if matchDataUpToDate[unit] then matchDataUpToDate[unit][filter] = nil end - CleanUpOutdatedMatchData(time, unit, filter) + CleanUpOutdatedMatchData(1, unit, filter) return end @@ -1268,7 +1283,7 @@ local function ScanUnitWithFilter(matchDataChanged, time, unit, filter, end end - CleanUpOutdatedMatchData(time, unit, filter) + CleanUpOutdatedMatchData(index, unit, filter) matchDataUpToDate[unit] = matchDataUpToDate[unit] or {} matchDataUpToDate[unit][filter] = true @@ -1514,11 +1529,7 @@ local function EventHandler(frame, event, arg1, arg2, ...) tinsert(unitsToRemove, unit) end end - elseif event == "UNIT_FLAGS" then - if WeakAuras.multiUnitUnits.group[arg1] then - RecheckActiveForUnitType("group", arg1, deactivatedTriggerInfos) - end - elseif event == "PLAYER_FLAGS_CHANGED" then + elseif event == "UNIT_FLAGS" or event == "UNIT_NAME_UPDATE" or event == "PLAYER_FLAGS_CHANGED" then if WeakAuras.multiUnitUnits.group[arg1] then RecheckActiveForUnitType("group", arg1, deactivatedTriggerInfos) end @@ -1549,6 +1560,7 @@ local function EventHandler(frame, event, arg1, arg2, ...) end frame:RegisterEvent("UNIT_AURA") +frame:RegisterEvent("UNIT_NAME_UPDATE") frame:RegisterEvent("UNIT_FLAGS") frame:RegisterEvent("PLAYER_FLAGS_CHANGED") frame:RegisterEvent("UNIT_PET") @@ -2128,6 +2140,8 @@ function BuffTrigger.Add(data) local effectiveClass = groupTrigger and trigger.useClass and trigger.class local effectiveIgnoreDead = groupTrigger and trigger.ignoreDead local effectiveIgnoreDisconnected = groupTrigger and trigger.ignoreDisconnected + local effectiveIgnoreInvisible = groupTrigger and trigger.ignoreInvisible + local effectiveNameCheck = groupTrigger and trigger.useUnitName and trigger.unitName if trigger.unit == "multi" then BuffTrigger.InitMultiAura() @@ -2180,12 +2194,14 @@ function BuffTrigger.Add(data) ignoreSelf = effectiveIgnoreSelf, ignoreDead = effectiveIgnoreDead, ignoreDisconnected = effectiveIgnoreDisconnected, + ignoreInvisible = effectiveIgnoreInvisible, groupSubType = groupSubType, groupCountFunc = groupCountFunc, class = effectiveClass, matchCountFunc = matchCountFunc, useAffected = unit == "group" and trigger.useAffected, isMulti = trigger.unit == "multi", + nameChecker = effectiveNameCheck and WeakAuras.ParseNameCheck(trigger.unitName) } triggerInfos[id] = triggerInfos[id] or {} triggerInfos[id][triggernum] = triggerInformation @@ -2322,6 +2338,7 @@ function BuffTrigger.GetAdditionalProperties(data, triggernum) ret = ret .. "|cFFFF0000%".. triggernum .. ".debuffClass|r - " .. L["Debuff Class"] .. "\n" ret = ret .. "|cFFFF0000%".. triggernum .. ".unitCaster|r - " .. L["Caster Unit"] .. "\n" ret = ret .. "|cFFFF0000%".. triggernum .. ".casterName|r - " .. L["Caster Name"] .. "\n" + ret = ret .. "|cFFFF0000%".. triggernum .. ".unit|r - " .. L["Unit"] .. "\n" ret = ret .. "|cFFFF0000%".. triggernum .. ".unitName|r - " .. L["Unit Name"] .. "\n" ret = ret .. "|cFFFF0000%".. triggernum .. ".matchCount|r - " .. L["Match Count"] .. "\n" ret = ret .. "|cFFFF0000%".. triggernum .. ".matchCountPerUnit|r - " .. L["Match Count per Unit"] .. "\n" @@ -2385,7 +2402,7 @@ function BuffTrigger.GetTriggerConditions(data, triggernum) result["spellId"] = { display = L["Spell Id"], type = "number", - operator_types_only_equal = true + operator_types = "only_equal" } result["matchCount"] = { diff --git a/WeakAuras/GenericTrigger.lua b/WeakAuras/GenericTrigger.lua index 912c5e6..991ecf0 100644 --- a/WeakAuras/GenericTrigger.lua +++ b/WeakAuras/GenericTrigger.lua @@ -190,7 +190,8 @@ function TestForMultiSelect(trigger, arg) end function ConstructTest(trigger, arg) - local test; + local test + local preamble local name = arg.name; if(arg.hidden or arg.type == "tristate" or arg.type == "toggle" or (arg.type == "multiselect" and trigger["use_"..name] ~= nil) or ((trigger["use_"..name] or arg.required) and trigger[name])) then local number = tonumber(trigger[name]); @@ -215,11 +216,15 @@ function ConstructTest(trigger, arg) end end - if (test == "(true)") then - return nil; + if arg.preamble then + preamble = arg.preamble:format(trigger[name] or "") end - return test; + if (test == "(true)") then + return nil, preamble + end + + return test, preamble end function ConstructFunction(prototype, trigger, inverse) @@ -243,6 +248,7 @@ function ConstructFunction(prototype, trigger, inverse) local debug = {}; local store = {}; local init; + local preambles = "" if(prototype.init) then init = prototype.init(trigger); else @@ -268,7 +274,7 @@ function ConstructFunction(prototype, trigger, inverse) if (arg.store) then tinsert(store, name); end - local test = ConstructTest(trigger, arg); + local test, preamble = ConstructTest(trigger, arg); if (test) then if(arg.required) then tinsert(required, test); @@ -279,10 +285,13 @@ function ConstructFunction(prototype, trigger, inverse) tinsert(debug, arg.debug:format(trigger[name])); end end + if (preamble) then + preambles = preambles .. "\n" .. preamble + end end end end - local ret = "return function("..tconcat(input, ", ")..")\n"; + local ret = preambles .. "return function("..tconcat(input, ", ")..")\n"; ret = ret..(init or ""); ret = ret..(#debug > 0 and tconcat(debug, "\n") or ""); @@ -558,6 +567,13 @@ local function RunTriggerFunc(allStates, data, id, triggernum, event, arg1, arg2 untriggerCheck = true; end elseif (data.statesParameter == "unit") then + if optionsEvent then + if WeakAuras.multiUnitUnits[data.trigger.unit] then + arg1 = next(WeakAuras.multiUnitUnits[data.trigger.unit]) + else + arg1 = data.trigger.unit + end + end if arg1 then local unit, cloneId if WeakAuras.multiUnitUnits[data.trigger.unit] then @@ -2501,7 +2517,7 @@ end local watchUnitChange -- Nameplates only distinguish between friends and everyone else -function WeakAuras.GetPlayerReaciton(unit) +function WeakAuras.GetPlayerReaction(unit) return UnitIsEnemy('player', unit) and 'hostile' or 'friendly' end @@ -3644,16 +3660,17 @@ function GenericTrigger.GetTriggerConditions(data, triggernum) end end end + if (v.conditionPreamble) then + result[v.name].preamble = v.conditionPreamble; + end if (v.conditionTest) then result[v.name].test = v.conditionTest; end if (v.conditionEvents) then result[v.name].events = v.conditionEvents; end - if (v.operator_types_without_equal) then - result[v.name].operator_types_without_equal = true; - elseif (v.operator_types_only_equal) then - result[v.name].operator_types_only_equal = true; + if (v.operator_types) then + result[v.name].operator_types = v.operator_types; end end end @@ -3861,6 +3878,43 @@ function GenericTrigger.GetTriggerDescription(data, triggernum, namestable) end end +do + -- Based on Code by DejaCharacterStats. Ugly code to figure out the GCD + local GetCombatRatingBonus = GetCombatRatingBonus + local CR_HASTE_MELEE = CR_HASTE_MELEE + local CR_HASTE_RANGED = CR_HASTE_RANGED + local CR_HASTE_SPELL = CR_HASTE_SPELL + local class = select(2, UnitClass("player")) + if class == "DRUID" then + function WeakAuras.CalculatedGcdDuration() + local id = GetShapeshiftFormID() + local haste = GetHaste() + return id == 1 and 1 or max(0.75, 1.5 * 100 / (100 + GetCombatRatingBonus(CR_HASTE_SPELL))) + end + elseif class == "ROGUE" then + function WeakAuras.CalculatedGcdDuration() + return 1 + end + else + local GetHaste + if class == "HUNTER" then + function GetHaste() + return GetCombatRatingBonus(CR_HASTE_RANGED) + end + elseif class == "DEATHKNIGHT" or class == "PALADIN" or class == "WARRIOR" then + function GetHaste() + return GetCombatRatingBonus(CR_HASTE_MELEE) + end + else + function GetHaste() + return GetCombatRatingBonus(CR_HASTE_SPELL) + end + end + function WeakAuras.CalculatedGcdDuration() + return max(0.75, 1.5 * 100 / (100 + GetHaste())) + end + end +end WeakAuras.RegisterTriggerSystem({"event", "status", "custom"}, GenericTrigger); diff --git a/WeakAuras/Init.lua b/WeakAuras/Init.lua index 68c8dd9..770863a 100644 --- a/WeakAuras/Init.lua +++ b/WeakAuras/Init.lua @@ -7,7 +7,7 @@ WeakAuras.halfWidth = WeakAuras.normalWidth / 2 WeakAuras.doubleWidth = WeakAuras.normalWidth * 2 local versionStringFromToc = GetAddOnMetadata("WeakAuras", "Version") -local versionString = "2.17.8" +local versionString = "2.17.11" local buildTime = "20200511190745" WeakAuras.versionString = versionStringFromToc diff --git a/WeakAuras/Prototypes.lua b/WeakAuras/Prototypes.lua index c0254ce..0465b5e 100644 --- a/WeakAuras/Prototypes.lua +++ b/WeakAuras/Prototypes.lua @@ -12,6 +12,7 @@ local UnitClass, UnitHealth, UnitHealthMax, UnitName, UnitPower, UnitPowerMax = local GetSpellInfo, GetItemInfo, GetItemCount, GetItemIcon = GetSpellInfo, GetItemInfo, GetItemCount, GetItemIcon local GetShapeshiftFormInfo, GetShapeshiftForm = GetShapeshiftFormInfo, GetShapeshiftForm local GetRuneCooldown, UnitCastingInfo, UnitChannelInfo = GetRuneCooldown, UnitCastingInfo, UnitChannelInfo +local UnitDetailedThreatSituation, UnitThreatSituation = UnitDetailedThreatSituation, UnitThreatSituation local WeakAuras = WeakAuras local L = WeakAuras.L @@ -74,6 +75,17 @@ end LibRangeCheck:RegisterCallback(LibRangeCheck.CHECKERS_CHANGED, RangeCacheUpdate) +function WeakAuras.UnitDetailedThreatSituation(unit1, unit2) + local ok, aggro, status, threatpct, rawthreatpct, threatvalue = pcall(UnitDetailedThreatSituation, unit1, unit2) + if ok then + return aggro, status, threatpct, rawthreatpct, threatvalue + end +end + +local constants = { + nameRealmFilterDesc = L[" Filter formats: 'Name', 'Name-Realm', '-Realm'. \n\nSupports multiple entries, separated by commas\n"], +} + WeakAuras.function_strings = { count = [[ return function(count) @@ -718,15 +730,31 @@ WeakAuras.load_prototype = { }, { name = "name", - display = L["Player Name"], - type = "tristatestring", - init = "arg" + hidden = true, + init = "arg", + test = "true" }, { name = "realm", - display = L["Realm"], - type = "tristatestring", - init = "arg" + hidden = true, + init = "arg", + test = "true" + }, + { + name = "namerealm", + display = L["Player Name/Realm"], + type = "string", + test = "nameRealmChecker:Check(player, realm)", + preamble = "local nameRealmChecker = WeakAuras.ParseNameCheck(%q)", + desc = constants.nameRealmFilterDesc, + }, + { + name = "namerealmblack", + display = L["Blacklisted Player Name/Realm"], + type = "string", + test = "not nameRealmBlacklistChecker:Check(player, realm)", + preamble = "local nameRealmBlacklistChecker = WeakAuras.ParseNameCheck(%q)", + desc = constants.nameRealmFilterDesc, }, { name = "class", @@ -927,6 +955,10 @@ WeakAuras.event_prototypes = { local result = {} AddUnitEventForEvents(result, unit, "UNIT_LEVEL") AddUnitEventForEvents(result, unit, "UNIT_FACTION") + AddUnitEventForEvents(result, unit, "UNIT_NAME_UPDATE") + if trigger.use_ignoreDead or trigger.use_ignoreDisconnected then + AddUnitEventForEvents(result, unit, "UNIT_FLAGS") + end return result; end, internal_events = function(trigger) @@ -946,6 +978,7 @@ WeakAuras.event_prototypes = { unit = string.lower(unit) local smart = %s local extraUnit = %q; + local name, realm = WeakAuras.UnitNameWithRealm(unit) ]=]; ret = ret .. unitHelperFunctions.SpecificUnitCheck(trigger) @@ -980,9 +1013,32 @@ WeakAuras.event_prototypes = { name = "name", display = L["Name"], type = "string", - init = "UnitName(unit)", store = true, - conditionType = "string" + hidden = true, + test = "true" + }, + { + name = "realm", + display = L["Realm"], + type = "string", + store = true, + hidden = true, + test = "true" + }, + { + name = "namerealm", + display = L["Unit Name/Realm"], + type = "string", + preamble = "local nameRealmChecker = WeakAuras.ParseNameCheck(%q)", + test = "nameRealmChecker:Check(name, realm)", + conditionType = "string", + conditionPreamble = function(input) + return WeakAuras.ParseNameCheck(input) + end, + conditionTest = function(state, needle, op, preamble) + return preamble:Check(state.name, state.realm) + end, + operator_types = "none", }, { name = "class", @@ -1002,11 +1058,31 @@ WeakAuras.event_prototypes = { store = true, conditionType = "select" }, + { + name = "ignoreDead", + display = WeakAuras.newFeatureString .. L["Ignore Dead"], + type = "toggle", + width = WeakAuras.doubleWidth, + enable = function(trigger) + return trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party" + end, + init = "not UnitIsDeadOrGhost(unit)" + }, + { + name = "ignoreDisconnected", + display = WeakAuras.newFeatureString .. L["Ignore Disconnected"], + type = "toggle", + width = WeakAuras.doubleWidth, + enable = function(trigger) + return trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party" + end, + init = "UnitIsConnected(unit)" + }, { name = "hostility", display = L["Hostility"], type = "select", - init = "WeakAuras.GetPlayerReaciton(unit)", + init = "WeakAuras.GetPlayerReaction(unit)", values = "hostility_types", store = true, conditionType = "select", @@ -1058,6 +1134,10 @@ WeakAuras.event_prototypes = { local unit = trigger.unit local result = {} AddUnitEventForEvents(result, unit, "UNIT_HEALTH") + AddUnitEventForEvents(result, unit, "UNIT_NAME_UPDATE") + if trigger.use_ignoreDead or trigger.use_ignoreDisconnected then + AddUnitEventForEvents(result, unit, "UNIT_FLAGS") + end return result end, internal_events = function(trigger) @@ -1072,6 +1152,7 @@ WeakAuras.event_prototypes = { trigger.unit = trigger.unit or "player"; local ret = [=[ unit = string.lower(unit) + local name, realm = WeakAuras.UnitNameWithRealm(unit) local smart = %s ]=]; @@ -1132,9 +1213,33 @@ WeakAuras.event_prototypes = { name = "name", display = L["Unit Name"], type = "string", - init = "UnitName(unit)", store = true, - conditionType = "string" + hidden = true, + test = "true" + }, + { + name = "realm", + display = L["Realm"], + type = "string", + store = true, + hidden = true, + test = "true" + }, + { + name = "namerealm", + display = L["Unit Name/Realm"], + type = "string", + preamble = "local nameRealmChecker = WeakAuras.ParseNameCheck(%q)", + test = "nameRealmChecker:Check(name, realm)", + conditionType = "string", + conditionPreamble = function(input) + return WeakAuras.ParseNameCheck(input) + end, + conditionTest = function(state, needle, op, preamble) + return preamble:Check(state.name, state.realm) + end, + operator_types = "none", + desc = constants.nameRealmFilterDesc, }, { name = "npcId", @@ -1153,6 +1258,26 @@ WeakAuras.event_prototypes = { store = true, conditionType = "select" }, + { + name = "ignoreDead", + display = WeakAuras.newFeatureString .. L["Ignore Dead"], + type = "toggle", + width = WeakAuras.doubleWidth, + enable = function(trigger) + return trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party" + end, + init = "not UnitIsDeadOrGhost(unit)" + }, + { + name = "ignoreDisconnected", + display = WeakAuras.newFeatureString .. L["Ignore Disconnected"], + type = "toggle", + width = WeakAuras.doubleWidth, + enable = function(trigger) + return trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party" + end, + init = "UnitIsConnected(unit)" + }, { name = "name", hidden = true, @@ -1184,6 +1309,11 @@ WeakAuras.event_prototypes = { AddUnitEventForEvents(result, unit, "UNIT_MAXRUNIC_POWER") AddUnitEventForEvents(result, unit, "UNIT_DISPLAYPOWER") AddUnitEventForEvents(result, unit, "UNIT_HAPPINESS") + AddUnitEventForEvents(result, unit, "UNIT_NAME_UPDATE") + + if trigger.use_ignoreDead or trigger.use_ignoreDisconnected then + AddUnitEventForEvents(result, unit, "UNIT_FLAGS") + end return result; end, @@ -1200,6 +1330,7 @@ WeakAuras.event_prototypes = { trigger.unit = trigger.unit or "player"; local ret = [=[ unit = string.lower(unit) + local name, realm = WeakAuras.UnitNameWithRealm(unit) local smart = %s local powerType = %s; local unitPowerType = UnitPowerType(unit); @@ -1290,9 +1421,33 @@ WeakAuras.event_prototypes = { name = "name", display = L["Unit Name"], type = "string", - init = "UnitName(unit)", store = true, - conditionType = "string" + hidden = true, + test = "true" + }, + { + name = "realm", + display = L["Realm"], + type = "string", + store = true, + hidden = true, + test = "true" + }, + { + name = "namerealm", + display = L["Unit Name/Realm"], + type = "string", + preamble = "local nameRealmChecker = WeakAuras.ParseNameCheck(%q)", + test = "nameRealmChecker:Check(name, realm)", + conditionType = "string", + conditionPreamble = function(input) + return WeakAuras.ParseNameCheck(input) + end, + conditionTest = function(state, needle, op, preamble) + return preamble:Check(state.name, state.realm) + end, + operator_types = "none", + desc = constants.nameRealmFilterDesc, }, { name = "npcId", @@ -1311,6 +1466,26 @@ WeakAuras.event_prototypes = { store = true, conditionType = "select" }, + { + name = "ignoreDead", + display = WeakAuras.newFeatureString .. L["Ignore Dead"], + type = "toggle", + width = WeakAuras.doubleWidth, + enable = function(trigger) + return trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party" + end, + init = "not UnitIsDeadOrGhost(unit)" + }, + { + name = "ignoreDisconnected", + display = WeakAuras.newFeatureString .. L["Ignore Disconnected"], + type = "toggle", + width = WeakAuras.doubleWidth, + enable = function(trigger) + return trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party" + end, + init = "UnitIsConnected(unit)" + }, { hidden = true, test = "WeakAuras.UnitExistsFixed(unit, smart) and specificUnitCheck" @@ -1341,7 +1516,8 @@ WeakAuras.event_prototypes = { init = "arg", hidden = "true", test = "true", - store = true + store = true, + display = L["Source GUID"] }, { name = "sourceUnit", @@ -1415,7 +1591,8 @@ WeakAuras.event_prototypes = { init = "arg", hidden = "true", test = "true", - store = true + store = true, + display = L["Destination GUID"] }, { name = "destUnit", @@ -4401,26 +4578,42 @@ WeakAuras.event_prototypes = { }, ["Threat Situation"] = { type = "status", - events = { - ["unit_events"] = { - ["player"] = {"UNIT_THREAT_SITUATION_UPDATE"} - } - }, - internal_events = function(trigger) + events = function(trigger) local result = {} - AddUnitChangeInternalEvents(trigger.unit, result) + if trigger.threatUnit and trigger.threatUnit ~= "none" then + AddUnitEventForEvents(result, trigger.threatUnit, "UNIT_THREAT_LIST_UPDATE") + else + AddUnitEventForEvents(result, "player", "UNIT_THREAT_SITUATION_UPDATE") + end return result end, - force_events = "UNIT_THREAT_SITUATION_UPDATE", + internal_events = function(trigger) + local result = {} + if trigger.threatUnit and trigger.threatUnit ~= "none" then + AddUnitChangeInternalEvents(trigger.threatUnit, result) + end + return result + end, + force_events = "UNIT_THREAT_LIST_UPDATE", name = L["Threat Situation"], init = function(trigger) local ret = [[ - local status = UnitThreatSituation('player', %s) or -1; - local aggro = status == 2 or status == 3; + local unit = %s + local ok = true + local aggro, status, threatpct, rawthreatpct, threatvalue, threattotal + if unit then + aggro, status, threatpct, rawthreatpct, threatvalue = WeakAuras.UnitDetailedThreatSituation('player', unit) + threattotal = (threatvalue or 0) * 100 / (threatpct or 1) + else + status = UnitThreatSituation('player') + aggro = status == 2 or status == 3 + threatpct, rawthreatpct, threatvalue, threattotal = 100, 100, 0, 100 + end ]]; - return ret:format(trigger.threatUnit and trigger.threatUnit ~= "none" and "[["..trigger.threatUnit.."]]" or "nil"); end, + canHaveDuration = true, + statesParameter = "one", args = { { name = "threatUnit", @@ -4435,17 +4628,68 @@ WeakAuras.event_prototypes = { name = "status", display = L["Status"], type = "select", - values = "unit_threat_situation_types" + values = "unit_threat_situation_types", + store = true, + conditionType = "select" }, { name = "aggro", display = L["Aggro"], - type = "tristate" + store = true, + conditionType = "bool", + }, + { + name = "threatpct", + display = L["Threat Percent"], + desc = L["Your threat on the mob as a percentage of the amount required to pull aggro. Will pull aggro at 100."], + type = "number", + store = true, + conditionType = "number", + enable = function(trigger) return trigger.threatUnit ~= "none" end, + }, + { + name = "rawthreatpct", + display = L["Raw Threat Percent"], + desc = L["Your threat as a percentage of the tank's current threat."], + type = "number", + store = true, + conditionType = "number", + enable = function(trigger) return trigger.threatUnit ~= "none" end, + }, + { + name = "threatvalue", + display = L["Threat Value"], + desc = L["Your total threat on the mob."], + type = "number", + store = true, + conditionType = "number", + enable = function(trigger) return trigger.threatUnit ~= "none" end, + }, + { + name = "value", + hidden = true, + init = "threatvalue", + store = true, + test = "true" + }, + { + name = "total", + hidden = true, + init = "threattotal", + store = true, + test = "true" + }, + { + name = "progressType", + hidden = true, + init = "'static'", + store = true, + test = "true" }, { hidden = true, - test = "status ~= -1" - }, + test = "status ~= nil and ok" + } }, automaticrequired = true }, @@ -4483,6 +4727,7 @@ WeakAuras.event_prototypes = { AddUnitEventForEvents(result, unit, "UNIT_SPELLCAST_NOT_INTERRUPTIBLE") AddUnitEventForEvents(result, unit, "UNIT_SPELLCAST_INTERRUPTED") AddUnitEventForEvents(result, unit, "UNIT_TARGET") + AddUnitEventForEvents(result, unit, "UNIT_NAME_UPDATE") return result end, internal_events = function(trigger) @@ -4499,6 +4744,11 @@ WeakAuras.event_prototypes = { trigger.unit = trigger.unit or "player"; local ret = [=[ unit = string.lower(unit) + local destUnit = unit .. '-target' + local sourceName, sourceRealm = WeakAuras.UnitNameWithRealm(unit) + local destName, destRealm = WeakAuras.UnitNameWithRealm(destUnit) + destName = destName or "" + destRealm = destRealm or "" local smart = %s local remainingCheck = %s local inverseTrigger = %s @@ -4678,18 +4928,43 @@ WeakAuras.event_prototypes = { { name = "sourceName", display = L["Caster Name"], + type = "string", store = true, hidden = true, test = "true", - init = "UnitName(unit)", enable = function(trigger) return not trigger.use_inverse end, }, + { + name = "sourceRealm", + display = L["Caster Realm"], + type = "string", + store = true, + hidden = true, + test = "true", + enable = function(trigger) return not trigger.use_inverse end, + }, + { + name = "sourceNameRealm", + display = L["Source Unit Name/Realm"], + type = "string", + preamble = "local sourceNameRealmChecker = WeakAuras.ParseNameCheck(%q)", + test = "sourceNameRealmChecker:Check(sourceName, sourceRealm)", + conditionType = "string", + conditionPreamble = function(input) + return WeakAuras.ParseNameCheck(input) + end, + conditionTest = function(state, needle, op, preamble) + return preamble:Check(state.sourceName, state.sourceRealm) + end, + operator_types = "none", + enable = function(trigger) return not trigger.use_inverse end, + desc = constants.nameRealmFilterDesc, + }, { name = "destUnit", display = L["Caster's Target "], type = "unit", values = "actual_unit_types_with_specific", - init = "unit .. '-target'", conditionType = "unit", conditionTest = function(state, unit, op) return state and state.show and state.destUnit and ((UnitIsUnit(state.destUnit, unit) == 1 and true or false) == (op == "==")) @@ -4701,12 +4976,38 @@ WeakAuras.event_prototypes = { { name = "destName", display = L["Name of Caster's Target"], + type = "string", store = true, hidden = true, test = "true", - init = "UnitName(destUnit)", enable = function(trigger) return not trigger.use_inverse end, }, + { + name = "destRealm", + display = L["Realm of Caster's Target"], + type = "string", + store = true, + hidden = true, + test = "true", + enable = function(trigger) return not trigger.use_inverse end, + }, + { + name = "destNameRealm", + display = L["Name/Realm of Caster's Target"], + type = "string", + preamble = "local destNameRealmChecker = WeakAuras.ParseNameCheck(%q)", + test = "destNameRealmChecker:Check(destName, destRealm)", + conditionType = "string", + conditionPreamble = function(input) + return WeakAuras.ParseNameCheck(input) + end, + conditionTest = function(state, needle, op, preamble) + return preamble:Check(state.destName, state.destRealm) + end, + operator_types = "none", + enable = function(trigger) return not trigger.use_inverse end, + desc = constants.nameRealmFilterDesc, + }, { name = "inverse", display = L["Inverse"], @@ -5336,7 +5637,7 @@ WeakAuras.event_prototypes = { name = "range", display = L["Distance"], type = "number", - operator_types_without_equal = true, + operator_types = "without_equal", test = "triggerResult", conditionType = "number", conditionTest = function(state, needle, needle2) @@ -5360,97 +5661,121 @@ WeakAuras.event_prototypes["BigWigs Timer"] = nil WeakAuras.dynamic_texts = { ["p"] = { - func = function(state, region) - if not state then return "" end + get = function(state) + if not state then return nil end if state.progressType == "static" then - return state.value or "" + return state.value or nil end if state.progressType == "timed" then if not state.expirationTime or not state.duration then - return "" + return nil end local remaining = state.expirationTime - GetTime(); - local duration = state.duration; - - local remainingStr = ""; - if remaining == math.huge then - remainingStr = " "; - elseif remaining > 60 then - remainingStr = string.format("%i:", math.floor(remaining / 60)); - remaining = remaining % 60; - remainingStr = remainingStr..string.format("%02i", remaining); - elseif remaining > 0 then - local progressPrecision = region.progressPrecision and math.abs(region.progressPrecision) or 1 - -- remainingStr = remainingStr..string.format("%."..(data.progressPrecision or 1).."f", remaining); - if progressPrecision == 4 and remaining <= 3 then - remainingStr = remainingStr..string.format("%.1f", remaining); - elseif progressPrecision == 5 and remaining <= 3 then - remainingStr = remainingStr..string.format("%.2f", remaining); - elseif (progressPrecision == 4 or progressPrecision == 5) and remaining > 3 then - remainingStr = remainingStr..string.format("%d", remaining); - else - remainingStr = remainingStr..string.format("%.".. progressPrecision .."f", remaining); - end - else - remainingStr = " "; - end - return remainingStr + return remaining >= 0 and remaining or nil end + end, + func = function(remaining, state, progressPrecision) + progressPrecision = progressPrecision or 1 + if not state or state.progressType ~= "timed" then + return remaining + end + if type(remaining) ~= "number" then + return "" + end + + local remainingStr = ""; + if remaining == math.huge then + remainingStr = " "; + elseif remaining > 60 then + remainingStr = string.format("%i:", math.floor(remaining / 60)); + remaining = remaining % 60; + remainingStr = remainingStr..string.format("%02i", remaining); + elseif remaining > 0 then + if progressPrecision == 4 and remaining <= 3 then + remainingStr = remainingStr..string.format("%.1f", remaining); + elseif progressPrecision == 5 and remaining <= 3 then + remainingStr = remainingStr..string.format("%.2f", remaining); + elseif progressPrecision == 6 and remaining <= 3 then + remainingStr = remainingStr..string.format("%.3f", remaining); + elseif (progressPrecision == 4 or progressPrecision == 5 or progressPrecision == 6) and remaining > 3 then + remainingStr = remainingStr..string.format("%d", remaining); + else + remainingStr = remainingStr..string.format("%.".. progressPrecision .."f", remaining); + end + else + remainingStr = " "; + end + return remainingStr end }, ["t"] = { - func = function(state, region) + get = function(state) if not state then return "" end if state.progressType == "static" then - return state.total or "" + return state.total, false end if state.progressType == "timed" then if not state.duration then - return "" + return nil end - -- Format a duration time string - local durationStr = ""; - local duration = state.duration - if math.abs(duration) == math.huge or tostring(duration) == "nan" then - durationStr = " "; - elseif duration > 60 then - durationStr = string.format("%i:", math.floor(duration / 60)); - duration = duration % 60; - durationStr = durationStr..string.format("%02i", duration); - elseif duration > 0 then - local totalPrecision = region.totalPrecision and math.abs(region.totalPrecision) or 1 - if totalPrecision == 4 and duration <= 3 then - durationStr = durationStr..string.format("%.1f", duration); - elseif totalPrecision == 5 and duration <= 3 then - durationStr = durationStr..string.format("%.2f", duration); - elseif (totalPrecision == 4 or totalPrecision == 5) and duration > 3 then - durationStr = durationStr..string.format("%d", duration); - else - durationStr = durationStr..string.format("%."..totalPrecision.."f", duration); - end - else - durationStr = " "; - end - return durationStr + return state.duration, true end + end, + func = function(duration, state, totalPrecision) + if state.progressType ~= "timed" then + return duration + end + if type(duration) ~= "number" then + return "" + end + local durationStr = ""; + if math.abs(duration) == math.huge or tostring(duration) == "nan" then + durationStr = " "; + elseif duration > 60 then + durationStr = string.format("%i:", math.floor(duration / 60)); + duration = duration % 60; + durationStr = durationStr..string.format("%02i", duration); + elseif duration > 0 then + if totalPrecision == 4 and duration <= 3 then + durationStr = durationStr..string.format("%.1f", duration); + elseif totalPrecision == 5 and duration <= 3 then + durationStr = durationStr..string.format("%.2f", duration); + elseif totalPrecision == 6 and duration <= 3 then + durationStr = durationStr..string.format("%.3f", duration); + elseif (totalPrecision == 4 or totalPrecision == 5 or totalPrecision == 6) and duration > 3 then + durationStr = durationStr..string.format("%d", duration); + else + durationStr = durationStr..string.format("%."..totalPrecision.."f", duration); + end + else + durationStr = " "; + end + return durationStr end }, ["n"] = { - func = function(state) - if not state then return "" end - return state.name or state.id + get = function(state) + return state.name or state.id or "", true + end, + func = function(v) + return v end }, ["i"] = { - func = function(state) - if not state or not state.icon then return "|TInterface\\Icons\\INV_Misc_QuestionMark:12:12:0:0:64:64:4:60:4:60|t" end - return "|T".. state.icon ..":12:12:0:0:64:64:4:60:4:60|t" + get = function(state) + return state.icon or "Interface\\Icons\\INV_Misc_QuestionMark" + end, + func = function(v) + return "|T".. v ..":12:12:0:0:64:64:4:60:4:60|t" end }, ["s"] = { - func = function(state) + get = function(state) if not state or state.stacks == 0 then return "" end return state.stacks + end, + func = function(v) + return v end } }; diff --git a/WeakAuras/RegionTypes/AuraBar.lua b/WeakAuras/RegionTypes/AuraBar.lua index d78b19d..36b8d5b 100644 --- a/WeakAuras/RegionTypes/AuraBar.lua +++ b/WeakAuras/RegionTypes/AuraBar.lua @@ -53,6 +53,11 @@ local properties = { setter = "Color", type = "color", }, + icon_visible = { + display = L["Icon Visible"], + setter = "SetIconVisible", + type = "bool" + }, icon_color = { display = L["Icon Color"], setter = "SetIconColor", @@ -552,6 +557,151 @@ local barPrototype = { ["orientation"] = "HORIZONTAL", } +local GetRealSize = { + ["HORIZONTAL"] = { + [true] = function(self) + return self.totalWidth - self.iconWidth, self.totalHeight + end, + [false] = function(self) + return self.totalWidth, self.totalHeight + end + }, + ["VERTICAL"] = { + [true] = function(self) + return self.totalWidth, self.totalHeight - self.iconHeight + end, + [false] = function(self) + return self.totalWidth, self.totalHeight + end + }, +} + +-- Orientation helper methods +local function orientHorizontalInverse(region) + -- Localize + local bar, icon = region.bar, region.icon; + + -- Reset + icon:ClearAllPoints(); + bar:ClearAllPoints(); + + bar.GetRealSize = GetRealSize["HORIZONTAL"][region.iconVisible or false] + + -- Align icon and bar + if region.iconVisible then + if region.icon_side == "LEFT" then + icon:SetPoint("LEFT", region, "LEFT"); + bar:SetPoint("BOTTOMRIGHT", region, "BOTTOMRIGHT"); + bar:SetPoint("TOPLEFT", icon, "TOPRIGHT"); + else + icon:SetPoint("RIGHT", region, "RIGHT"); + bar:SetPoint("BOTTOMLEFT", region, "BOTTOMLEFT"); + bar:SetPoint("TOPRIGHT", icon, "TOPLEFT"); + end + else + bar:SetPoint("BOTTOMRIGHT", region, "BOTTOMRIGHT"); + bar:SetPoint("TOPLEFT", region, "TOPLEFT"); + end + + -- Save orientation + bar:SetOrientation(region.effectiveOrientation); +end + +local function orientHorizontal(region) + -- Localize + local bar, icon = region.bar, region.icon; + + bar.GetRealSize = GetRealSize["HORIZONTAL"][region.iconVisible or false] + + -- Reset + icon:ClearAllPoints(); + bar:ClearAllPoints(); + + -- Align icon and bar + if region.iconVisible then + if region.icon_side == "LEFT" then + icon:SetPoint("LEFT", region, "LEFT"); + bar:SetPoint("BOTTOMRIGHT", region, "BOTTOMRIGHT"); + bar:SetPoint("TOPLEFT", icon, "TOPRIGHT"); + else + icon:SetPoint("RIGHT", region, "RIGHT"); + bar:SetPoint("BOTTOMLEFT", region, "BOTTOMLEFT"); + bar:SetPoint("TOPRIGHT", icon, "TOPLEFT"); + end + else + bar:SetPoint("BOTTOMRIGHT", region, "BOTTOMRIGHT"); + bar:SetPoint("TOPLEFT", region, "TOPLEFT"); + end + + -- Save orientation + bar:SetOrientation(region.effectiveOrientation); +end + +local function orientVerticalInverse(region) + -- Localize + local bar, icon = region.bar, region.icon; + + bar.GetRealSize = GetRealSize["VERTICAL"][region.iconVisible or false] + + -- Reset + icon:ClearAllPoints(); + bar:ClearAllPoints(); + + -- Align icon and bar + if region.iconVisible then + if region.icon_side == "LEFT" then + icon:SetPoint("TOP", region, "TOP"); + bar:SetPoint("BOTTOMRIGHT", region, "BOTTOMRIGHT"); + bar:SetPoint("TOPLEFT", icon, "BOTTOMLEFT"); + else + icon:SetPoint("BOTTOM", region, "BOTTOM"); + bar:SetPoint("TOPRIGHT", region, "TOPRIGHT"); + bar:SetPoint("BOTTOMLEFT", icon, "TOPLEFT"); + end + else + bar:SetPoint("BOTTOMRIGHT", region, "BOTTOMRIGHT"); + bar:SetPoint("TOPLEFT", region, "TOPLEFT"); + end + + -- Save orientation + bar:SetOrientation("VERTICAL_INVERSE"); +end + +local function orientVertical(region) + -- Localize + local bar, icon = region.bar, region.icon; + + bar.GetRealSize = GetRealSize["VERTICAL"][region.iconVisible or false] + + -- Reset + icon:ClearAllPoints(); + bar:ClearAllPoints(); + + -- Align icon and bar + if region.iconVisible then + if region.icon_side == "LEFT" then + icon:SetPoint("TOP", region, "TOP"); + bar:SetPoint("BOTTOMRIGHT", region, "BOTTOMRIGHT"); + bar:SetPoint("TOPLEFT", icon, "BOTTOMLEFT"); + else + icon:SetPoint("BOTTOM", region, "BOTTOM"); + bar:SetPoint("TOPRIGHT", region, "TOPRIGHT"); + bar:SetPoint("BOTTOMLEFT", icon, "TOPLEFT"); + end + else + bar:SetPoint("BOTTOMRIGHT", region, "BOTTOMRIGHT"); + bar:SetPoint("TOPLEFT", region, "TOPLEFT"); + end + + -- Save orientation + bar:SetOrientation("VERTICAL"); +end + +local function GetTexCoordZoom(texWidth) + local texCoord = {texWidth, texWidth, texWidth, 1 - texWidth, 1 - texWidth, texWidth, 1 - texWidth, 1 - texWidth} + return unpack(texCoord) +end + local funcs = { AnchorSubRegion = function(self, subRegion, anchorType, selfPoint, anchorPoint, anchorXOffset, anchorYOffset) if anchorType == "area" then @@ -616,9 +766,11 @@ local funcs = { end end, SetIconColor = function(self, r, g, b, a) + self.icon_color = {r, g, b, a} self.icon:SetVertexColor(r, g, b, a); end, SetIconDesaturated = function(self, b) + self.desaturateIcon = b self.icon:SetDesaturated(b); end, SetBackgroundColor = function (self, r, g, b, a) @@ -679,6 +831,38 @@ local funcs = { self:UpdateEffectiveOrientation() self.bar:SetValue(self.bar:GetValue()); end, + + SetIconVisible = function(self, iconVisible) + if (self.iconVisible == iconVisible) then + return + end + + self.iconVisible = iconVisible + + local icon = self.icon + if self.iconVisible then + -- Update icon + local iconsize = math.min(self.height, self.width); + icon:SetWidth(iconsize); + icon:SetHeight(iconsize); + self.bar.iconWidth = iconsize + self.bar.iconHeight = iconsize + local texWidth = 0.25 * self.zoom; + icon:SetTexCoord(GetTexCoordZoom(texWidth)) + icon:SetDesaturated(self.desaturateIcon); + icon:SetVertexColor(self.icon_color[1], self.icon_color[2], self.icon_color[3], self.icon_color[4]); + + -- Update icon visibility + icon:Show(); + else + self.bar.iconWidth = 0 + self.bar.iconHeight = 0 + icon:Hide(); + end + + self:ReOrient() + self.subRegionEvents:Notify("OrientationChanged") + end, SetOverlayColor = function(self, id, r, g, b, a) self.bar:SetAdditionalBarColor(id, { r, g, b, a}); end, @@ -687,6 +871,42 @@ local funcs = { end, GetInverse = function(self) return self.inverseDirection + end, + ReOrient = function(self) + if self.effectiveOrientation == "HORIZONTAL_INVERSE" then + orientHorizontalInverse(self); + elseif self.effectiveOrientation == "HORIZONTAL" then + orientHorizontal(self); + elseif self.effectiveOrientation == "VERTICAL_INVERSE" then + orientVerticalInverse(self); + elseif self.effectiveOrientation == "VERTICAL" then + orientVertical(self); + end + end, + UpdateEffectiveOrientation = function(self) + local orientation = self.orientation + + if self.flipX then + if self.orientation == "HORIZONTAL" then + orientation = "HORIZONTAL_INVERSE" + elseif self.orientation == "HORIZONTAL_INVERSE" then + orientation = "HORIZONTAL" + end + end + if self.flipY then + if self.orientation == "VERTICAL" then + orientation = "VERTICAL_INVERSE" + elseif self.orientation == "VERTICAL_INVERSE" then + orientation = "VERTICAL" + end + end + + if orientation ~= self.effectiveOrientation then + self.effectiveOrientation = orientation + self:ReOrient() + end + + self.subRegionEvents:Notify("OrientationChanged") end } @@ -746,151 +966,6 @@ local function create(parent) return region; end -local GetRealSize = { - ["HORIZONTAL"] = { - [true] = function(self) - return self.totalWidth - self.iconWidth, self.totalHeight - end, - [false] = function(self) - return self.totalWidth, self.totalHeight - end - }, - ["VERTICAL"] = { - [true] = function(self) - return self.totalWidth, self.totalHeight - self.iconHeight - end, - [false] = function(self) - return self.totalWidth, self.totalHeight - end - }, -} - --- Orientation helper methods -local function orientHorizontalInverse(region, data) - -- Localize - local bar, icon = region.bar, region.icon; - - -- Reset - icon:ClearAllPoints(); - bar:ClearAllPoints(); - - bar.GetRealSize = GetRealSize["HORIZONTAL"][data.icon or false] - - -- Align icon and bar - if data.icon then - if data.icon_side == "LEFT" then - icon:SetPoint("LEFT", region, "LEFT"); - bar:SetPoint("BOTTOMRIGHT", region, "BOTTOMRIGHT"); - bar:SetPoint("TOPLEFT", icon, "TOPRIGHT"); - else - icon:SetPoint("RIGHT", region, "RIGHT"); - bar:SetPoint("BOTTOMLEFT", region, "BOTTOMLEFT"); - bar:SetPoint("TOPRIGHT", icon, "TOPLEFT"); - end - else - bar:SetPoint("BOTTOMRIGHT", region, "BOTTOMRIGHT"); - bar:SetPoint("TOPLEFT", region, "TOPLEFT"); - end - - -- Save orientation - bar:SetOrientation(region.effectiveOrientation); -end - -local function orientHorizontal(region, data) - -- Localize - local bar, icon = region.bar, region.icon; - - bar.GetRealSize = GetRealSize["HORIZONTAL"][data.icon or false] - - -- Reset - icon:ClearAllPoints(); - bar:ClearAllPoints(); - - -- Align icon and bar - if data.icon then - if data.icon_side == "LEFT" then - icon:SetPoint("LEFT", region, "LEFT"); - bar:SetPoint("BOTTOMRIGHT", region, "BOTTOMRIGHT"); - bar:SetPoint("TOPLEFT", icon, "TOPRIGHT"); - else - icon:SetPoint("RIGHT", region, "RIGHT"); - bar:SetPoint("BOTTOMLEFT", region, "BOTTOMLEFT"); - bar:SetPoint("TOPRIGHT", icon, "TOPLEFT"); - end - else - bar:SetPoint("BOTTOMRIGHT", region, "BOTTOMRIGHT"); - bar:SetPoint("TOPLEFT", region, "TOPLEFT"); - end - - -- Save orientation - bar:SetOrientation(region.effectiveOrientation); -end - -local function orientVerticalInverse(region, data) - -- Localize - local bar, icon = region.bar, region.icon; - - bar.GetRealSize = GetRealSize["VERTICAL"][data.icon or false] - - -- Reset - icon:ClearAllPoints(); - bar:ClearAllPoints(); - - -- Align icon and bar - if data.icon then - if data.icon_side == "LEFT" then - icon:SetPoint("TOP", region, "TOP"); - bar:SetPoint("BOTTOMRIGHT", region, "BOTTOMRIGHT"); - bar:SetPoint("TOPLEFT", icon, "BOTTOMLEFT"); - else - icon:SetPoint("BOTTOM", region, "BOTTOM"); - bar:SetPoint("TOPRIGHT", region, "TOPRIGHT"); - bar:SetPoint("BOTTOMLEFT", icon, "TOPLEFT"); - end - else - bar:SetPoint("BOTTOMRIGHT", region, "BOTTOMRIGHT"); - bar:SetPoint("TOPLEFT", region, "TOPLEFT"); - end - - -- Save orientation - bar:SetOrientation("VERTICAL_INVERSE"); -end - -local function orientVertical(region, data) - -- Localize - local bar, icon = region.bar, region.icon; - - bar.GetRealSize = GetRealSize["VERTICAL"][data.icon or false] - - -- Reset - icon:ClearAllPoints(); - bar:ClearAllPoints(); - - -- Align icon and bar - if data.icon then - if data.icon_side == "LEFT" then - icon:SetPoint("TOP", region, "TOP"); - bar:SetPoint("BOTTOMRIGHT", region, "BOTTOMRIGHT"); - bar:SetPoint("TOPLEFT", icon, "BOTTOMLEFT"); - else - icon:SetPoint("BOTTOM", region, "BOTTOM"); - bar:SetPoint("TOPRIGHT", region, "TOPRIGHT"); - bar:SetPoint("BOTTOMLEFT", icon, "TOPLEFT"); - end - else - bar:SetPoint("BOTTOMRIGHT", region, "BOTTOMRIGHT"); - bar:SetPoint("TOPLEFT", region, "TOPLEFT"); - end - - -- Save orientation - bar:SetOrientation("VERTICAL"); -end - -local function GetTexCoordZoom(texWidth) - local texCoord = {texWidth, texWidth, texWidth, 1 - texWidth, 1 - texWidth, texWidth, 1 - texWidth, 1 - texWidth} - return unpack(texCoord) -end - local function TimerTick(self) local state = self.state local duration = state.duration or 0 @@ -927,6 +1002,11 @@ local function modify(parent, region, data) region.effectiveOrientation = nil region.overlayclip = data.overlayclip; + region.iconVisible = data.icon + region.icon_side = data.icon_side + region.icon_color = CopyTable(data.icon_color) + region.desaturateIcon = data.desaturate + region.zoom = data.zoom region.overlays = {}; if (data.overlays) then @@ -938,7 +1018,7 @@ local function modify(parent, region, data) bar:SetStatusBarTexture(texturePath); bar:SetBackgroundColor(data.backgroundColor[1], data.backgroundColor[2], data.backgroundColor[3], data.backgroundColor[4]); -- Update spark settings - WeakAuras.SetTexture(bar.spark, data.sparkTexture); + bar.spark:SetTexture(data.sparkTexture); bar.spark:SetVertexColor(data.sparkColor[1], data.sparkColor[2], data.sparkColor[3], data.sparkColor[4]); bar.spark:SetWidth(data.sparkWidth); bar.spark:SetHeight(data.sparkHeight); @@ -977,7 +1057,7 @@ local function modify(parent, region, data) local textDegrees = data.rotateText == "LEFT" and 90 or data.rotateText == "RIGHT" and -90 or 0; -- Update icon visibility - if data.icon then + if region.iconVisible then -- Update icon local iconsize = math.min(region.height, region.width); icon:SetWidth(iconsize); @@ -999,40 +1079,6 @@ local function modify(parent, region, data) region.inverseDirection = data.inverse; - region.UpdateEffectiveOrientation = function() - local orientation = region.orientation - - if region.flipX then - if region.orientation == "HORIZONTAL" then - orientation = "HORIZONTAL_INVERSE" - elseif region.orientation == "HORIZONTAL_INVERSE" then - orientation = "HORIZONTAL" - end - end - if region.flipY then - if region.orientation == "VERTICAL" then - orientation = "VERTICAL_INVERSE" - elseif region.orientation == "VERTICAL_INVERSE" then - orientation = "VERTICAL" - end - end - - if orientation ~= region.effectiveOrientation then - region.effectiveOrientation = orientation - if region.effectiveOrientation == "HORIZONTAL_INVERSE" then - orientHorizontalInverse(region, data); - elseif region.effectiveOrientation == "HORIZONTAL" then - orientHorizontal(region, data); - elseif region.effectiveOrientation == "VERTICAL_INVERSE" then - orientVerticalInverse(region, data); - elseif region.effectiveOrientation == "VERTICAL" then - orientVertical(region, data); - end - end - - region.subRegionEvents:Notify("OrientationChanged") - end - -- Apply orientation alignment region:UpdateEffectiveOrientation() diff --git a/WeakAuras/RegionTypes/RegionPrototype.lua b/WeakAuras/RegionTypes/RegionPrototype.lua index 0884c59..6079d61 100644 --- a/WeakAuras/RegionTypes/RegionPrototype.lua +++ b/WeakAuras/RegionTypes/RegionPrototype.lua @@ -117,7 +117,7 @@ local screenWidth, screenHeight = math.ceil(GetScreenWidth() / 20) * 20, math.ce function WeakAuras.GetAnchorsForData(parentData, type) local result if not parentData.controlledChildren then - if not WeakAuras.regionOptions[parentData.regionType].getAnchors then + if not WeakAuras.regionOptions[parentData.regionType] or not WeakAuras.regionOptions[parentData.regionType].getAnchors then return end @@ -258,7 +258,7 @@ local function SendChat(self, options) if (not options or WeakAuras.IsOptionsOpen()) then return end - WeakAuras.HandleChatAction(options.message_type, options.message, options.message_dest, options.message_channel, options.r, options.g, options.b, self, options.message_custom); + WeakAuras.HandleChatAction(options.message_type, options.message, options.message_dest, options.message_channel, options.r, options.g, options.b, self, options.message_custom, nil, options.message_formaters); end local function RunCode(self, func) @@ -532,6 +532,22 @@ function WeakAuras.regionPrototype.modify(parent, region, data) WeakAuras.AnchorFrame(data, region, parent); end end + + region.startFormatters = WeakAuras.CreateFormatters(data.actions.start.message, function(key, default) + local fullKey = "message_format_" .. key + if data.actions.start[fullKey] == nil then + data.actions.start[fullKey] = default + end + return data.actions.start[fullKey] + end) + + region.finishFormatters = WeakAuras.CreateFormatters(data.actions.finish.message, function(key, default) + local fullKey = "message_format_" .. key + if data.actions.finish[fullKey] == nil then + data.actions.finish[fullKey] = default + end + return data.actions.finish[fullKey] + end) end function WeakAuras.regionPrototype.modifyFinish(parent, region, data) @@ -849,7 +865,3 @@ function WeakAuras.regionPrototype.AddExpandFunction(data, region, cloneId, pare function region:Expand() end end end - -function WeakAuras.SetTexture(texture, path, wrapModeH, wrapModeV) - texture:SetTexture(path, wrapModeH, wrapModeV); -end diff --git a/WeakAuras/RegionTypes/Text.lua b/WeakAuras/RegionTypes/Text.lua index 2d0fdd3..9c26f47 100644 --- a/WeakAuras/RegionTypes/Text.lua +++ b/WeakAuras/RegionTypes/Text.lua @@ -6,7 +6,6 @@ local L = WeakAuras.L; local defaultFont = WeakAuras.defaultFont local defaultFontSize = WeakAuras.defaultFontSize - local default = { displayText = "%p", outline = "OUTLINE", @@ -76,8 +75,6 @@ local function modify(parent, region, data) local text = region.text; region.useAuto = WeakAuras.CanHaveAuto(data); - region.progressPrecision = data.progressPrecision; - region.totalPrecision = data.totalPrecision; local fontPath = SharedMedia:Fetch("font", data.font); text:SetFont(fontPath, data.fontSize, data.outline); @@ -161,9 +158,17 @@ local function modify(parent, region, data) local UpdateText if WeakAuras.ContainsAnyPlaceHolders(data.displayText) then + local getter = function(key, default) + local fullKey = "displayText_format_" .. key + if (data[fullKey] == nil) then + data[fullKey] = default + end + return data[fullKey] + end + local formatters = WeakAuras.CreateFormatters(data.displayText, getter) UpdateText = function() local textStr = data.displayText; - textStr = WeakAuras.ReplacePlaceHolders(textStr, region, nil); + textStr = WeakAuras.ReplacePlaceHolders(textStr, region, nil, false, formatters); if (textStr == nil or textStr == "") then textStr = " "; end diff --git a/WeakAuras/RegionTypes/Texture.lua b/WeakAuras/RegionTypes/Texture.lua index 0a8a0a0..ee37697 100644 --- a/WeakAuras/RegionTypes/Texture.lua +++ b/WeakAuras/RegionTypes/Texture.lua @@ -86,7 +86,7 @@ end local function modify(parent, region, data) WeakAuras.regionPrototype.modify(parent, region, data); - WeakAuras.SetTexture(region.texture, data.texture); + region.texture:SetTexture(data.texture); region.texture:SetDesaturated(data.desaturate) region:SetWidth(data.width); region:SetHeight(data.height); @@ -182,7 +182,7 @@ local function modify(parent, region, data) function region:Update() if region.state.texture then - WeakAuras.SetTexture(region.texture, region.state.texture); + region.texture:SetTexture(region.state.texture); end end diff --git a/WeakAuras/SubRegionTypes/SubText.lua b/WeakAuras/SubRegionTypes/SubText.lua index cfad242..8775855 100644 --- a/WeakAuras/SubRegionTypes/SubText.lua +++ b/WeakAuras/SubRegionTypes/SubText.lua @@ -225,12 +225,6 @@ local function modify(parent, region, parentData, data, first) end if first then - -- Certain data is stored directly on the parent, because it's shared between multiple texts - -- And shared by other code paths e.g. SendChatMessage - -- That is partly for legacy reasons - parent.progressPrecision = parentData.progressPrecision - parent.totalPrecision = parentData.totalPrecision - local containsCustomText = false for index, subRegion in ipairs(parentData.subRegions) do if subRegion.type == "subtext" and WeakAuras.ContainsCustomPlaceHolder(subRegion.text_text) then @@ -249,9 +243,17 @@ local function modify(parent, region, parentData, data, first) local UpdateText if data.text_text and WeakAuras.ContainsAnyPlaceHolders(data.text_text) then + local getter = function(key, default) + local fullKey = "text_text_format_" .. key + if data[fullKey] == nil then + data[fullKey] = default + end + return data[fullKey] + end + local formatters = WeakAuras.CreateFormatters(data.text_text, getter) UpdateText = function() local textStr = data.text_text or "" - textStr = WeakAuras.ReplacePlaceHolders(textStr, parent, nil) + textStr = WeakAuras.ReplacePlaceHolders(textStr, parent, nil, false, formatters) if text:GetFont() then text:SetText(WeakAuras.ReplaceRaidMarkerSymbols(textStr)) diff --git a/WeakAuras/Transmission.lua b/WeakAuras/Transmission.lua index d21acc0..6e97f64 100644 --- a/WeakAuras/Transmission.lua +++ b/WeakAuras/Transmission.lua @@ -1175,6 +1175,9 @@ local function findMatch(data, children) if old.uid ~= new.uid then return else + if (children == nil) ~= (old.controlledChildren == nil) then + return + end return true end else diff --git a/WeakAuras/Types.lua b/WeakAuras/Types.lua index 732186e..85a3e2a 100644 --- a/WeakAuras/Types.lua +++ b/WeakAuras/Types.lua @@ -68,10 +68,492 @@ WeakAuras.precision_types = { [1] = "12.3", [2] = "12.34", [3] = "12.345", - [4] = "Dynamic 12.3", -- will show 1 digit precision when time is lower than 3 seconds, hardcoded - [5] = "Dynamic 12.34" -- will show 2 digits precision when time is lower than 3 seconds, hardcoded } +WeakAuras.big_number_types = { + ["AbbreviateNumbers"] = L["AbbreviateNumbers (Blizzard)"], + ["AbbreviateLargeNumbers"] = L["AbbreviateLargeNumbers (Blizzard)"] +} + +WeakAuras.round_types = { + floor = L["Floor"], + ceil = L["Ceil"], + round = L["Round"] +} + +WeakAuras.unit_color_types = { + none = L["None"], + class = L["Class"] +} + +WeakAuras.unit_realm_name_types = { + never = L["Never"], + star = L["* Suffix"], + differentServer = L["Only if on a different realm"], + always = L["Always include realm"] +} + +local simpleFormatters = { +--[[ + AbbreviateNumbers = function(value, state) + return (type(value) == "number") and AbbreviateNumbers(value) or value + end, + AbbreviateLargeNumbers = function(value, state) + return (type(value) == "number") and AbbreviateLargeNumbers(Round(value)) or value + end, +]] + floor = function(value) + return (type(value) == "number") and floor(value) or value + end, + ceil = function(value) + return (type(value) == "number") and ceil(value) or value + end, + round = function(value) + return (type(value) == "number") and Round(value) or value + end +} + +WeakAuras.format_types = { + none = { + display = L["None"], + AddOptions = function() end, + CreateFormatter = function() end + }, + timed = { + display = L["Time Format"], + AddOptions = function(symbol, hidden, addOption, get) + addOption(symbol .. "_time_precision", { + type = "select", + name = L["Precision"], + width = WeakAuras.normalWidth, + values = WeakAuras.precision_types, + hidden = hidden + }) + addOption(symbol .. "_time_dynamic", { + type = "toggle", + name = L["Dynamic"], + desc = L["Increased Precision below 3s"], + width = WeakAuras.normalWidth, + hidden = hidden, + disabled = function() return get(symbol .. "_time_precision") == 0 end + }) + end, + CreateFormatter = function(symbol, get) + local precision = get(symbol .. "_time_precision", 1) + local dynamic = get(symbol .. "_time_dynamic", false) + + if dynamic then + if precision == 1 or precision == 2 or precision == 3 then + precision = precision + 3 + end + end + + return function(value, state) + return WeakAuras.dynamic_texts.p.func(value, state, precision) + end + end + }, +--[[ + BigNumber = { + display = L["Big Number"], + AddOptions = function(symbol, hidden, addOption) + addOption(symbol .. "_big_number_format", { + type = "select", + name = L["Format"], + width = WeakAuras.normalWidth, + values = WeakAuras.big_number_types, + hidden = hidden + }) + end, + CreateFormatter = function(symbol, get) + local format = get(symbol .. "_big_number_format", "AbbreviateNumbers") + if (format == "AbbreviateNumbers") then + return simpleFormatters.AbbreviateNumbers + end + return simpleFormatters.AbbreviateLargeNumbers + end + }, +]] + Number = { + display = L["Number"], + AddOptions = function(symbol, hidden, addOption, get) + addOption(symbol .. "_decimal_precision", { + type = "select", + name = L["Precision"], + width = WeakAuras.normalWidth, + values = WeakAuras.precision_types, + hidden = hidden + }) + addOption(symbol .. "_round_type", { + type = "select", + name = L["Round Mode"], + width = WeakAuras.normalWidth, + values = WeakAuras.round_types, + hidden = hidden, + disabled = function() + return get(symbol .. "_decimal_precision") ~= 0 + end + }) + end, + CreateFormatter = function(symbol, get) + local precision = get(symbol .. "_decimal_precision", 1) + if precision == 0 then + local type = get(symbol .. "_round_type", "floor") + return simpleFormatters[type] + else + local format = "%." .. precision .. "f" + return function(value) + return (type(value) == "number") and string.format(format, value) or value + end + end + end + }, + Unit = { + display = L["Formats |cFFFF0000%unit|r"], + AddOptions = function(symbol, hidden, addOption, get) + addOption(symbol .. "_color", { + type = "select", + name = L["Color"], + width = WeakAuras.normalWidth, + values = WeakAuras.unit_color_types, + hidden = hidden, + }) + addOption(symbol .. "_realm_name", { + type = "select", + name = L["Realm Name"], + width = WeakAuras.normalWidth, + values = WeakAuras.unit_realm_name_types, + hidden = hidden, + }) + addOption(symbol .. "_abbreviate", { + type = "toggle", + name = L["Abbreviate"], + width = WeakAuras.normalWidth, + hidden = hidden, + }) + addOption(symbol .. "_abbreviate_max", { + type = "range", + name = L["Max Char "], + width = WeakAuras.normalWidth, + hidden = hidden, + min = 1, + max = 20, + hidden = hidden, + disabled = function() + return not get(symbol .. "_abbreviate") + end + }) + end, + CreateFormatter = function(symbol, get) + local color = get(symbol .. "_color", true) + local realm = get(symbol .. "_realm_name", "never") + local abbreviate = get(symbol .. "_abbreviate", false) + local abbreviateMax = get(symbol .. "_abbreviate_max", 8) + + local nameFunc + local colorFunc + local abbreviateFunc + if color == "class" then + colorFunc = function(unit, text) + if UnitPlayerControlled(unit) then + return GetClassColoredTextForUnit(unit, text) + end + return text + end + end + + if realm == "never" then + nameFunc = UnitName + elseif realm == "star" then + nameFunc = function(unit) + local name, realm = UnitName(unit) + if realm then + return name .. "*" + end + return name + end + elseif realm == "differentServer" then + nameFunc = function(unit) + local name, realm = UnitName(unit) + if realm then + return name .. "-" .. realm + end + return name + end + elseif realm == "always" then + nameFunc = function(unit) + local name, realm = WeakAuras.UnitNameWithRealm(unit) + return name .. "-" .. realm + end + end + + if abbreviate then + abbreviateFunc = function(input) + return WeakAuras.WA_Utf8Sub(input, abbreviateMax) + end + end + + -- Do the checks on what is necessary here instead of inside the returned + -- formatter + if colorFunc then + if abbreviateFunc then + return function(unit) + local name = abbreviateFunc(nameFunc(unit)) + return colorFunc(unit, name) + end + else + return function(unit) + local name = nameFunc(unit) + return colorFunc(unit, name) + end + end + else + if abbreviateFunc then + return function(unit) + local name = nameFunc(unit) + return abbreviateFunc(name) + end + else + return nameFunc + end + end + end + }, + guid = { + display = L["Formats Player's |cFFFF0000%guid|r"], + AddOptions = function(symbol, hidden, addOption, get) + addOption(symbol .. "_color", { + type = "select", + name = L["Color"], + width = WeakAuras.normalWidth, + values = WeakAuras.unit_color_types, + hidden = hidden, + }) + addOption(symbol .. "_realm_name", { + type = "select", + name = L["Realm Name"], + width = WeakAuras.normalWidth, + values = WeakAuras.unit_realm_name_types, + hidden = hidden, + }) + addOption(symbol .. "_abbreviate", { + type = "toggle", + name = L["Abbreviate"], + width = WeakAuras.normalWidth, + hidden = hidden, + }) + addOption(symbol .. "_abbreviate_max", { + type = "range", + name = L["Max Char "], + width = WeakAuras.normalWidth, + hidden = hidden, + min = 1, + max = 20, + hidden = hidden, + disabled = function() + return not get(symbol .. "_abbreviate") + end + }) + end, + CreateFormatter = function(symbol, get) + local color = get(symbol .. "_color", true) + local realm = get(symbol .. "_realm_name", "never") + local abbreviate = get(symbol .. "_abbreviate", false) + local abbreviateMax = get(symbol .. "_abbreviate_max", 8) + + local nameFunc + local colorFunc + local abbreviateFunc + if color == "class" then + colorFunc = function(class, text) + local color = RAID_CLASS_COLORS[class] + if color then + return string.format("|c%s%s|r", string.format("ff%.2x%.2x%.2x", color.r * 255, color.g * 255, color.b * 255), text) + else + return text + end + end + end + + if realm == "never" then + nameFunc = function(name, realm) + return name + end + elseif realm == "star" then + nameFunc = function(name, realm) + if realm ~= "" then + return name .. "*" + end + return name + end + elseif realm == "differentServer" then + nameFunc = function(name, realm) + if realm ~= "" then + return name .. "-" .. realm + end + return name + end + elseif realm == "always" then + nameFunc = function(name, realm) + if realm == "" then + realm = select(2, WeakAuras.UnitNameWithRealm("player")) + end + return name .. "-" .. realm + end + end + + if abbreviate then + abbreviateFunc = function(input) + return WeakAuras.WA_Utf8Sub(input, abbreviateMax) + end + end + + -- Do the checks on what is necessary here instead of inside the returned + -- formatter + if colorFunc then + if abbreviateFunc then + return function(guid) + local ok, _, class, _, _, _, name, realm = pcall(GetPlayerInfoByGUID, guid) + if ok then + local name = abbreviateFunc(nameFunc(name, realm)) + return colorFunc(class, name) + end + end + else + return function(guid) + local ok, _, class, _, _, _, name, realm = pcall(GetPlayerInfoByGUID, guid) + if ok then + return colorFunc(class, nameFunc(name, realm)) + end + end + end + else + if abbreviateFunc then + return function(guid) + local ok, _, class, _, _, _, name, realm = pcall(GetPlayerInfoByGUID, guid) + if ok then + return abbreviateFunc(nameFunc(name, realm)) + end + end + else + return function(guid) + local ok, _, class, _, _, _, name, realm = pcall(GetPlayerInfoByGUID, guid) + if ok then + return nameFunc(name, realm) + end + end + end + end + end + }, + GCDTime = { + display = L["Time in GCDs"], + AddOptions = function(symbol, hidden, addOption, get) + addOption(symbol .. "_gcd_gcd", { + type = "toggle", + name = L["Subtract GCD"], + width = WeakAuras.normalWidth, + hidden = hidden + }) + addOption(symbol .. "_gcd_cast", { + type = "toggle", + name = L["Subtract Cast"], + width = WeakAuras.normalWidth, + hidden = hidden + }) + addOption(symbol .. "_gcd_channel", { + type = "toggle", + name = L["Subtract Channel"], + width = WeakAuras.normalWidth, + hidden = hidden + }) + addOption(symbol .. "_gcd_hide_zero", { + type = "toggle", + name = L["Hide 0 cooldowns"], + width = WeakAuras.normalWidth, + hidden = hidden + }) + + addOption(symbol .. "_decimal_precision", { + type = "select", + name = L["Precision"], + width = WeakAuras.normalWidth, + values = WeakAuras.precision_types, + hidden = hidden + }) + addOption(symbol .. "_round_type", { + type = "select", + name = L["Round Mode"], + width = WeakAuras.normalWidth, + values = WeakAuras.round_types, + hidden = hidden, + disabled = function() + return get(symbol .. "_decimal_precision") ~= 0 + end + }) + end, + CreateFormatter = function(symbol, get) + local gcd = get(symbol .. "_gcd_gcd", true) + local cast = get(symbol .. "_gcd_cast", false) + local channel = get(symbol .. "_gcd_channel", false) + local hideZero = get(symbol .. "_gcd_hide_zero", false) + local precision = get(symbol .. "_decimal_precision", 1) + + local numberToStringFunc + if precision ~= 0 then + local format = "%." .. precision .. "f" + numberToStringFunc = function(number) + return string.format(format, number) + end + else + local type = get(symbol .. "_round_type", "ceil") + numberToStringFunc = simpleFormatters[type] + end + + return function(value, state) + if state.progressType ~= "timed" or type(value) ~= "number" then + return value + end + + WeakAuras.WatchGCD() + local result = value + local now = GetTime() + if gcd then + local gcdDuration, gcdExpirationTime = WeakAuras.GetGCDInfo() + if gcdDuration ~= 0 then + result = now + value - gcdExpirationTime + end + end + + if cast then + local _, _, _, _, endTime = UnitCastingInfo("player") + local castExpirationTIme = endTime and endTime > 0 and (endTime / 1000) or 0 + if castExpirationTIme > 0 then + result = min(result, now + value - castExpirationTIme) + end + end + if channel then + local _, _, _, _, endTime = UnitChannelInfo("player") + local castExpirationTIme = endTime and endTime > 0 and (endTime / 1000) or 0 + if castExpirationTIme > 0 then + result = min(result, now + value - castExpirationTIme) + end + end + + if result <= 0 then + return hideZero and "" or "0" + end + + return numberToStringFunc(result / WeakAuras.CalculatedGcdDuration()) + end + end + } +} + +WeakAuras.format_types_display = {} +for k, v in pairs(WeakAuras.format_types) do WeakAuras.format_types_display[k] = v.display end + + WeakAuras.sound_channel_types = { Master = L["Master"], SFX = ENABLE_SOUNDFX, @@ -510,28 +992,6 @@ WeakAuras.orientation_with_circle_types = { ANTICLOCKWISE = L["Anticlockwise"] } --- TODO -WeakAuras.spec_types = { - [1] = "SPECIALIZATION".." 1", - [2] = "SPECIALIZATION".." 2", - [3] = "SPECIALIZATION".." 3", - [4] = "SPECIALIZATION".." 4" -} - -WeakAuras.spec_types_3 = { - [1] = "SPECIALIZATION".." 1", - [2] = "SPECIALIZATION".." 2", - [3] = "SPECIALIZATION".." 3" -} - -WeakAuras.spec_types_2 = { - [1] = "SPECIALIZATION".." 1", - [2] = "SPECIALIZATION".." 2" -} - -WeakAuras.spec_types_specific = {} -WeakAuras.spec_types_all = {} - WeakAuras.talent_types = {} for tab = 1, 5 do for num_talent = 1, MAX_NUM_TALENTS do @@ -1961,9 +2421,12 @@ WeakAuras.multiUnitUnits.party["player"] = true for i = 1, 4 do WeakAuras.baseUnitId["party"..i] = true WeakAuras.baseUnitId["partypet"..i] = true - WeakAuras.baseUnitId["boss"..i] = true WeakAuras.multiUnitUnits.group["party"..i] = true WeakAuras.multiUnitUnits.party["party"..i] = true +end + +for i = 1, MAX_BOSS_FRAMES do + WeakAuras.baseUnitId["boss"..i] = true WeakAuras.multiUnitUnits.boss["boss"..i] = true end diff --git a/WeakAuras/WeakAuras.lua b/WeakAuras/WeakAuras.lua index e646732..5d2233b 100644 --- a/WeakAuras/WeakAuras.lua +++ b/WeakAuras/WeakAuras.lua @@ -1,4 +1,4 @@ -local internalVersion = 29; +local internalVersion = 32; -- Lua APIs local insert = table.insert @@ -320,6 +320,12 @@ WeakAuras.customActionsFunctions = {}; -- Custom Functions used in conditions, keyed on id, condition number, "changes", property number WeakAuras.customConditionsFunctions = {}; +-- Text format functions for chat messages, keyed on id, condition number, changes, property number +WeakAuras.conditionTextFormatters = {} + +-- Helpers for conditions, that is custom run functions and preamble objects for built in checks +-- keyed on UID not on id! +WeakAuras.conditionHelpers = {} local anim_function_strings = WeakAuras.anim_function_strings; local anim_presets = WeakAuras.anim_presets; @@ -580,6 +586,7 @@ function WeakAuras.ConstructFunction(prototype, trigger, skipOptional) local debug = {}; local events = {} local init; + local preambles = "" if(prototype.init) then init = prototype.init(trigger); else @@ -682,10 +689,16 @@ function WeakAuras.ConstructFunction(prototype, trigger, skipOptional) end test = "("..name..(trigger[name.."_operator"] or "==")..(number or "[["..(trigger[name] or "").."]]")..")"; end - if(arg.required) then - tinsert(required, test); - else - tinsert(tests, test); + if (arg.preamble) then + preambles = preambles .. arg.preamble:format(trigger[name]) .. "\n" + end + + if test ~= "(test)" then + if(arg.required) then + tinsert(required, test); + else + tinsert(tests, test); + end end if test and arg.events then @@ -702,7 +715,7 @@ function WeakAuras.ConstructFunction(prototype, trigger, skipOptional) end end - local ret = "return function("..table.concat(input, ", ")..")\n"; + local ret = preambles .. "return function("..table.concat(input, ", ")..")\n"; ret = ret..(init or ""); ret = ret..(#debug > 0 and table.concat(debug, "\n") or ""); ret = ret.."if("; @@ -722,7 +735,7 @@ function WeakAuras.GetActiveConditions(id, cloneId) return triggerState[id].activatedConditions[cloneId]; end -local function formatValueForAssignment(vType, value, pathToCustomFunction) +local function formatValueForAssignment(vType, value, pathToCustomFunction, pathToFormatters) if (value == nil) then value = false; end @@ -739,10 +752,12 @@ local function formatValueForAssignment(vType, value, pathToCustomFunction) return "{1, 1, 1, 1}"; elseif(vType == "chat") then if (value and type(value) == "table") then - return string.format("{message_type = %q, message = %q, message_dest = %q, message_channel = %q, message_custom = %s}", + 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); + pathToCustomFunction, + pathToFormatters) + return serialized end elseif(vType == "sound") then if (value and type(value) == "table") then @@ -822,10 +837,8 @@ function WeakAuras.scheduleConditionCheck(time, id, cloneId) end end -local customConditionTestFunctions = {}; - -function WeakAuras.CallCustomConditionTest(testFunctionNumber, ...) - local ok, result = pcall(customConditionTestFunctions[testFunctionNumber], ...) +function WeakAuras.CallCustomConditionTest(uid, testFunctionNumber, ...) + local ok, result = pcall(WeakAuras.conditionHelpers[uid].customTestFunctions[testFunctionNumber], ...) if not ok then geterrorhandler()(result) elseif (ok) then @@ -833,7 +846,7 @@ function WeakAuras.CallCustomConditionTest(testFunctionNumber, ...) end end -local function CreateTestForCondition(input, allConditionsTemplate, usedStates) +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; @@ -846,7 +859,7 @@ local function CreateTestForCondition(input, allConditionsTemplate, usedStates) local test = {}; if (input.checks) then for i, subcheck in ipairs(input.checks) do - local subtest, subrecheckCode = CreateTestForCondition(subcheck, allConditionsTemplate, usedStates); + local subtest, subrecheckCode = CreateTestForCondition(uid, subcheck, allConditionsTemplate, usedStates); if (subtest) then tinsert(test, "(" .. subtest .. ")"); end @@ -871,16 +884,31 @@ local function CreateTestForCondition(input, allConditionsTemplate, usedStates) 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 - tinsert(customConditionTestFunctions, test); - local testFunctionNumber = #(customConditionTestFunctions); - local valueString = type(value) == "string" and "[[" .. value .. "]]" or value; - local opString = type(op) == "string" and "[[" .. op .. "]]" or op; - check = "state and WeakAuras.CallCustomConditionTest(" .. testFunctionNumber .. ", state[" .. trigger .. "], " .. valueString .. ", " .. (opString or "nil") .. ")"; + 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 == "number" and op) then local v = tonumber(value) @@ -923,9 +951,9 @@ local function CreateTestForCondition(input, allConditionsTemplate, usedStates) return check, recheckCode; end -local function CreateCheckCondition(ret, condition, conditionNumber, allConditionsTemplate, debug) +local function CreateCheckCondition(uid, ret, condition, conditionNumber, allConditionsTemplate, debug) local usedStates = {}; - local check, recheckCode = CreateTestForCondition(condition.check, allConditionsTemplate, 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"; @@ -1005,14 +1033,21 @@ local function CreateActivateCondition(ret, id, condition, conditionNumber, prop 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 - ret = ret .. " region:" .. propertyData.action .. "(" .. formatValueForAssignment(propertyData.type, change.value, pathToCustomFunction) .. ")" .. "\n"; - if (debug) then ret = ret .. " print('# " .. propertyData.action .. "(" .. formatValueForAssignment(propertyData.type, change.value, pathToCustomFunction) .. "')\n"; 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 @@ -1133,6 +1168,20 @@ function WeakAuras.LoadConditionPropertyFunctions(data) 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 @@ -1199,8 +1248,9 @@ function WeakAuras.ConstructConditionFunction(data) -- 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(ret, condition, conditionNumber, allConditionsTemplate, debug) + ret = CreateCheckCondition(data.uid, ret, condition, conditionNumber, allConditionsTemplate, debug) end end ret = ret .. " end\n"; @@ -2255,6 +2305,7 @@ function WeakAuras.Delete(data) WeakAuras.customActionsFunctions[id] = nil; WeakAuras.customConditionsFunctions[id] = nil; + WeakAuras.conditionTextFormatters[id] = nil for event, funcs in pairs(dynamicConditions) do funcs[id] = nil; @@ -2262,6 +2313,8 @@ function WeakAuras.Delete(data) WeakAuras.frameLevels[id] = nil; + WeakAuras.conditionHelpers[data.uid] = nil + WeakAuras.DeleteCollapsedData(id) end @@ -2366,6 +2419,9 @@ function WeakAuras.Rename(data, newid) WeakAuras.customConditionsFunctions[newid] = WeakAuras.customConditionsFunctions[oldid]; WeakAuras.customConditionsFunctions[oldid] = nil; + WeakAuras.conditionTextFormatters[newid] = WeakAuras.conditionTextFormatters[oldid] + WeakAuras.conditionTextFormatters[oldid] = nil + for event, funcs in pairs(dynamicConditions) do funcs[newid] = funcs[oldid] funcs[oldid] = nil; @@ -3570,6 +3626,189 @@ function WeakAuras.Modernize(data) 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 whitelist + local blacklist + if data.load.use_name == true and data.load.name then + whitelist = data.load.name + elseif data.load.use_name == false and data.load.name then + blacklist = data.load.name + end + + if data.load.use_realm == true and data.load.realm then + whitelist = (whitelist or "") .. "-" .. data.load.realm + elseif data.load.use_realm == false and data.load.realm then + blacklist = (blacklist or "") .. "-" .. data.load.realm + end + + if whitelist then + data.load.use_namerealm = true + data.load.namerealm = whitelist + end + + if blacklist then + data.load.use_namerealmblack = true + data.load.namerealmblack = blacklist + 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 + for _, triggerSystem in pairs(triggerSystems) do triggerSystem.Modernize(data); end @@ -4345,10 +4584,10 @@ function WeakAuras.ReleaseClone(id, cloneId, regionType) clonePool[regionType][#clonePool[regionType] + 1] = region; end -function WeakAuras.HandleChatAction(message_type, message, message_dest, message_channel, r, g, b, region, customFunc, when) +function WeakAuras.HandleChatAction(message_type, message, message_dest, message_channel, r, g, b, region, customFunc, when, formatters) local useHiddenStates = when == "finish" if (message:find('%%')) then - message = WeakAuras.ReplacePlaceHolders(message, region, customFunc, useHiddenStates); + message = WeakAuras.ReplacePlaceHolders(message, region, customFunc, useHiddenStates, formatters); end if(message_type == "PRINT") then DEFAULT_CHAT_FRAME:AddMessage(message, r or 1, g or 1, b or 1); @@ -4567,17 +4806,20 @@ function WeakAuras.PerformActions(data, when, region) return; end; local actions; + local formatters if(when == "start") then actions = data.actions.start; + formatters = region.startFormatters elseif(when == "finish") then actions = data.actions.finish; + formatters = region.finishFormatters else return; end if(actions.do_message and actions.message_type and actions.message) then local customFunc = WeakAuras.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); + WeakAuras.HandleChatAction(actions.message_type, actions.message, actions.message_dest, actions.message_channel, actions.r, actions.g, actions.b, region, customFunc, when, formatters); end if(actions.do_sound and actions.sound) then @@ -5943,11 +6185,12 @@ function WeakAuras.RunCustomTextFunc(region, customFunc) end local state = region.state WeakAuras.ActivateAuraEnvironment(region.id, region.cloneId, region.state, region.states); - local progress = WeakAuras.dynamic_texts.p.func(state, region) - local dur = WeakAuras.dynamic_texts.t.func(state, region) - local name = WeakAuras.dynamic_texts.n.func(state, region) - local icon = WeakAuras.dynamic_texts.i.func(state, region) - local stacks = WeakAuras.dynamic_texts.s.func(state, region) + + local progress = WeakAuras.dynamic_texts.p.func(WeakAuras.dynamic_texts.p.get(state), state, 1) + local dur = WeakAuras.dynamic_texts.t.func( WeakAuras.dynamic_texts.t.get(state), state, 1) + local name = WeakAuras.dynamic_texts.n.func(WeakAuras.dynamic_texts.n.get(state)) + local icon = WeakAuras.dynamic_texts.i.func(WeakAuras.dynamic_texts.i.get(state)) + local stacks = WeakAuras.dynamic_texts.s.func(WeakAuras.dynamic_texts.s.get(state)) local expirationTime local duration @@ -5972,7 +6215,7 @@ function WeakAuras.RunCustomTextFunc(region, customFunc) return custom end -local function ReplaceValuePlaceHolders(textStr, region, customFunc, state) +local function ReplaceValuePlaceHolders(textStr, region, customFunc, state, formatter) local regionValues = region.values; local value; if string.sub(textStr, 1, 1) == "c" then @@ -5992,7 +6235,12 @@ local function ReplaceValuePlaceHolders(textStr, region, customFunc, state) if (not variable) then return nil; end - value = variable.func(state, region) + value = variable.get(state) + if formatter then + value = formatter(value, state) + elseif variable.func then + value = variable.func(value) + end end return value or ""; end @@ -6110,30 +6358,42 @@ function WeakAuras.ContainsAnyPlaceHolders(textStr) return ContainsPlaceHolders(textStr, function(symbol) return true end) end -local function ValueForSymbol(symbol, region, customFunc, regionState, regionStates, useHiddenStates) +local function ValueForSymbol(symbol, region, customFunc, regionState, regionStates, useHiddenStates, formatters) local triggerNum, sym = string.match(symbol, "(.+)%.(.+)") triggerNum = triggerNum and tonumber(triggerNum) if triggerNum and sym then if regionStates[triggerNum] then if (useHiddenStates or regionStates[triggerNum].show) then if regionStates[triggerNum][sym] then - return tostring(regionStates[triggerNum][sym]) or "" + local value = regionStates[triggerNum][sym] + if formatters[symbol] then + return tostring(formatters[symbol](value, regionStates[triggerNum])) or "" + else + return tostring(value) or "" + end else - local value = ReplaceValuePlaceHolders(sym, region, customFunc, regionStates[triggerNum]); + local value = ReplaceValuePlaceHolders(sym, region, customFunc, regionStates[triggerNum], formatters[symbol]); return value or "" end end end return "" elseif regionState[symbol] then - return (useHiddenStates or regionState.show) and tostring(regionState[symbol]) or "" + if(useHiddenStates or regionState.show) then + local value = regionState[symbol] + if formatters[symbol] then + return tostring(formatters[symbol](value, regionState) or "") or "" + else + return tostring(value) or "" + end + end else - local value = (useHiddenStates or regionState.show) and ReplaceValuePlaceHolders(symbol, region, customFunc, regionState); + local value = (useHiddenStates or regionState.show) and ReplaceValuePlaceHolders(symbol, region, customFunc, regionState, formatters[symbol]); return value or "" end end -function WeakAuras.ReplacePlaceHolders(textStr, region, customFunc, useHiddenStates) +function WeakAuras.ReplacePlaceHolders(textStr, region, customFunc, useHiddenStates, formatters) local regionValues = region.values; local regionState = region.state or {}; local regionStates = region.states or {}; @@ -6148,7 +6408,8 @@ function WeakAuras.ReplacePlaceHolders(textStr, region, customFunc, useHiddenSta if (endPos == 2) then if string.byte(textStr, 1) == 37 then - local value = (regionState.show or useHiddenStates) and ReplaceValuePlaceHolders(string.sub(textStr, 2), region, customFunc, regionState); + local symbol = string.sub(textStr, 2) + local value = (regionState.show or useHiddenStates) and ReplaceValuePlaceHolders(symbol, region, customFunc, regionState, formatters[symbol]); if (value) then textStr = tostring(value); end @@ -6186,7 +6447,7 @@ function WeakAuras.ReplacePlaceHolders(textStr, region, customFunc, useHiddenSta -- 0-9a-zA-Z or dot character else -- End of variable local symbol = string.sub(textStr, start, currentPos - 1) - result = result .. ValueForSymbol(symbol, region, customFunc, regionState, regionStates, useHiddenStates) + result = result .. ValueForSymbol(symbol, region, customFunc, regionState, regionStates, useHiddenStates, formatters) if char == 37 then else @@ -6196,7 +6457,7 @@ function WeakAuras.ReplacePlaceHolders(textStr, region, customFunc, useHiddenSta elseif state == 3 then if char == 125 then -- } closing brace local symbol = string.sub(textStr, start, currentPos - 1) - result = result .. ValueForSymbol(symbol, region, customFunc, regionState, regionStates, useHiddenStates) + result = result .. ValueForSymbol(symbol, region, customFunc, regionState, regionStates, useHiddenStates, formatters) start = currentPos + 1 end end @@ -6208,7 +6469,7 @@ function WeakAuras.ReplacePlaceHolders(textStr, region, customFunc, useHiddenSta result = result .. string.sub(textStr, start, currentPos - 1) elseif state == 2 and currentPos > start then local symbol = string.sub(textStr, start, currentPos - 1) - result = result .. ValueForSymbol(symbol, region, customFunc, regionState, regionStates, useHiddenStates) + result = result .. ValueForSymbol(symbol, region, customFunc, regionState, regionStates, useHiddenStates, formatters) elseif state == 1 then result = result .. "%" end @@ -6217,6 +6478,79 @@ function WeakAuras.ReplacePlaceHolders(textStr, region, customFunc, useHiddenSta return textStr; end +function WeakAuras.ParseTextStr(textStr, symbolCallback) + if not textStr then + return + end + local endPos = textStr:len(); + local currentPos = 1 -- Position of the "cursor" + local state = 0 + local start = 1 -- Start of whatever "word" we are currently considering, doesn't include % or {} symbols + + while currentPos <= endPos do + local char = string.byte(textStr, currentPos); + if state == 0 then -- Normal State + elseif state == 1 then -- Percent Start State + if char == 37 then + start = currentPos + elseif char == 123 then + start = currentPos + 1 + elseif (char >= 48 and char <= 57) or (char >= 65 and char <= 90) or (char >= 97 and char <= 122) or char == 46 then + -- 0-9a-zA-Z or dot character + start = currentPos + else + start = currentPos + end + elseif state == 2 then -- Percent Rest State + if (char >= 48 and char <= 57) or (char >= 65 and char <= 90) or (char >= 97 and char <= 122) or char == 46 then + -- 0-9a-zA-Z or dot character + else -- End of variable + local symbol = string.sub(textStr, start, currentPos - 1) + symbolCallback(symbol) + if char == 37 then + else + start = currentPos + end + end + elseif state == 3 then + if char == 125 then -- } closing brace + local symbol = string.sub(textStr, start, currentPos - 1) + symbolCallback(symbol) + start = currentPos + 1 + end + end + state = nextState(char, state) + currentPos = currentPos + 1 + end + + if state == 2 and currentPos > start then + local symbol = string.sub(textStr, start, currentPos - 1) + symbolCallback(symbol) + end +end + +function WeakAuras.CreateFormatters(input, getter) + local seenSymbols = {} + local formatters = {} + WeakAuras.ParseTextStr(input, function(symbol) + if not seenSymbols[symbol] then + local triggerNum, sym = string.match(symbol, "(.+)%.(.+)") + sym = sym or symbol + if sym == "c" or sym == "i" then + -- Do nothing + else + local default = (sym == "p" or sym == "t") and "timed" or "none" + local selectedFormat = getter(symbol .. "_format", default) + if (WeakAuras.format_types[selectedFormat]) then + formatters[symbol] = WeakAuras.format_types[selectedFormat].CreateFormatter(symbol, getter) + end + end + end + seenSymbols[symbol] = true + end) + return formatters +end + function WeakAuras.IsTriggerActive(id) local active = triggerState[id]; return active and active.show; @@ -6685,3 +7019,48 @@ end function WeakAuras.UntrackableUnit(unit) return not trackableUnits[unit] end + +local ownRealm = GetRealmName() +function WeakAuras.UnitNameWithRealm(unit) + local name, realm = UnitName(unit) + return name or "", realm or ownRealm or "" +end + +function WeakAuras.ParseNameCheck(name) + local matches = { + name = {}, + realm = {}, + full = {}, + AddMatch = function(self, name, start, last) + local match = strtrim(name:sub(start, last)) + + if (match:sub(1, 1) == "-") then + self.realm[match:sub(2)] = true + elseif match:find("-", 1, true) then + self.full[match] = true + else + self.name[match] = true + end + end, + Check = function(self, name, realm) + if not name or not realm then + return false + end + return self.name[name] or self.realm[realm] or self.full[name .. "-" .. realm] + end + } + + local start = 1 + local last = name:find(',', start, true) + + while (last) do + matches:AddMatch(name, start, last - 1) + start = last + 1 + last = name:find(',', start, true) + end + + last = #name + matches:AddMatch(name, start, last) + + return matches +end diff --git a/WeakAuras/WeakAuras.toc b/WeakAuras/WeakAuras.toc index f3b0c0c..a9ba41c 100644 --- a/WeakAuras/WeakAuras.toc +++ b/WeakAuras/WeakAuras.toc @@ -1,7 +1,7 @@ ## Interface: 30300 ## Title: WeakAuras 2 ## Author: Mirrored and the WeakAuras Team -## Version: 2.17.8 +## Version: 2.17.11 ## Notes: A powerful, comprehensive utility for displaying graphics and information based on buffs, debuffs, and other triggers. ## Notes-esES: Potente y completa aplicación que te permitirá mostrar por pantalla múltiples diseños, basados en beneficios, perjuicios y otros activadores. ## Notes-deDE: Ein leistungsfähiges, umfassendes Addon zur grafischen Darstellung von Informationen von Auren, Cooldowns, Timern und vielem mehr. diff --git a/WeakAuras/compat.lua b/WeakAuras/compat.lua index 5c621da..cc091b6 100644 --- a/WeakAuras/compat.lua +++ b/WeakAuras/compat.lua @@ -2,10 +2,22 @@ local ipairs = ipairs local pairs = pairs local select = select local ceil, floor = math.ceil, math.floor +local format = string.format local GetInstanceInfo = GetInstanceInfo local GetNumPartyMembers = GetNumPartyMembers local GetNumRaidMembers = GetNumRaidMembers +local UnitClass = UnitClass + +function GetClassColoredTextForUnit(unit, text) + local _, classFilename = UnitClass(unit) + local color = RAID_CLASS_COLORS[classFilename] + if color then + return format("|c%s%s|r", format("ff%.2x%.2x%.2x", color.r * 255, color.g * 255, color.b * 255), text) + else + return text + end +end function tInvert(tbl) local inverted = {}; diff --git a/WeakAurasOptions/ActionOptions.lua b/WeakAurasOptions/ActionOptions.lua index b5a74cf..8d01695 100644 --- a/WeakAurasOptions/ActionOptions.lua +++ b/WeakAurasOptions/ActionOptions.lua @@ -51,6 +51,9 @@ function WeakAuras.AddActionOption(id, data) pcall(PlaySound, v, "Master"); end WeakAuras.Add(data); + if(value == "message") then + WeakAuras.ReloadOptions(data.id) + end end, args = { init_header = { @@ -112,7 +115,7 @@ function WeakAuras.AddActionOption(id, data) type = "input", width = WeakAuras.normalWidth, name = L["Send To"], - order = 4, + order = 3.1, disabled = function() return not data.actions.start.do_message end, hidden = function() return data.actions.start.message_type ~= "WHISPER" end }, @@ -120,7 +123,7 @@ function WeakAuras.AddActionOption(id, data) type = "input", width = WeakAuras.doubleWidth, name = L["Message"], - order = 5, + order = 4, disabled = function() return not data.actions.start.do_message end, desc = function() return L["Dynamic text tooltip"] .. WeakAuras.GetAdditionalProperties(data) @@ -131,27 +134,27 @@ function WeakAuras.AddActionOption(id, data) type = "toggle", width = WeakAuras.normalWidth, name = L["Play Sound"], - order = 7 + order = 8 }, start_do_loop = { type = "toggle", width = WeakAuras.normalWidth, name = L["Loop"], - order = 7.1, + order = 8.1, disabled = function() return not data.actions.start.do_sound end, }, start_sound_repeat = { type = "range", width = WeakAuras.normalWidth, name = L["Repeat After"], - order = 7.2, + order = 8.2, hidden = function() return not data.actions.start.do_loop end, disabled = function() return not data.actions.start.do_sound end, }, start_sound_repeat_space = { type = "description", width = WeakAuras.normalWidth, - order = 7.3, + order = 8.3, name = "", hidden = function() return not data.actions.start.do_loop end, }, @@ -159,7 +162,7 @@ function WeakAuras.AddActionOption(id, data) type = "select", width = WeakAuras.normalWidth, name = L["Sound"], - order = 8, + order = 8.4, values = sound_types, disabled = function() return not data.actions.start.do_sound end, control = "WeakAurasSortedDropdown" @@ -484,7 +487,7 @@ function WeakAuras.AddActionOption(id, data) type = "input", width = WeakAuras.normalWidth, name = L["Send To"], - order = 24, + order = 23.1, disabled = function() return not data.actions.finish.do_message end, hidden = function() return data.actions.finish.message_type ~= "WHISPER" end }, @@ -492,7 +495,7 @@ function WeakAuras.AddActionOption(id, data) type = "input", width = WeakAuras.doubleWidth, name = L["Message"], - order = 25, + order = 24, disabled = function() return not data.actions.finish.do_message end, desc = function() return L["Dynamic text tooltip"] .. WeakAuras.GetAdditionalProperties(data) @@ -503,13 +506,13 @@ function WeakAuras.AddActionOption(id, data) type = "toggle", width = WeakAuras.normalWidth, name = L["Play Sound"], - order = 27 + order = 28 }, finish_sound = { type = "select", width = WeakAuras.normalWidth, name = L["Sound"], - order = 28, + order = 28.1, values = sound_types, disabled = function() return not data.actions.finish.do_sound end, control = "WeakAurasSortedDropdown" @@ -796,17 +799,125 @@ function WeakAuras.AddActionOption(id, data) }, } + -- Text format option helpers + WeakAuras.AddCodeOption(action.args, data, L["Custom Code"], "init", "https://github.com/WeakAuras/WeakAuras2/wiki/Custom-Code-Blocks#on-init", 0.011, function() return not data.actions.init.do_custom end, {"actions", "init", "custom"}, true); WeakAuras.AddCodeOption(action.args, data, L["Custom Code"], "start_message", "https://github.com/WeakAuras/WeakAuras2/wiki/Custom-Code-Blocks#chat-message---custom-code", - 5.1, function() return not (data.actions.start.do_message and WeakAuras.ContainsCustomPlaceHolder(data.actions.start.message)) end, {"actions", "start", "message_custom"}, false); + 5, function() return not (data.actions.start.do_message and WeakAuras.ContainsCustomPlaceHolder(data.actions.start.message)) end, {"actions", "start", "message_custom"}, false); + + local startHidden = function() + return WeakAuras.IsCollapsed("format_option", "actions", "start_message", true) + end + + local startSetHidden = function(hidden) + WeakAuras.SetCollapsed("format_option", "actions", "start_message", hidden) + end + + local startGet = function(key) + return data.actions.start["message_format_" .. key] + end + + local order = 6 + local usedKeys = {} + local function startAddOption(key, option) + if usedKeys[key] then + return + end + usedKeys[key] = true + option.order = order + order = order + 0.01 + local reload = option.reloadOptions + option.reloadOptions = nil + option.set = function(info, v) + data.actions.start["message_format_" .. key] = v + WeakAuras.Add(data) + if reload then + WeakAuras.ReloadOptions2(data.id, data) + end + end + + if option.hidden then + local hidden = option.hidden + option.hidden = function() return not data.actions.start.do_message or hidden() end + else + option.hidden = function() return not data.actions.start.do_message end + end + + action.args["start_message_format_" .. key] = option + end + + if data.controlledChildren then + for _, childId in pairs(data.controlledChildren) do + local childData = WeakAuras.GetData(childId) + local startGet = function(key) + return childData.actions.start["message_format_" .. key] + end + WeakAuras.AddTextFormatOption(childData.actions and childData.actions.start.message, true, startGet, startAddOption, startHidden, startSetHidden) + end + else + WeakAuras.AddTextFormatOption(data.actions and data.actions.start.message, true, startGet, startAddOption, startHidden, startSetHidden) + end + WeakAuras.AddCodeOption(action.args, data, L["Custom Code"], "start", "https://github.com/WeakAuras/WeakAuras2/wiki/Custom-Code-Blocks#on-show", 13, function() return not data.actions.start.do_custom end, {"actions", "start", "custom"}, true); WeakAuras.AddCodeOption(action.args, data, L["Custom Code"], "finish_message", "https://github.com/WeakAuras/WeakAuras2/wiki/Custom-Code-Blocks#chat-message---custom-code", - 26, function() return not (data.actions.finish.do_message and WeakAuras.ContainsCustomPlaceHolder(data.actions.finish.message)) end, {"actions", "finish", "message_custom"}, false); + 25, function() return not (data.actions.finish.do_message and WeakAuras.ContainsCustomPlaceHolder(data.actions.finish.message)) end, {"actions", "finish", "message_custom"}, false); + + local finishHidden = function() + return WeakAuras.IsCollapsed("format_option", "actions", "finish_message", true) + end + + local finishSetHidden = function(hidden) + WeakAuras.SetCollapsed("format_option", "actions", "finish_message", hidden) + end + + local finishGet = function(key) + return data.actions.finish["message_format_" .. key] + end + + order = 25 + usedKeys = {} + local function finishAddOption(key, option) + if usedKeys[key] then + return + end + option.order = order + order = order + 0.01 + local reload = option.reloadOptions + option.reloadOptions = nil + option.set = function(info, v) + data.actions.finish["message_format_" .. key] = v + WeakAuras.Add(data) + if reload then + WeakAuras.ReloadOptions2(data.id, data) + end + end + + if option.hidden then + local hidden = option.hidden + option.hidden = function() return not data.actions.finish.do_message or hidden() end + else + option.hidden = function() return not data.actions.finish.do_message end + end + + action.args["finish_message_format_" .. key] = option + end + + if data.controlledChildren then + for _, childId in pairs(data.controlledChildren) do + local childData = WeakAuras.GetData(childId) + local finishGet = function(key) + return childData.actions.finish["message_format_" .. key] + end + WeakAuras.AddTextFormatOption(childData.actions and childData.actions.finish.message, true, finishGet, finishAddOption, finishHidden, finishSetHidden) + end + else + WeakAuras.AddTextFormatOption(data.actions and data.actions.finish.message, true, finishGet, finishAddOption, finishHidden, finishSetHidden) + end WeakAuras.AddCodeOption(action.args, data, L["Custom Code"], "finish", "https://github.com/WeakAuras/WeakAuras2/wiki/Custom-Code-Blocks#on-hide", 32, function() return not data.actions.finish.do_custom end, {"actions", "finish", "custom"}, true); diff --git a/WeakAurasOptions/BuffTrigger.lua b/WeakAurasOptions/BuffTrigger.lua index 923952b..1e4c1a5 100644 --- a/WeakAurasOptions/BuffTrigger.lua +++ b/WeakAurasOptions/BuffTrigger.lua @@ -11,8 +11,7 @@ local group_aura_name_info_types = WeakAuras.group_aura_name_info_types; local group_aura_stack_info_types = WeakAuras.group_aura_stack_info_types; local function getAuraMatchesLabel(name) - local iconCache = WeakAuras.spellCache.Get(); - local ids = iconCache[name] + local ids = WeakAuras.spellCache.GetSpellsMatching(name) if(ids) then local descText = ""; local numMatches = 0; @@ -37,8 +36,7 @@ local function spellId_tremove(tbl, pos) end local function getAuraMatchesList(name) - local iconCache = WeakAuras.spellCache.Get(); - local ids = iconCache[name] + local ids = WeakAuras.spellCache.GetSpellsMatching(name) if(ids) then local descText = ""; for id, _ in pairs(ids) do diff --git a/WeakAurasOptions/BuffTrigger2.lua b/WeakAurasOptions/BuffTrigger2.lua index 8c9a4b3..04a582e 100644 --- a/WeakAurasOptions/BuffTrigger2.lua +++ b/WeakAurasOptions/BuffTrigger2.lua @@ -6,8 +6,7 @@ local operator_types = WeakAuras.operator_types local debuff_types = WeakAuras.debuff_types local function getAuraMatchesLabel(name) - local iconCache = WeakAuras.spellCache.Get() - local ids = iconCache[name] + local ids = WeakAuras.spellCache.GetSpellsMatching(name) if ids then local descText = "" local numMatches = 0 @@ -21,8 +20,7 @@ local function getAuraMatchesLabel(name) end local function getAuraMatchesList(name) - local iconCache = WeakAuras.spellCache.Get() - local ids = iconCache[name] + local ids = WeakAuras.spellCache.GetSpellsMatching(name) if ids then local descText = "" for id, _ in pairs(ids) do @@ -695,18 +693,57 @@ local function GetBuffTriggerOptions(data, optionTriggerChoices) width = WeakAuras.doubleWidth, hidden = function() return not (trigger.type == "aura2" and (trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party")) end }, + ignoreInvisible = { + type = "toggle", + name = WeakAuras.newFeatureString .. L["Ignore out of checking range."], + desc = L["Uses UnitIsVisible() to check if in range. This is polled every second."], + order = 68.9, + width = WeakAuras.doubleWidth, + hidden = function() return not (trigger.type == "aura2" and (trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party")) end + }, + + useUnitName = { + type = "toggle", + width = "WeakAuras.normalWidth", + name = L["UnitName Filter"], + order = 69.1, + hidden = function() return + not (trigger.type == "aura2" and (trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party")) + end + }, + unitName = { + type = "input", + width = WeakAuras.normalWidth, + name = L["Unit Name Filter"], + desc = L["Filter formats: 'Name', 'Name-Realm', '-Realm'.\n\nSupports multiple entries, separated by commas\n"], + order = 69.2, + hidden = function() + return not (trigger.type == "aura2" + and (trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party") and trigger.useUnitName) + end + }, + unitNameSpace = { + type = "description", + name = "", + order = 69.3, + width = WeakAuras.normalWidth, + hidden = function() + return not (trigger.type == "aura2" + and (trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party") and not trigger.useUnitName) + end + }, useGroup_count = { type = "toggle", width = WeakAuras.normalWidth, name = L["Unit Count"], hidden = function() return not (trigger.type == "aura2" and IsGroupTrigger(trigger)) end, - order = 69 + order = 70 }, useGroup_countSpace = { type = "description", name = "", - order = 69.1, + order = 70.1, width = WeakAuras.normalWidth, hidden = function() return not (trigger.type == "aura2" and IsGroupTrigger(trigger) and not trigger.useGroup_count) end }, @@ -721,7 +758,7 @@ local function GetBuffTriggerOptions(data, optionTriggerChoices) return L["Group aura count description"]:format(groupType, groupType, groupType, groupType, groupType, groupType, groupType) end end, - order = 69.2, + order = 70.2, width = WeakAuras.halfWidth, values = operator_types, hidden = function() return not (trigger.type == "aura2" and IsGroupTrigger(trigger) and trigger.useGroup_count) end, @@ -738,7 +775,7 @@ local function GetBuffTriggerOptions(data, optionTriggerChoices) return L["Group aura count description"]:format(groupType, groupType, groupType, groupType, groupType, groupType, groupType) end end, - order = 69.3, + order = 70.3, width = WeakAuras.halfWidth, hidden = function() return not (trigger.type == "aura2" and IsGroupTrigger(trigger) and trigger.useGroup_count) end, }, diff --git a/WeakAurasOptions/Cache.lua b/WeakAurasOptions/Cache.lua index 6f936c7..711db1e 100644 --- a/WeakAurasOptions/Cache.lua +++ b/WeakAurasOptions/Cache.lua @@ -12,42 +12,70 @@ local spellCache = {} WeakAuras.spellCache = spellCache local cache +local metaData local bestIcon = {} local dynFrame = WeakAuras.dynFrame -- Builds a cache of name/icon pairs from existing spell data -- This is a rather slow operation, so it's only done once, and the result is subsequently saved -function spellCache.Build(callback) - if cache then - local co = coroutine.create(function() - local id = 0 - local misses = 0 - - while misses < 400 do - id = id + 1 - local name, _, icon = GetSpellInfo(id) - - if(icon == 136243) then -- 136243 is the a gear icon, we can ignore those spells - misses = 0; - elseif name and name ~= "" then - cache[name] = cache[name] or {} - cache[name][id] = icon - misses = 0 - else - misses = misses + 1 - end - - coroutine.yield() - end - - if callback then - callback() - end - end) - dynFrame:AddAction(callback, co) - else +function spellCache.Build() + if not cache then error("spellCache has not been loaded. Call WeakAuras.spellCache.Load(...) first.") end + + if not metaData.needsRebuild then + return + end + + wipe(cache) + local co = coroutine.create(function() + local id = 0 + local misses = 0 + + while misses < 400 do + id = id + 1 + local name, _, icon = GetSpellInfo(id) + + if(icon == 136243) then -- 136243 is the a gear icon, we can ignore those spells + misses = 0; + elseif name and name ~= "" then + cache[name] = cache[name] or {} + cache[name].spells = cache[name].spells or {} + cache[name].spells[id] = icon + misses = 0 + else + misses = misses + 1 + end + + coroutine.yield() + end + + for _, category in pairs(GetCategoryList()) do + local total = GetCategoryNumAchievements(category, true) + for i = 1, total do + local id,name,_,_,_,_,_,_,_,iconID = GetAchievementInfo(category, i) + if name and iconID then + cache[name] = cache[name] or {} + cache[name].achievements = cache[name].achievements or {} + cache[name].achievements[id] = iconID + end + end + coroutine.yield() + end + + -- Updates the icon cache with whatever icons WeakAuras core has actually used. + -- This helps keep name<->icon matches relevant. + for name, icons in pairs(WeakAurasSaved.dynamicIconCache) do + if WeakAurasSaved.dynamicIconCache[name] then + for spellId, icon in pairs(WeakAurasSaved.dynamicIconCache[name]) do + spellCache.AddIcon(name, spellId, icon) + end + end + end + + metaData.needsRebuild = false + end) + dynFrame:AddAction("spellCache", co) end function spellCache.GetIcon(name) @@ -62,22 +90,30 @@ function spellCache.GetIcon(name) local icons = cache[name] local bestMatch = nil if (icons) then - for spellId, icon in pairs(icons) do - if (not bestMatch) then - bestMatch = spellId - elseif(type(spellId) == "number" and IsSpellKnown(spellId)) then - bestMatch = spellId + if (icons.spells) then + for spellId, icon in pairs(icons.spells) do + if (not bestMatch) then + bestMatch = spellId + elseif(type(spellId) == "number" and IsSpellKnown(spellId)) then + bestMatch = spellId + end end end end - bestIcon[name] = bestMatch and icons[bestMatch]; + bestIcon[name] = bestMatch and icons.spells[bestMatch]; return bestIcon[name]; else error("spellCache has not been loaded. Call WeakAuras.spellCache.Load(...) first.") end end +function spellCache.GetSpellsMatching(name) + if cache[name] then + return cache[name].spells + end +end + function spellCache.AddIcon(name, id, icon) if cache then if name then @@ -100,7 +136,26 @@ function spellCache.Get() end function spellCache.Load(data) - cache = cache or data + metaData = data + cache = metaData.spellCache + + local _, build = GetBuildInfo(); + local locale = GetLocale(); + local version = WeakAuras.versionString + + local num = 0; + for i,v in pairs(cache) do + num = num + 1; + end + + if(num < 39000 or metaData.locale ~= locale or metaData.build ~= build or metaData.version ~= version or not metaData.spellCacheAchivements) then + metaData.build = build; + metaData.locale = locale; + metaData.version = version; + metaData.spellCacheAchivements = true + metaData.needsRebuild = true + wipe(cache) + end end -- This function computes the Levenshtein distance between two strings diff --git a/WeakAurasOptions/ConditionOptions.lua b/WeakAurasOptions/ConditionOptions.lua index dda92a4..2221ac1 100644 --- a/WeakAurasOptions/ConditionOptions.lua +++ b/WeakAurasOptions/ConditionOptions.lua @@ -331,7 +331,7 @@ local function addControlsForChange(args, order, data, conditionVariable, condit WeakAuras.ReloadTriggerOptions(data); end - setValueComplex = function(property) + setValueComplex = function(property, reloadOptions) return function(info, v) for id, reference in pairs(conditions[i].changes[j].references) do local auraData = WeakAuras.GetData(id); @@ -346,7 +346,11 @@ local function addControlsForChange(args, order, data, conditionVariable, condit conditions[i].changes[j].value = {}; end conditions[i].changes[j].value[property] = v; - WeakAuras.ReloadTriggerOptions(data); + if reloadOptions then + WeakAuras.ReloadOptions2(data.id, data) + else + WeakAuras.ReloadTriggerOptions(data); + end end end @@ -394,13 +398,16 @@ local function addControlsForChange(args, order, data, conditionVariable, condit WeakAuras.Add(data); end - setValueComplex = function(property) + setValueComplex = function(property, reloadOptions) return function(info, v) if (type (conditions[i].changes[j].value) ~= "table") then conditions[i].changes[j].value = {}; end conditions[i].changes[j].value[property] = v; WeakAuras.Add(data); + if reloadOptions then + WeakAuras.ReloadOptions2(data.id, data) + end end end @@ -671,6 +678,10 @@ local function addControlsForChange(args, order, data, conditionVariable, condit descMessage = L["Dynamic text tooltip"] .. WeakAuras.GetAdditionalProperties(data) end + local message_getter = function() + return type(conditions[i].changes[j].value) == "table" and conditions[i].changes[j].value.message; + end + args["condition" .. i .. "value" .. j .. "message"] = { type = "input", width = WeakAuras.doubleWidth, @@ -680,10 +691,47 @@ local function addControlsForChange(args, order, data, conditionVariable, condit get = function() return type(conditions[i].changes[j].value) == "table" and conditions[i].changes[j].value.message; end, - set = setValueComplex("message") + get = message_getter, + set = setValueComplex("message", true) } order = order + 1; + local formatGet = function(key) + return type(conditions[i].changes[j].value) == "table" and conditions[i].changes[j].value["message_format_" .. key] + end + + local usedKeys = {} + local function addOption(key, option) + if usedKeys[key] then + return + end + usedKeys[key] = true + option.order = order + order = order + 0.01 + local fullKey = "condition" .. i .. "value" .. j .. "message_format_" .. key + option.get = function() + return type(conditions[i].changes[j].value) == "table" and conditions[i].changes[j].value["message_format_" .. key]; + end + local originalName = option.name + option.name = blueIfNoValue2(data, conditions[i].changes[j], "value", "message_format_" .. key, originalName, originalName) + option.desc = descIfNoValue2(data, conditions[i].changes[j], "value", "message_format_" .. key, nil, option.values) + + option.set = setValueComplex("message_format_" .. key, option.reloadOptions) + option.reloadOptions = nil + + args[fullKey] = option + end + + if data.controlledChildren then + for id, reference in pairs(conditions[i].changes[j].references) do + local input = reference.value and reference.value.message + WeakAuras.AddTextFormatOption(input, false, formatGet, addOption) + end + else + local input = type(conditions[i].changes[j].value) == "table" and conditions[i].changes[j].value["message"] + WeakAuras.AddTextFormatOption(input, false, formatGet, addOption) + end + local function customHidden() local message = type(conditions[i].changes[j].value) == "table" and conditions[i].changes[j].value.message; if (not message) then return true; end @@ -1434,9 +1482,9 @@ local function addControlsForIfLine(args, order, data, conditionVariable, condit if (currentConditionTemplate.type == "number" or currentConditionTemplate.type == "timer") then local opTypes = WeakAuras.operator_types - if currentConditionTemplate.operator_types_without_equal then + if currentConditionTemplate.operator_types == "without_equal" then opTypes = WeakAuras.operator_types_without_equal - elseif currentConditionTemplate.operator_types_only_equal then + elseif currentConditionTemplate.operator_types == "only_equal" then opTypes = WeakAuras.equality_operator_types end @@ -1546,21 +1594,22 @@ local function addControlsForIfLine(args, order, data, conditionVariable, condit } order = order + 1; elseif (currentConditionTemplate.type == "string") then - args["condition" .. i .. tostring(path) .. "_op"] = { - name = blueIfNoValue(data, conditions[i].check, "op", L["Differences"]), - desc = descIfNoValue(data, conditions[i].check, "op", currentConditionTemplate.type), - type = "select", - width = WeakAuras.normalWidth, - order = order, - values = WeakAuras.string_operator_types, - get = function() - return check and check.op; - end, - set = setOp - } - order = order + 1; - - order = addSpace(args, order); + if currentConditionTemplate.operator_types ~= "none" then + args["condition" .. i .. tostring(path) .. "_op"] = { + name = blueIfNoValue(data, conditions[i].check, "op", L["Differences"]), + desc = descIfNoValue(data, conditions[i].check, "op", currentConditionTemplate.type), + type = "select", + width = WeakAuras.normalWidth, + order = order, + values = WeakAuras.string_operator_types, + get = function() + return check and check.op; + end, + set = setOp + } + order = order + 1; + order = addSpace(args, order); + end args["condition" .. i .. tostring(path) .. "_value"] = { type = "input", @@ -2085,18 +2134,34 @@ local function findMatchingProperty(all, change, id) return nil; end -local propertyTypeToSubProperty = { - chat = { "message_type", "message_dest", "message_channel", "message", "custom" }, - sound = { "sound", "sound_channel", "sound_path", "sound_kit_id", "sound_repeat", "sound_type"}, - customcode = { "custom" }, - glowexternal = { - "glow_action", "glow_frame_type", "glow_type", - "glow_frame", "choose_glow_frame", - "use_glow_color", "glow_color", - "glow_lines", "glow_frequency", "glow_length", "glow_thickness", "glow_XOffset", "glow_YOffset", - "glow_scale", "glow_border" - } -}; +local noop = function() end +local function SubPropertiesForChange(change) + if change.property == "sound" then + return { "sound", "sound_channel", "sound_path", "sound_kit_id", "sound_repeat", "sound_type"} + elseif change.property == "customcode" then + return { "custom" } + elseif change.property == "glowexternal" then + return { + "glow_action", "glow_frame_type", "glow_type", + "glow_frame", "choose_glow_frame", + "use_glow_color", "glow_color", + "glow_lines", "glow_frequency", "glow_length", "glow_thickness", "glow_XOffset", "glow_YOffset", + "glow_scale", "glow_border" + } + elseif change.property == "chat" then + local result = { "message_type", "message_dest", "message_channel", "message", "custom" } + local input = change.value and change.value.message + if input then + local getter = function(key) + return change.value["message_format_" .. key] + end + WeakAuras.AddTextFormatOption(input, false, getter, function(key) + tinsert(result, "message_format_" .. key) + end) + end + return result + end +end local subPropertyToType = { glow_color = "color" @@ -2115,8 +2180,12 @@ local function mergeConditionChange(all, change, id, changeIndex, allProperties) all.samevalue = false; end else - for _, propertyName in ipairs(propertyTypeToSubProperty[propertyType]) do - if not compareValues(all.value[propertyName], change.value[propertyName], subPropertyToType[propertyName]) then + for _, propertyName in ipairs(SubPropertiesForChange(change)) do + if all.samevalue[propertyName] == nil then + -- NEW not yet seen property + all.value[propertyName] = change.value[propertyName] + all.samevalue[propertyName] = true + elseif not compareValues(all.value[propertyName], change.value[propertyName], subPropertyToType[propertyName]) then all.value[propertyName] = nil; if all.samevalue then all.samevalue[propertyName] = false; @@ -2172,7 +2241,7 @@ local function mergeCondition(all, aura, id, conditionIndex, allProperties) local propertyType = change.property and allProperties.propertyMap[change.property] and allProperties.propertyMap[change.property].type; if (propertyType == "chat" or propertyType == "sound" or propertyType == "customcode" or propertyType == "glowexternal") then copy.samevalue = {}; - for _, propertyName in ipairs(propertyTypeToSubProperty[propertyType]) do + for _, propertyName in ipairs(SubPropertiesForChange(change)) do copy.samevalue[propertyName] = true; end else @@ -2220,7 +2289,7 @@ local function mergeConditions(all, aura, id, allConditionTemplates, propertyTyp local propertyType = change.property and propertyTypes.propertyMap[change.property] and propertyTypes.propertyMap[change.property].type; if (propertyType == "chat" or propertyType == "sound" or propertyType == "customcode" or propertyType == "glowexternal") then change.samevalue = {}; - for _, propertyName in ipairs(propertyTypeToSubProperty[propertyType]) do + for _, propertyName in ipairs(SubPropertiesForChange(change)) do change.samevalue[propertyName] = true; end else diff --git a/WeakAurasOptions/OptionsFrames/IconPicker.lua b/WeakAurasOptions/OptionsFrames/IconPicker.lua index 569df1c..3bd65e1 100644 --- a/WeakAurasOptions/OptionsFrames/IconPicker.lua +++ b/WeakAurasOptions/OptionsFrames/IconPicker.lua @@ -22,7 +22,7 @@ local function ConstructIconPicker(frame) group.frame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -17, 30); -- 12 group.frame:SetPoint("TOPLEFT", frame, "TOPLEFT", 17, -50); group.frame:Hide(); - group:SetLayout("flow"); + group:SetLayout("fill"); local scroll = AceGUI:Create("ScrollFrame"); scroll:SetLayout("flow"); @@ -48,27 +48,40 @@ local function ConstructIconPicker(frame) end local usedIcons = {}; + local AddButton = function(name, icon) + local button = AceGUI:Create("WeakAurasIconButton"); + button:SetName(name); + button:SetTexture(icon); + button:SetClick(function() + group:Pick(icon); + end); + scroll:AddChild(button); + + usedIcons[icon] = true; + end + local num = 0; if(subname and subname ~= "") then for name, icons in pairs(spellCache.Get()) do - local bestDistance = math.huge; - local bestName; if(name:lower():find(subname, 1, true)) then - - for spellId, icon in pairs(icons) do - if (not usedIcons[icon]) then - local button = AceGUI:Create("WeakAurasIconButton"); - button:SetName(name); - button:SetTexture(icon); - button:SetClick(function() - group:Pick(icon); - end); - scroll:AddChild(button); - - usedIcons[icon] = true; - num = num + 1; - if(num >= 500) then - break; + if icons.spells then + for spellId, icon in pairs(icons.spells) do + if (not usedIcons[icon]) then + AddButton(name, icon) + num = num + 1; + if(num >= 500) then + break; + end + end + end + elseif icons.achievements then + for _, icon in pairs(icons.achievements) do + if (not usedIcons[icon]) then + AddButton(name, icon) + num = num + 1; + if(num >= 500) then + break; + end end end end @@ -88,7 +101,6 @@ local function ConstructIconPicker(frame) input:SetWidth(170); input:SetHeight(15); input:SetPoint("BOTTOMRIGHT", group.frame, "TOPRIGHT", -12, -5); - WeakAuras.input = input; local inputLabel = input:CreateFontString(nil, "OVERLAY", "GameFontNormal"); inputLabel:SetText(L["Search"]); @@ -189,7 +201,6 @@ local function ConstructIconPicker(frame) close:SetWidth(100); close:SetText(L["Okay"]); - scroll.frame:SetPoint("BOTTOM", close, "TOP", 0, 10); return group end diff --git a/WeakAurasOptions/OptionsFrames/OptionsFrame.lua b/WeakAurasOptions/OptionsFrames/OptionsFrame.lua index 4f06567..4b1da58 100644 --- a/WeakAurasOptions/OptionsFrames/OptionsFrame.lua +++ b/WeakAurasOptions/OptionsFrames/OptionsFrame.lua @@ -748,7 +748,7 @@ function WeakAuras.CreateFrame() tremove(tempGroup.controlledChildren, index) displayButtons[id]:ClearPick() - WeakAuras.ReloadTriggerOptions(tempGroup) + WeakAuras.AddOption(tempGroup.id, tempGroup) self:FillOptions(displayOptions[tempGroup.id]) end @@ -962,7 +962,7 @@ function WeakAuras.CreateFrame() end end - WeakAuras.ReloadTriggerOptions(data) + WeakAuras.AddOption(data.id, data) self:FillOptions(displayOptions[id], tab) -- TODO: remove tab parametter once legacy aura trigger is removed WeakAuras.SetMoverSizer(id) @@ -1041,7 +1041,7 @@ function WeakAuras.CreateFrame() WeakAuras.EnsureOptions(id) displayButtons[id]:Pick() tinsert(tempGroup.controlledChildren, id) - WeakAuras.ReloadTriggerOptions(tempGroup) + WeakAuras.AddOption(tempGroup.id, tempGroup) self:FillOptions(displayOptions[tempGroup.id]) end end @@ -1062,7 +1062,7 @@ function WeakAuras.CreateFrame() tinsert(tempGroup.controlledChildren, id) end end - WeakAuras.ReloadTriggerOptions(tempGroup) + WeakAuras.AddOption(tempGroup.id, tempGroup) self:FillOptions(displayOptions[tempGroup.id]) self.pickedDisplay = tempGroup end diff --git a/WeakAurasOptions/OptionsFrames/TextEditor.lua b/WeakAurasOptions/OptionsFrames/TextEditor.lua index d786449..b62b9e1 100644 --- a/WeakAurasOptions/OptionsFrames/TextEditor.lua +++ b/WeakAurasOptions/OptionsFrames/TextEditor.lua @@ -146,39 +146,6 @@ function(allstates, event, ...) index = , } return true -end]=] - }, - { - name = "Text: Decimals (percentage)", - snippet = [=[ -function() - -- Change percentpower as needed - -- Change [1] to other your trigger number - -- The 0 in `"%.0f"` controls how many decimal places it will round to - if aura_env.states[1] and aura_env.states[1].percentpower then - return string.format("%.0f", aura_env.states[1].percentpower) - end -end]=] - }, - { - name = "Text: Abbreviate numbers", - snippet = [=[ -function() - -- Change tooltip1 to your value - -- Change [1] to other your trigger number - -- If using a tooltip value, be sure to tick Use Tooltip Values in the trigger - if aura_env.states[1] and aura_env.states[1].tooltip1 then - return AbbreviateNumbers(aura_env.states[1].tooltip1) - end -end]=] - }, - { - name = "Text: Colored Name", - snippet = [=[ -function() - if aura_env.states[1] and aura_env.states[1].unit then - return WA_ClassColorName(aura_env.states[1].unit) - end end]=] }, } diff --git a/WeakAurasOptions/RegionOptions/Text.lua b/WeakAurasOptions/RegionOptions/Text.lua index c89e6d1..6aa25d8 100644 --- a/WeakAurasOptions/RegionOptions/Text.lua +++ b/WeakAurasOptions/RegionOptions/Text.lua @@ -29,10 +29,11 @@ local function createOptions(id, data) set = function(info, v) data.displayText = WeakAuras.ReplaceLocalizedRaidMarkers(v); WeakAuras.Add(data); + WeakAuras.ReloadOptions2(data.id, data) WeakAuras.UpdateThumbnail(data); WeakAuras.SetIconNames(data); WeakAuras.ResetMoverSizer(); - end + end, }, customTextUpdate = { type = "select", @@ -43,33 +44,6 @@ local function createOptions(id, data) order = 36 }, -- code editor added below - progressPrecision = { - type = "select", - width = WeakAuras.normalWidth, - order = 39, - name = L["Remaining Time Precision"], - values = WeakAuras.precision_types, - get = function() return data.progressPrecision or 1 end, - hidden = function() return not (WeakAuras.ContainsPlaceHolders(data.displayText, "pt")); - end, - disabled = function() - return not WeakAuras.ContainsPlaceHolders(data.displayText, "p"); - end - }, - totalPrecision = { - type = "select", - width = WeakAuras.normalWidth, - order = 39.5, - name = L["Total Time Precision"], - values = WeakAuras.precision_types, - get = function() return data.totalPrecision or 1 end, - hidden = function() - return not (WeakAuras.ContainsPlaceHolders(data.displayText, "pt")); - end, - disabled = function() - return not WeakAuras.ContainsPlaceHolders(data.displayText, "t"); - end - }, font = { type = "select", @@ -269,6 +243,43 @@ local function createOptions(id, data) WeakAuras.AddCodeOption(options, data, L["Custom Function"], "customText", "https://github.com/WeakAuras/WeakAuras2/wiki/Custom-Code-Blocks#custom-text", 37, function() return not WeakAuras.ContainsCustomPlaceHolder(data.displayText) end, {"customText"}, false); + -- Add Text Format Options + local input = data.displayText + local hidden = function() + return WeakAuras.IsCollapsed("format_option", "text", "displayText", true) + end + + local setHidden = function(hidden) + WeakAuras.SetCollapsed("format_option", "text", "displayText", hidden) + end + + local get = function(key) + return data["displayText_format_" .. key] + end + + local order = 12 + local function addOption(key, option) + option.order = order + order = order + 0.01 + if option.reloadOptions then + option.reloadOptions = nil + option.set = function(info, v) + data["displayText_format_" .. key] = v + WeakAuras.Add(data) + WeakAuras.ReloadOptions2(data.id, data) + end + end + options["displayText_format_" .. key] = option + end + + WeakAuras.AddTextFormatOption(input, true, get, addOption, hidden, setHidden) + addOption("footer", { + type = "description", + name = "", + width = WeakAuras.doubleWidth, + hidden = hidden + }) + return { text = options; position = WeakAuras.PositionOptions(id, data, nil, true); @@ -341,7 +352,6 @@ local function modifyThumbnail(parent, borderframe, data, fullModify, size) local function UpdateText() local textStr = data.displayText; - textStr = WeakAuras.ReplacePlaceHolders(textStr, borderframe); text:SetText(textStr); rescroll(); end diff --git a/WeakAurasOptions/RegionOptions/Texture.lua b/WeakAurasOptions/RegionOptions/Texture.lua index 865cd26..d471d5e 100644 --- a/WeakAurasOptions/RegionOptions/Texture.lua +++ b/WeakAurasOptions/RegionOptions/Texture.lua @@ -134,7 +134,7 @@ local function modifyThumbnail(parent, region, data, fullModify, size) region.texture:SetHeight(scale * data.height); end - WeakAuras.SetTexture(region.texture, data.texture); + region.texture:SetTexture(data.texture); region.texture:SetVertexColor(data.color[1], data.color[2], data.color[3], data.color[4]); region.texture:SetBlendMode(data.blendMode); diff --git a/WeakAurasOptions/SubRegionOptions/SubText.lua b/WeakAurasOptions/SubRegionOptions/SubText.lua index 0ea0732..0aa5d26 100644 --- a/WeakAurasOptions/SubRegionOptions/SubText.lua +++ b/WeakAurasOptions/SubRegionOptions/SubText.lua @@ -82,14 +82,14 @@ local function createOptions(parentData, data, index, subIndex) width = WeakAuras.normalWidth, dialogControl = "LSM30_Font", name = L["Font"], - order = 12, + order = 13, values = AceGUIWidgetLSMlists.font, }, text_fontSize = { type = "range", width = WeakAuras.normalWidth, name = L["Size"], - order = 13, + order = 14, min = 6, softMax = 72, step = 1, @@ -423,11 +423,9 @@ local function createOptions(parentData, data, index, subIndex) end end - local CheckForTimePlaceHolders = CheckTextOptions("pt") - local commonTextOptions = { __title = L["Common Text"], - __hidden = function() return hideCustomTextOption() and CheckForTimePlaceHolders() end, + __hidden = function() return hideCustomTextOption() end, text_customTextUpdate = { type = "select", width = WeakAuras.doubleWidth, @@ -442,42 +440,48 @@ local function createOptions(parentData, data, index, subIndex) WeakAuras.ReloadOptions2(parentData.id, parentData) end }, - -- Code Editor added below - text_progressPrecision = { - type = "select", - width = WeakAuras.normalWidth, - hidden = CheckForTimePlaceHolders, - disabled = CheckTextOptions("p"), - order = 5, - name = L["Remaining Time Precision"], - values = WeakAuras.precision_types, - get = function() return parentData.progressPrecision or 1 end, - set = function(info, v) - parentData.progressPrecision = v - WeakAuras.Add(parentData) - WeakAuras.ReloadOptions2(parentData.id, parentData) - end, - }, - text_totalPrecision = { - type = "select", - width = WeakAuras.normalWidth, - hidden = CheckForTimePlaceHolders, - disabled = CheckTextOptions("t"), - order = 6, - name = L["Total Time Precision"], - values = WeakAuras.precision_types, - get = function() return parentData.totalPrecision or 1 end, - set = function(info, v) - parentData.totalPrecision = v - WeakAuras.Add(parentData) - WeakAuras.ReloadOptions2(parentData.id, parentData) - end, - }, } WeakAuras.AddCodeOption(commonTextOptions, parentData, L["Custom Function"], "customText", "https://github.com/WeakAuras/WeakAuras2/wiki/Custom-Code-Blocks#custom-text", 4, hideCustomTextOption, {"customText"}, false) + -- Add Text Format Options + local input = data["text_text"] + local hidden = function() + return WeakAuras.IsCollapsed("format_option", "text", "text_text", true) + end + + local setHidden = function(hidden) + WeakAuras.SetCollapsed("format_option", "text", "text_text", hidden) + end + + local get = function(key) + return data["text_text_format_" .. key] + end + + local order = 12 + local function addOption(key, option) + option.order = order + order = order + 0.01 + if option.reloadOptions then + option.reloadOptions = nil + option.set = function(info, v) + data["text_text_format_" .. key] = v + WeakAuras.Add(parentData) + WeakAuras.ReloadOptions2(parentData.id, parentData) + end + end + options["text_text_format_" .. key] = option + end + + WeakAuras.AddTextFormatOption(input, true, get, addOption, hidden, setHidden) + addOption("footer", { + type = "description", + name = "", + width = WeakAuras.doubleWidth, + hidden = hidden + }) + return options, commonTextOptions end diff --git a/WeakAurasOptions/WeakAurasOptions.lua b/WeakAurasOptions/WeakAurasOptions.lua index 88a3939..a79745e 100644 --- a/WeakAurasOptions/WeakAurasOptions.lua +++ b/WeakAurasOptions/WeakAurasOptions.lua @@ -1064,35 +1064,7 @@ loadedFrame:SetScript("OnEvent", function(self, event, addon) odb.idCache = nil; end odb.spellCache = odb.spellCache or {}; - spellCache.Load(odb.spellCache); - - local _, build = GetBuildInfo(); - local locale = GetLocale(); - local version = WeakAuras.versionString - - local num = 0; - - for i,v in pairs(odb.spellCache) do - num = num + 1; - end - - if(num < 39000 or odb.locale ~= locale or odb.build ~= build or odb.version ~= version) then - spellCache.Build(); - - odb.build = build; - odb.locale = locale; - odb.version = version; - end - - -- Updates the icon cache with whatever icons WeakAuras core has actually used. - -- This helps keep name<->icon matches relevant. - for name, icons in pairs(db.dynamicIconCache) do - if db.dynamicIconCache[name] then - for spellId, icon in pairs(db.dynamicIconCache[name]) do - spellCache.AddIcon(name, spellId, icon) - end - end - end + spellCache.Load(odb); if odb.magnetAlign == nil then odb.magnetAlign = true @@ -1392,6 +1364,8 @@ function WeakAuras.ShowOptions(msg) WeakAuras.Pause(); WeakAuras.SetFakeStates() + WeakAuras.spellCache.Build() + if (firstLoad) then frame = WeakAuras.CreateFrame(); frame.buttonsScroll.frame:Show(); @@ -2551,7 +2525,7 @@ local function flattenRegionOptions(allOptions, isGroupTab) end local function parsePrefix(input, data, create) - local subRegionIndex, property = string.match(input, "^sub%.(%d+)%..+%.(.+)") + local subRegionIndex, property = string.match(input, "^sub%.(%d+)%..-%.(.+)") subRegionIndex = tonumber(subRegionIndex) if subRegionIndex then if create then @@ -4837,3 +4811,76 @@ end function WeakAuras.DeleteCollapsedData(id) collapsedOptions[id] = nil end + +function WeakAuras.AddTextFormatOption(input, withHeader, get, addOption, hidden, setHidden) + local headerOption + if withHeader then + headerOption = { + type = "execute", + control = "WeakAurasExpandSmall", + name = function() + return L["|cFFffcc00Format Options|r"] + end, + width = WeakAuras.doubleWidth, + func = function() + setHidden(not hidden()) + end, + image = function() + return hidden() and "Interface\\AddOns\\WeakAuras\\Media\\Textures\\edit" or "Interface\\AddOns\\WeakAuras\\Media\\Textures\\editdown" + end, + imageWidth = 24, + imageHeight = 24 + } + addOption("header", headerOption) + else + hidden = false + end + + + local seenSymbols = {} + WeakAuras.ParseTextStr(input, function(symbol) + if not seenSymbols[symbol] then + local triggerNum, sym = string.match(symbol, "(.+)%.(.+)") + sym = sym or symbol + + addOption(symbol .. "desc", { + type = "description", + name = L["Format for %s"]:format("%" .. symbol), + width = WeakAuras.normalWidth, + hidden = hidden + }) + + if sym == "c" or sym == "i" then + -- No special options for these + else + addOption(symbol .. "_format", { + type = "select", + name = L["Format"], + width = WeakAuras.normalWidth, + values = WeakAuras.format_types_display, + hidden = hidden, + reloadOptions = true + }) + + local selectedFormat = get(symbol .. "_format") + if (WeakAuras.format_types[selectedFormat]) then + WeakAuras.format_types[selectedFormat].AddOptions(symbol, hidden, addOption, get) + end + + end + end + seenSymbols[symbol] = true + end) + + if next(seenSymbols) then + local footerOption = { + type = "header", + name = "", + } + addOption("footer", footerOption) + end + + if not next(seenSymbols) and withHeader then + headerOption.hidden = true + end +end