diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..62c89355 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea/ \ No newline at end of file diff --git a/.luarc.json b/.luarc.json new file mode 100644 index 00000000..d283ec53 --- /dev/null +++ b/.luarc.json @@ -0,0 +1,7 @@ +{ + "diagnostics.disable": [ + "undefined-global", + "deprecated", + "cast-local-type" + ] +} \ No newline at end of file diff --git a/Folder.DotSettings b/Folder.DotSettings new file mode 100644 index 00000000..e3866698 --- /dev/null +++ b/Folder.DotSettings @@ -0,0 +1,15 @@ + + True + True + True + True + True + True + True + True + True + True + True + True + True + \ No newline at end of file diff --git a/api/api.lua b/api/api.lua index e4a94402..30f8e054 100644 --- a/api/api.lua +++ b/api/api.lua @@ -7,6 +7,62 @@ setfenv(1, pfUI:GetEnvironment()) gfind = string.gmatch or string.gfind mod = math.mod or mod +local loadstring_cache = {} +local loadstring_cache_size = 0 +local LOADSTRING_CACHE_MAX_SIZE = 1024 -- in practice such a max-size is more than enough we will rarely have to wipe the cache during the session +-- [ TryMemoizedFuncLoadstringForSpellCasts ] +-- Returns a function for the given string if the given msg is a string and a valid +-- lua-function can indeed be loaded from it, otherwise nil. +-- +-- If msg is an actual raw-lua-function is passed it is returned as-is without any processing. +-- This allows to use both raw functions and string-based scriptlets with the same API. +-- +-- If msg is any other type (number, boolean, ...) then nil is returned. +-- +-- The function is memoized for better performance on repeated calls with the same string. +-- +-- The cache is automatically cleared when it exceeds a certain size (1024 entries) to prevent abuse. +-- +-- In practice it is very unlikely to have more than a few dozen different scriptlets during a session, +-- so cache-nuking should be a very rare event. +function pfUI.api.TryMemoizedFuncLoadstringForSpellCasts(msg) + local msgType = type(msg) + if msgType == "function" then --10 order + return msg -- return as-is + end + + if msgType ~= "string" then --20 order + return nil -- numbers and the like are not valid for loadstring + end + + local result = loadstring_cache[msg] + if result ~= nil then -- already seen? + return result + end + + result = loadstring(msg) + if result == nil then -- invalid code + return nil + end + + if loadstring_cache_size > LOADSTRING_CACHE_MAX_SIZE then --90 just in case + loadstring_cache = {} + loadstring_cache_size = 0 + end + + loadstring_cache[msg] = result -- order + loadstring_cache_size = loadstring_cache_size + 1 -- order + + return result + + --10 special cases for when using /pfcast with funcs or scriptlets if a raw func is passed we dont need to + -- to loadstring it all + -- + --20 if the user passes a direct integer-spell-id or something else, we should not attempt to loadstring it + -- + --90 prevent abuse by capping the cache size +end + -- [ strsplit ] -- Splits a string using a delimiter. -- 'delimiter' [string] characters that will be interpreted as delimiter @@ -14,11 +70,15 @@ mod = math.mod or mod -- 'subject' [string] String to split. -- return: [list] a list of strings. function pfUI.api.strsplit(delimiter, subject) - if not subject then return nil end - local delimiter, fields = delimiter or ":", {} - local pattern = string.format("([^%s]+)", delimiter) - string.gsub(subject, pattern, function(c) fields[table.getn(fields)+1] = c end) - return unpack(fields) + if not subject then + return nil + end + local delimiter, fields = delimiter or ":", {} + local pattern = string.format("([^%s]+)", delimiter) + string.gsub(subject, pattern, function(c) + fields[table.getn(fields) + 1] = c + end) + return unpack(fields) end -- [ isempty ] @@ -26,11 +86,13 @@ end -- 'tbl' [table] the table that shall be checked -- return: [boolean] result of the check. function pfUI.api.isempty(tbl) - if not tbl then return true end - for k, v in pairs(tbl) do - return false - end - return true + if not tbl then + return true + end + for _ in pairs(tbl) do + return false + end + return true end -- [ checkversion ] @@ -43,15 +105,15 @@ end -- to the given value, otherwise returns nil. local major, minor, fix = nil, nil, nil function pfUI.api.checkversion(chkmajor, chkminor, chkfix) - if not major and not minor and not fix then - -- load and convert current version - major, minor, fix = pfUI.api.strsplit(".", tostring(pfUI_config.version)) - major, minor, fix = tonumber(major) or 0, tonumber(minor) or 0, tonumber(fix) or 0 - end + if not major and not minor and not fix then + -- load and convert current version + major, minor, fix = pfUI.api.strsplit(".", tostring(pfUI_config.version)) + major, minor, fix = tonumber(major) or 0, tonumber(minor) or 0, tonumber(fix) or 0 + end - local chkversion = chkmajor + chkminor/100 + chkfix/10000 - local curversion = major + minor/100 + fix/10000 - return curversion <= chkversion and true or nil + local chkversion = chkmajor + chkminor / 100 + chkfix / 10000 + local curversion = major + minor / 100 + fix / 10000 + return curversion <= chkversion and true or nil end -- [ UnitInRange ] @@ -59,15 +121,14 @@ end -- It takes care of the rangecheck module if existing. -- unit [string] A unit to query (string, unitID) -- return: [bool] "1" if in range otherwise "nil" -local RangeCache = {} function pfUI.api.UnitInRange(unit) - if not UnitExists(unit) or not UnitIsVisible(unit) then - return nil - elseif CheckInteractDistance(unit, 4) then - return 1 - else - return librange:UnitInSpellRange(unit) - end + if not UnitExists(unit) or not UnitIsVisible(unit) then + return nil + elseif CheckInteractDistance(unit, 4) then + return 1 + else + return librange:UnitInSpellRange(unit) + end end -- [ RunOOC ] @@ -77,18 +138,23 @@ end -- nil if the function already exists in queue local queue, frame = {} function pfUI.api.RunOOC(func) - if not frame then - frame = CreateFrame("Frame") - frame:SetScript("OnUpdate", function() - if InCombatLockdown and InCombatLockdown() then return end - for key, func in pairs(queue) do func(); queue[key] = nil end - end) - end + if not frame then + frame = CreateFrame("Frame") + frame:SetScript("OnUpdate", function() + if InCombatLockdown and InCombatLockdown() then + return + end + for k, f in pairs(queue) do + f(); + queue[k] = nil + end + end) + end - if not queue[tostring(func)] then - queue[tostring(func)] = func - return true - end + if not queue[tostring(func)] then + queue[tostring(func)] = func + return true + end end -- [ UnitHasBuff ] @@ -97,15 +163,15 @@ end -- buff [string] The texture of the buff. -- return: [bool] true if unit has buff otherwise "nil" function pfUI.api.UnitHasBuff(unit, buff) - local hasbuff = nil - for i=1,32 do - if UnitBuff(unit, i) == buff then - hasbuff = true - break + local hasbuff = nil + for i = 1, 32 do + if UnitBuff(unit, i) == buff then + hasbuff = true + break + end end - end - return hasbuff + return hasbuff end -- [[ GetUnitColor ]] @@ -113,14 +179,14 @@ end -- unit [string] the unitstring -- return: [table] string, r, g, b function pfUI.api.GetUnitColor(unitstr) - local _, class = UnitClass(unitstr) + local _, class = UnitClass(unitstr) - local r, g, b = .8, .8, .8 - if RAID_CLASS_COLORS[class] then - r, g, b = RAID_CLASS_COLORS[class].r, RAID_CLASS_COLORS[class].g, RAID_CLASS_COLORS[class].b - end + local r, g, b = .8, .8, .8 + if RAID_CLASS_COLORS[class] then + r, g, b = RAID_CLASS_COLORS[class].r, RAID_CLASS_COLORS[class].g, RAID_CLASS_COLORS[class].b + end - return pfUI.api.rgbhex(r,g,b), r, g, b + return pfUI.api.rgbhex(r, g, b), r, g, b end -- [ strvertical ] @@ -128,12 +194,12 @@ end -- 'str' [string] String to columnize. -- return: [string] the string tranformed to a column. function pfUI.api.strvertical(str) - local _, len = string.gsub(str,"[^\128-\193]", "") - if (len == string.len(str)) then - return string.gsub(str, "(.)", "%1\n") - else - return string.gsub(str,"([%z\1-\127\194-\244][\128-\191]*)", "%1\n") - end + local _, len = string.gsub(str, "[^\128-\193]", "") + if (len == string.len(str)) then + return string.gsub(str, "(.)", "%1\n") + else + return string.gsub(str, "([%z\1-\127\194-\244][\128-\191]*)", "%1\n") + end end -- [ round ] @@ -142,12 +208,16 @@ end -- 'places' [int] amount of places after the comma. -- returns: [float] rounded number. function pfUI.api.round(input, places) - if not places then places = 0 end - if type(input) == "number" and type(places) == "number" then - local pow = 1 - for i = 1, places do pow = pow * 10 end - return floor(input * pow + 0.5) / pow - end + if not places then + places = 0 + end + if type(input) == "number" and type(places) == "number" then + local pow = 1 + for i = 1, places do + pow = pow * 10 + end + return floor(input * pow + 0.5) / pow + end end -- [ clamp ] @@ -157,11 +227,11 @@ end -- 'max' [number] maximum value. -- returns: [number] clamped value: 'x', 'min' or 'max' value itself. function pfUI.api.clamp(x, min, max) - if type(x) == "number" and type(min) == "number" and type(max) == "number" then - return x < min and min or x > max and max or x - else - return x - end + if type(x) == "number" and type(min) == "number" and type(max) == "number" then + return x < min and min or x > max and max or x + else + return x + end end -- [ modf ] @@ -169,11 +239,13 @@ end -- 'f' [float] the number to breakdown. -- returns: [int],[float] whole and fractional part. function pfUI.api.modf(f) - if modf then return modf(f) end - if f > 0 then - return math.floor(f), mod(f,1) - end - return math.ceil(f), mod(f,1) + if modf then + return modf(f) + end + if f > 0 then + return math.floor(f), mod(f, 1) + end + return math.ceil(f), mod(f, 1) end -- [ GetSlashCommands ] @@ -181,15 +253,15 @@ end -- 'text' [string] optional, a specific command to find -- return: [list] a list of all matching slash commands function pfUI.api.GetSlashCommands(text) - local cmds - for k, v in pairs(_G) do - if strfind(k, "^SLASH_") and (not text or v == text) then - cmds = cmds or {} - cmds[k] = v + local cmds + for k, v in pairs(_G) do + if strfind(k, "^SLASH_") and (not text or v == text) then + cmds = cmds or {} + cmds[k] = v + end end - end - return cmds + return cmds end -- [ RegisterSlashCommand ] @@ -200,16 +272,16 @@ end -- 'force' [boolean] force assign the command even if aleady provided -- by another function/addon. function pfUI.api.RegisterSlashCommand(name, cmds, func, force) - local counter = 1 + local counter = 1 - for _, cmd in pairs(cmds) do - if force or not pfUI.api.GetSlashCommands(cmd) then - _G["SLASH_"..name..counter] = cmd - counter = counter + 1 + for _, cmd in pairs(cmds) do + if force or not pfUI.api.GetSlashCommands(cmd) then + _G["SLASH_" .. name .. counter] = cmd + counter = counter + 1 + end end - end - _G.SlashCmdList[name] = func + _G.SlashCmdList[name] = func end -- [ GetCaptures ] @@ -237,23 +309,23 @@ end -- returns: [string] simplified gfind compatible pattern local sanitize_cache = {} function pfUI.api.SanitizePattern(pattern) - if not sanitize_cache[pattern] then - local ret = pattern - -- escape magic characters - ret = gsub(ret, "([%+%-%*%(%)%?%[%]%^])", "%%%1") - -- remove capture indexes - ret = gsub(ret, "%d%$","") - -- catch all characters - ret = gsub(ret, "(%%%a)","%(%1+%)") - -- convert all %s to .+ - ret = gsub(ret, "%%s%+",".+") - -- set priority to numbers over strings - ret = gsub(ret, "%(.%+%)%(%%d%+%)","%(.-%)%(%%d%+%)") - -- cache it - sanitize_cache[pattern] = ret - end + if not sanitize_cache[pattern] then + local ret = pattern + -- escape magic characters + ret = gsub(ret, "([%+%-%*%(%)%?%[%]%^])", "%%%1") + -- remove capture indexes + ret = gsub(ret, "%d%$", "") + -- catch all characters + ret = gsub(ret, "(%%%a)", "%(%1+%)") + -- convert all %s to .+ + ret = gsub(ret, "%%s%+", ".+") + -- set priority to numbers over strings + ret = gsub(ret, "%(.%+%)%(%%d%+%)", "%(.-%)%(%%d%+%)") + -- cache it + sanitize_cache[pattern] = ret + end - return sanitize_cache[pattern] + return sanitize_cache[pattern] end -- [ cmatch ] @@ -265,18 +337,18 @@ local a, b, c, d, e local _, va, vb, vc, vd, ve local ra, rb, rc, rd, re function pfUI.api.cmatch(str, pat) - -- read capture indexes - a, b, c, d, e = GetCaptures(pat) - _, _, va, vb, vc, vd, ve = string.find(str, pfUI.api.SanitizePattern(pat)) - - -- put entries into the proper return values - ra = e == 1 and ve or d == 1 and vd or c == 1 and vc or b == 1 and vb or va - rb = e == 2 and ve or d == 2 and vd or c == 2 and vc or a == 2 and va or vb - rc = e == 3 and ve or d == 3 and vd or a == 3 and va or b == 3 and vb or vc - rd = e == 4 and ve or a == 4 and va or c == 4 and vc or b == 4 and vb or vd - re = a == 5 and va or d == 5 and vd or c == 5 and vc or b == 5 and vb or ve - - return ra, rb, rc, rd, re + -- read capture indexes + a, b, c, d, e = GetCaptures(pat) + _, _, va, vb, vc, vd, ve = string.find(str, pfUI.api.SanitizePattern(pat)) + + -- put entries into the proper return values + ra = e == 1 and ve or d == 1 and vd or c == 1 and vc or b == 1 and vb or va + rb = e == 2 and ve or d == 2 and vd or c == 2 and vc or a == 2 and va or vb + rc = e == 3 and ve or d == 3 and vd or a == 3 and va or b == 3 and vb or vc + rd = e == 4 and ve or a == 4 and va or c == 4 and vc or b == 4 and vb or vd + re = a == 5 and va or d == 5 and vd or c == 5 and vc or b == 5 and vb or ve + + return ra, rb, rc, rd, re end -- [ GetItemLinkByName ] @@ -284,13 +356,13 @@ end -- 'name' [string] name of the item -- returns: [string] entire itemLink for the given item function pfUI.api.GetItemLinkByName(name) - for itemID = 1, 25818 do - local itemName, hyperLink, itemQuality = GetItemInfo(itemID) - if (itemName and itemName == name) then - local _, _, _, hex = GetItemQualityColor(tonumber(itemQuality)) - return hex.. "|H"..hyperLink.."|h["..itemName.."]|h|r" + for itemID = 1, 25818 do + local itemName, hyperLink, itemQuality = GetItemInfo(itemID) + if (itemName and itemName == name) then + local _, _, _, hex = GetItemQualityColor(tonumber(itemQuality)) + return hex .. "|H" .. hyperLink .. "|h[" .. itemName .. "]|h|r" + end end - end end -- [ GetItemCount ] @@ -298,24 +370,24 @@ end -- 'itemName' [string] name of the item -- returns: [int] the number of the given item function pfUI.api.GetItemCount(itemName) - local count = 0 - for bag = 4, 0, -1 do - for slot = 1, GetContainerNumSlots(bag) do - local _, itemCount = GetContainerItemInfo(bag, slot) - if itemCount then - local itemLink = GetContainerItemLink(bag,slot) - local _, _, itemParse = strfind(itemLink, "(%d+):") - local queryName = GetItemInfo(itemParse) - if queryName and queryName ~= "" then - if queryName == itemName then - count = count + itemCount - end + local count = 0 + for bag = 4, 0, -1 do + for slot = 1, GetContainerNumSlots(bag) do + local _, itemCount = GetContainerItemInfo(bag, slot) + if itemCount then + local itemLink = GetContainerItemLink(bag, slot) + local _, _, itemParse = strfind(itemLink, "(%d+):") + local queryName = GetItemInfo(itemParse) + if queryName and queryName ~= "" then + if queryName == itemName then + count = count + itemCount + end + end + end end - end end - end - return count + return count end -- [ FindItem ] @@ -324,20 +396,20 @@ end -- returns: [int] bag -- [int] slot function pfUI.api.FindItem(item) - for bag = 4, 0, -1 do - for slot = 1, GetContainerNumSlots(bag) do - local itemLink = GetContainerItemLink(bag,slot) - if itemLink then - local _, _, parse = strfind(itemLink, "(%d+):") - local query = GetItemInfo(parse) - if query and query ~= "" and string.lower(query) == string.lower(item) then - return bag, slot + for bag = 4, 0, -1 do + for slot = 1, GetContainerNumSlots(bag) do + local itemLink = GetContainerItemLink(bag, slot) + if itemLink then + local _, _, parse = strfind(itemLink, "(%d+):") + local query = GetItemInfo(parse) + if query and query ~= "" and string.lower(query) == string.lower(item) then + return bag, slot + end + end end - end end - end - return nil + return nil end -- [ GetBagFamily ] @@ -346,22 +418,36 @@ end -- 'bag' [int] the bag id -- returns: [string] the type of the bag, e.g "QUIVER" function pfUI.api.GetBagFamily(bag) - if bag == -2 then return "KEYRING" end - if bag == 0 then return "BAG" end -- backpack - if bag == -1 then return "BAG" end -- bank - - local _, _, id = strfind(GetInventoryItemLink("player", ContainerIDToInventoryID(bag)) or "", "item:(%d+)") - if id then - local _, _, _, _, _, itemType, subType = GetItemInfo(id) - local bagsubtype = L["bagtypes"][subType] - - if bagsubtype == "DEFAULT" then return "BAG" end - if bagsubtype == "SOULBAG" then return "SOULBAG" end - if bagsubtype == "QUIVER" then return "QUIVER" end - if bagsubtype == nil then return "SPECIAL" end - end + if bag == -2 then + return "KEYRING" + end + if bag == 0 then + return "BAG" + end -- backpack + if bag == -1 then + return "BAG" + end -- bank + + local _, _, id = strfind(GetInventoryItemLink("player", ContainerIDToInventoryID(bag)) or "", "item:(%d+)") + if id then + local _, _, _, _, _, itemType, subType = GetItemInfo(id) + local bagsubtype = L["bagtypes"][subType] + + if bagsubtype == "DEFAULT" then + return "BAG" + end + if bagsubtype == "SOULBAG" then + return "SOULBAG" + end + if bagsubtype == "QUIVER" then + return "QUIVER" + end + if bagsubtype == nil then + return "SPECIAL" + end + end - return nil + return nil end -- [ Abbreviate ] @@ -369,35 +455,35 @@ end -- 'number' [number] the number that should be abbreviated -- 'returns: [string] the abbreviated value function pfUI.api.Abbreviate(number) - if pfUI_config.unitframes.abbrevnum == "1" then - local sign = number < 0 and -1 or 1 - number = math.abs(number) - - if number > 1000000 then - return pfUI.api.round(number/1000000*sign,2) .. "m" - elseif number > 1000 then - return pfUI.api.round(number/1000*sign,2) .. "k" + if pfUI_config.unitframes.abbrevnum == "1" then + local sign = number < 0 and -1 or 1 + number = math.abs(number) + + if number > 1000000 then + return pfUI.api.round(number / 1000000 * sign, 2) .. "m" + elseif number > 1000 then + return pfUI.api.round(number / 1000 * sign, 2) .. "k" + end end - end - return number + return number end -- [ SendChatMessageWide ] -- Sends a message to widest audience the player can broadcast to -- 'msg' [string] the message to send function pfUI.api.SendChatMessageWide(msg) - local channel = "SAY" - if UnitInRaid("player") then - if ( IsRaidLeader() or IsRaidOfficer() ) then - channel = "RAID_WARNING" - else - channel = "RAID" + local channel = "SAY" + if UnitInRaid("player") then + if (IsRaidLeader() or IsRaidOfficer()) then + channel = "RAID_WARNING" + else + channel = "RAID" + end + elseif UnitExists("party1") then + channel = "PARTY" end - elseif UnitExists("party1") then - channel = "PARTY" - end - SendChatMessage(msg,channel) + SendChatMessage(msg, channel) end -- [ GroupInfoByName ] @@ -405,48 +491,47 @@ end -- 'name' [string] party or raid member -- 'group' [string] "raid" or "party" -- returns: [table] {name='name',unitId='unitId',Id=Id,lclass='lclass',class='class'} -do -- create a scope so we don't have to worry about upvalue collisions - local party, raid, unitinfo = {}, {}, {} - party[0] = "player" -- fake unit - for i=1, MAX_PARTY_MEMBERS do - party[i] = "party"..i - end - for i=1, MAX_RAID_MEMBERS do - raid[i] = "raid"..i - end - function pfUI.api.GroupInfoByName(name,group) - unitinfo = pfUI.api.wipe(unitinfo) - if group == "party" then - for i=0, MAX_PARTY_MEMBERS do - local unitName = UnitName(party[i]) - if unitName == name then - local lclass,class = UnitClass(party[i]) - if not (lclass and class) then - lclass,class = _G.UNKNOWN, "UNKNOWN" - end - unitinfo.name,unitinfo.unitId,unitinfo.Id,unitinfo.lclass,unitinfo.class = - unitName,party[i],i,lclass,class - return unitinfo - end - end - elseif group == "raid" then - for i=1, MAX_RAID_MEMBERS do - local unitName = UnitName(raid[i]) - if unitName == name then - local lclass,class = UnitClass(raid[i]) - if not (lclass and class) then - lclass,class = _G.UNKNOWN, "UNKNOWN" - end - unitinfo.name,unitinfo.unitId,unitinfo.Id,unitinfo.lclass,unitinfo.class = - unitName,raid[i],i,lclass,class - return unitinfo +do + -- create a scope so we don't have to worry about upvalue collisions + local party, raid, unitinfo = {}, {}, {} + party[0] = "player" -- fake unit + for i = 1, MAX_PARTY_MEMBERS do + party[i] = "party" .. i + end + for i = 1, MAX_RAID_MEMBERS do + raid[i] = "raid" .. i + end + function pfUI.api.GroupInfoByName(name, group) + unitinfo = pfUI.api.wipe(unitinfo) + if group == "party" then + for i = 0, MAX_PARTY_MEMBERS do + local unitName = UnitName(party[i]) + if unitName == name then + local lclass, class = UnitClass(party[i]) + if not (lclass and class) then + lclass, class = _G.UNKNOWN, "UNKNOWN" + end + unitinfo.name, unitinfo.unitId, unitinfo.Id, unitinfo.lclass, unitinfo.class = unitName, party[i], i, lclass, class + return unitinfo + end + end + elseif group == "raid" then + for i = 1, MAX_RAID_MEMBERS do + local unitName = UnitName(raid[i]) + if unitName == name then + local lclass, class = UnitClass(raid[i]) + if not (lclass and class) then + lclass, class = _G.UNKNOWN, "UNKNOWN" + end + unitinfo.name, unitinfo.unitId, unitinfo.Id, unitinfo.lclass, unitinfo.class = unitName, raid[i], i, lclass, class + return unitinfo + end + end end - end + -- fallback for GetMasterLootCandidate not updating immediately for leavers + unitinfo.lclass, unitinfo.class = _G.UNKNOWN, "UNKNOWN" + return unitinfo end - -- fallback for GetMasterLootCandidate not updating immediately for leavers - unitinfo.lclass,unitinfo.class = _G.UNKNOWN, "UNKNOWN" - return unitinfo - end end -- [ HookScript ] @@ -455,11 +540,13 @@ end -- 'script' [string] the handler to hook -- 'func' [function] the function that should be added function HookScript(f, script, func) - local prev = f:GetScript(script) - f:SetScript(script, function(a1,a2,a3,a4,a5,a6,a7,a8,a9) - if prev then prev(a1,a2,a3,a4,a5,a6,a7,a8,a9) end - func(a1,a2,a3,a4,a5,a6,a7,a8,a9) - end) + local prev = f:GetScript(script) + f:SetScript(script, function(a1, a2, a3, a4, a5, a6, a7, a8, a9) + if prev then + prev(a1, a2, a3, a4, a5, a6, a7, a8, a9) + end + func(a1, a2, a3, a4, a5, a6, a7, a8, a9) + end) end -- [ HookAddonOrVariable ] @@ -467,54 +554,54 @@ end -- 'addon' [string] addon or variable name -- 'func' [function] function that should run function pfUI.api.HookAddonOrVariable(addon, func) - local lurker = CreateFrame("Frame", nil) - lurker.func = func - lurker:RegisterEvent("ADDON_LOADED") - lurker:RegisterEvent("VARIABLES_LOADED") - lurker:RegisterEvent("PLAYER_ENTERING_WORLD") - lurker:SetScript("OnEvent",function() - -- only run when config is available - if event == "ADDON_LOADED" and not this.foundConfig then - return - elseif event == "VARIABLES_LOADED" then - this.foundConfig = true - end - - if IsAddOnLoaded(addon) or _G[addon] then - this:func() - this:UnregisterAllEvents() - end - end) + local lurker = CreateFrame("Frame", nil) + lurker.func = func + lurker:RegisterEvent("ADDON_LOADED") + lurker:RegisterEvent("VARIABLES_LOADED") + lurker:RegisterEvent("PLAYER_ENTERING_WORLD") + lurker:SetScript("OnEvent", function() + -- only run when config is available + if event == "ADDON_LOADED" and not this.foundConfig then + return + elseif event == "VARIABLES_LOADED" then + this.foundConfig = true + end + + if IsAddOnLoaded(addon) or _G[addon] then + this:func() + this:UnregisterAllEvents() + end + end) end -- [ QueueFunction ] -- Add functions to a FIFO queue for execution after a short delay. -- '...' [vararg] function, [arguments] local timer -function pfUI.api.QueueFunction(a1,a2,a3,a4,a5,a6,a7,a8,a9) - if not timer then - timer = CreateFrame("Frame") - timer.queue = {} - timer.interval = TOOLTIP_UPDATE_TIME - timer.DeQueue = function() - local item = table.remove(timer.queue,1) - if item then - item[1](item[2],item[3],item[4],item[5],item[6],item[7],item[8],item[9]) - end - if table.getn(timer.queue) == 0 then - timer:Hide() -- no need to run the OnUpdate when the queue is empty - end - end - timer:SetScript("OnUpdate",function() - this.sinceLast = (this.sinceLast or 0) + arg1 - while (this.sinceLast > this.interval) do - this.DeQueue() - this.sinceLast = this.sinceLast - this.interval - end - end) - end - table.insert(timer.queue,{a1,a2,a3,a4,a5,a6,a7,a8,a9}) - timer:Show() -- start the OnUpdate +function pfUI.api.QueueFunction(a1, a2, a3, a4, a5, a6, a7, a8, a9) + if not timer then + timer = CreateFrame("Frame") + timer.queue = {} + timer.interval = TOOLTIP_UPDATE_TIME + timer.DeQueue = function() + local item = table.remove(timer.queue, 1) + if item then + item[1](item[2], item[3], item[4], item[5], item[6], item[7], item[8], item[9]) + end + if table.getn(timer.queue) == 0 then + timer:Hide() -- no need to run the OnUpdate when the queue is empty + end + end + timer:SetScript("OnUpdate", function() + this.sinceLast = (this.sinceLast or 0) + arg1 + while (this.sinceLast > this.interval) do + this.DeQueue() + this.sinceLast = this.sinceLast - this.interval + end + end) + end + table.insert(timer.queue, { a1, a2, a3, a4, a5, a6, a7, a8, a9 }) + timer:Show() -- start the OnUpdate end -- [ Create Gold String ] @@ -523,18 +610,24 @@ end -- return: [string] a colorized string which is split into -- gold,silver and copper values. function pfUI.api.CreateGoldString(money) - if type(money) ~= "number" then return "-" end + if type(money) ~= "number" then + return "-" + end - local gold = floor(money/ 100 / 100) - local silver = floor(mod((money/100),100)) - local copper = floor(mod(money,100)) + local gold = floor(money / 100 / 100) + local silver = floor(mod((money / 100), 100)) + local copper = floor(mod(money, 100)) - local string = "" - if gold > 0 then string = string .. "|cffffffff" .. gold .. "|cffffd700g" end - if silver > 0 or gold > 0 then string = string .. "|cffffffff " .. silver .. "|cffc7c7cfs" end - string = string .. "|cffffffff " .. copper .. "|cffeda55fc" + local string = "" + if gold > 0 then + string = string .. "|cffffffff" .. gold .. "|cffffd700g" + end + if silver > 0 or gold > 0 then + string = string .. "|cffffffff " .. silver .. "|cffc7c7cfs" + end + string = string .. "|cffffffff " .. copper .. "|cffeda55fc" - return string + return string end -- [ Enable Movable ] @@ -542,46 +635,52 @@ end -- 'name' [frame/string] Name of the Frame that should be movable -- 'addon' [string] Addon that must be loaded before being able to access the frame -- 'blacklist' [table] A list of frames that should be deactivated for mouse usage -local function OnDragStart() this:StartMoving() end -local function OnDragStop() this:StopMovingOrSizing() end +local function OnDragStart() + this:StartMoving() +end +local function OnDragStop() + this:StopMovingOrSizing() +end function pfUI.api.EnableMovable(name, addon, blacklist) - if addon then - local scan = CreateFrame("Frame") - scan:RegisterEvent("ADDON_LOADED") - scan:SetScript("OnEvent", function() - if arg1 == addon then - local frame = _G[name] - + if addon then + local scan = CreateFrame("Frame") + scan:RegisterEvent("ADDON_LOADED") + scan:SetScript("OnEvent", function() + if arg1 == addon then + local frame = _G[name] + + if blacklist then + for _, disable in pairs(blacklist) do + _G[disable]:EnableMouse(false) + end + end + + frame:SetMovable(true) + frame:EnableMouse(true) + frame:RegisterForDrag("LeftButton") + frame:SetScript("OnDragStart", OnDragStart) + frame:SetScript("OnDragStop", OnDragStop) + + this:UnregisterAllEvents() + end + end) + else if blacklist then - for _, disable in pairs(blacklist) do - _G[disable]:EnableMouse(false) - end + for _, disable in pairs(blacklist) do + _G[disable]:EnableMouse(false) + end end + local frame = name + if type(name) == "string" then + frame = _G[name] + end frame:SetMovable(true) frame:EnableMouse(true) frame:RegisterForDrag("LeftButton") frame:SetScript("OnDragStart", OnDragStart) frame:SetScript("OnDragStop", OnDragStop) - - this:UnregisterAllEvents() - end - end) - else - if blacklist then - for _, disable in pairs(blacklist) do - _G[disable]:EnableMouse(false) - end - end - - local frame = name - if type(name) == "string" then frame = _G[name] end - frame:SetMovable(true) - frame:EnableMouse(true) - frame:RegisterForDrag("LeftButton") - frame:SetScript("OnDragStart", OnDragStart) - frame:SetScript("OnDragStop", OnDragStop) - end + end end -- [ Copy Table ] @@ -590,21 +689,21 @@ end -- 'src' [table] the table that should be copied. -- return: [table] the replicated table. function pfUI.api.CopyTable(src) - local lookup_table = {} - local function _copy(src) - if type(src) ~= "table" then - return src - elseif lookup_table[src] then - return lookup_table[src] - end - local new_table = {} - lookup_table[src] = new_table - for index, value in pairs(src) do - new_table[_copy(index)] = _copy(value) - end - return setmetatable(new_table, getmetatable(src)) - end - return _copy(src) + local lookup_table = {} + local function _copy(src) + if type(src) ~= "table" then + return src + elseif lookup_table[src] then + return lookup_table[src] + end + local new_table = {} + lookup_table[src] = new_table + for index, value in pairs(src) do + new_table[_copy(index)] = _copy(value) + end + return setmetatable(new_table, getmetatable(src)) + end + return _copy(src) end -- [ Wipe Table ] @@ -612,20 +711,20 @@ end -- 'src' [table] the table that should be emptied. -- return: [table] the emptied table. function pfUI.api.wipe(src) - -- notes: table.insert, table.remove will have undefined behavior - -- when used on tables emptied this way because Lua removes nil - -- entries from tables after an indeterminate time. - -- Instead of table.insert(t,v) use t[table.getn(t)+1]=v as table.getn collapses nil entries. - -- There are no issues with hash tables, t[k]=v where k is not a number behaves as expected. - local mt = getmetatable(src) or {} - if mt.__mode == nil or mt.__mode ~= "kv" then - mt.__mode = "kv" - src=setmetatable(src,mt) - end - for k in pairs(src) do - src[k] = nil - end - return src + -- notes: table.insert, table.remove will have undefined behavior + -- when used on tables emptied this way because Lua removes nil + -- entries from tables after an indeterminate time. + -- Instead of table.insert(t,v) use t[table.getn(t)+1]=v as table.getn collapses nil entries. + -- There are no issues with hash tables, t[k]=v where k is not a number behaves as expected. + local mt = getmetatable(src) or {} + if mt.__mode == nil or mt.__mode ~= "kv" then + mt.__mode = "kv" + src = setmetatable(src, mt) + end + for k in pairs(src) do + src[k] = nil + end + return src end -- [ Load Movable ] @@ -633,52 +732,54 @@ end -- 'frame' [frame] the frame that should be positioned. -- 'init' [bool] treats the current position as initial data function pfUI.api.LoadMovable(frame, init) - -- update position data - if not frame.posdata or init then - frame.posdata = { scale = frame:GetScale(), pos = {} } - for i=1,frame:GetNumPoints() do - frame.posdata.pos[i] = { frame:GetPoint(i) } - end - end - - if pfUI_config["position"][frame:GetName()] then - if pfUI_config["position"][frame:GetName()]["parent"] then - frame:SetParent(_G[pfUI_config["position"][frame:GetName()]["parent"]]) + -- update position data + if not frame.posdata or init then + frame.posdata = { scale = frame:GetScale(), pos = {} } + for i = 1, frame:GetNumPoints() do + frame.posdata.pos[i] = { frame:GetPoint(i) } + end end - if pfUI_config["position"][frame:GetName()]["scale"] then - frame:SetScale(pfUI_config["position"][frame:GetName()].scale) - end + if pfUI_config["position"][frame:GetName()] then + if pfUI_config["position"][frame:GetName()]["parent"] then + frame:SetParent(_G[pfUI_config["position"][frame:GetName()]["parent"]]) + end - if pfUI_config["position"][frame:GetName()]["xpos"] then - local anchor = pfUI_config["position"][frame:GetName()]["anchor"] or "TOPLEFT" - frame:ClearAllPoints() - frame:SetPoint(anchor, pfUI_config["position"][frame:GetName()].xpos, pfUI_config["position"][frame:GetName()].ypos) - end - elseif frame.posdata and frame.posdata.pos[1] then - frame:ClearAllPoints() - frame:SetScale(frame.posdata.scale) + if pfUI_config["position"][frame:GetName()]["scale"] then + frame:SetScale(pfUI_config["position"][frame:GetName()].scale) + end - for id, point in pairs(frame.posdata.pos) do - local a, b, c, d, e = unpack(point) - if a and b then frame:SetPoint(a,b,c,d,e) end + if pfUI_config["position"][frame:GetName()]["xpos"] then + local anchor = pfUI_config["position"][frame:GetName()]["anchor"] or "TOPLEFT" + frame:ClearAllPoints() + frame:SetPoint(anchor, pfUI_config["position"][frame:GetName()].xpos, pfUI_config["position"][frame:GetName()].ypos) + end + elseif frame.posdata and frame.posdata.pos[1] then + frame:ClearAllPoints() + frame:SetScale(frame.posdata.scale) + + for id, point in pairs(frame.posdata.pos) do + local a, b, c, d, e = unpack(point) + if a and b then + frame:SetPoint(a, b, c, d, e) + end + end end - end end -- [ Save Movable ] -- Save the positions of a Frame. -- 'frame' [frame] the frame that should be saved. function pfUI.api.SaveMovable(frame, scale) - local anchor, _, _, xpos, ypos = frame:GetPoint() - C.position[frame:GetName()] = C.position[frame:GetName()] or {} - C.position[frame:GetName()]["xpos"] = round(xpos) - C.position[frame:GetName()]["ypos"] = round(ypos) - C.position[frame:GetName()]["anchor"] = anchor - C.position[frame:GetName()]["parent"] = frame:GetParent() and frame:GetParent():GetName() or nil - if scale then - C.position[frame:GetName()]["scale"] = frame:GetScale() - end + local anchor, _, _, xpos, ypos = frame:GetPoint() + C.position[frame:GetName()] = C.position[frame:GetName()] or {} + C.position[frame:GetName()]["xpos"] = round(xpos) + C.position[frame:GetName()]["ypos"] = round(ypos) + C.position[frame:GetName()]["anchor"] = anchor + C.position[frame:GetName()]["parent"] = frame:GetParent() and frame:GetParent():GetName() or nil + if scale then + C.position[frame:GetName()]["scale"] = frame:GetScale() + end end -- [ Update Movable ] @@ -687,25 +788,25 @@ end -- 'frame' [frame] the frame that should be updated. -- 'init' [bool] treats the current position as initial data function pfUI.api.UpdateMovable(frame, init) - local name = frame:GetName() + local name = frame:GetName() - if pfUI_config.global.offscreen == "0" then - frame:SetClampedToScreen(true) - end + if pfUI_config.global.offscreen == "0" then + frame:SetClampedToScreen(true) + end - if not pfUI.movables[name] then - pfUI.movables[name] = frame - end + if not pfUI.movables[name] then + pfUI.movables[name] = frame + end - LoadMovable(frame, init) + LoadMovable(frame, init) end -- [ Remove Movable ] -- Removes a Frame from the movable list. -- 'frame' [frame] the frame that should be removed. function pfUI.api.RemoveMovable(frame) - local name = frame:GetName() - pfUI.movables[name] = nil + local name = frame:GetName() + pfUI.movables[name] = nil end -- [ AlignToPosition ] @@ -742,68 +843,70 @@ end -- 'spacing' [number] the padding that should be used between the -- frame and its parent frame function pfUI.api.SetAutoPoint(frame, parent, spacing) - --[[ - - a b max - +-----------------+ - | 1 | 2 | 3 | - |-----+-----+-----| c - | 4 | 5 | 6 | - |-----+-----+-----| d - | 7 | 8 | 9 | - +-----------------+ - 0 - - ]]-- - - local a = GetScreenWidth() / 3 - local b = GetScreenWidth() / 3 * 2 - - local c = GetScreenHeight() / 3 * 2 - local d = GetScreenHeight() / 3 - - local x, y = parent:GetCenter() - - if not x or not y then return end + --[[ + + a b max + +-----------------+ + | 1 | 2 | 3 | + |-----+-----+-----| c + | 4 | 5 | 6 | + |-----+-----+-----| d + | 7 | 8 | 9 | + +-----------------+ + 0 + + ]]-- + + local a = GetScreenWidth() / 3 + local b = GetScreenWidth() / 3 * 2 + + local c = GetScreenHeight() / 3 * 2 + local d = GetScreenHeight() / 3 + + local x, y = parent:GetCenter() + + if not x or not y then + return + end - local off = spacing or 0 + local off = spacing or 0 - frame:ClearAllPoints() + frame:ClearAllPoints() - if x < a and y > c then - -- TOPLEFT - frame:SetPoint("TOPLEFT", parent, "BOTTOMLEFT", 0, -off) - elseif x > a and x < b and y > c then - -- TOP - frame:SetPoint("TOP", parent, "BOTTOM", 0, -off) - elseif x > b and y > c then - -- TOPRIGHT - frame:SetPoint("TOPRIGHT", parent, "BOTTOMRIGHT", 0, -off) - - elseif x < a and y > d and y < c then - -- LEFT - frame:SetPoint("LEFT", parent, "RIGHT", off, 0) - - elseif x > a and x < b and y > d and y < c then - -- CENTER - frame:SetPoint("BOTTOM", parent, "TOP", 0, off) - - elseif x > b and y > d and y < c then - -- RIGHT - frame:SetPoint("RIGHT", parent, "LEFT", -off, 0) - - elseif x < a and y < d then - -- BOTTOMLEFT - frame:SetPoint("BOTTOMLEFT", parent, "TOPLEFT", 0, off) - - elseif x > a and x < b and y < d then - -- BOTTOM - frame:SetPoint("BOTTOM", parent, "TOP", 0, off) - - elseif x > b and y < d then - -- BOTTOMRIGHT - frame:SetPoint("BOTTOMRIGHT", parent, "TOPRIGHT", 0, off) - end + if x < a and y > c then + -- TOPLEFT + frame:SetPoint("TOPLEFT", parent, "BOTTOMLEFT", 0, -off) + elseif x > a and x < b and y > c then + -- TOP + frame:SetPoint("TOP", parent, "BOTTOM", 0, -off) + elseif x > b and y > c then + -- TOPRIGHT + frame:SetPoint("TOPRIGHT", parent, "BOTTOMRIGHT", 0, -off) + + elseif x < a and y > d and y < c then + -- LEFT + frame:SetPoint("LEFT", parent, "RIGHT", off, 0) + + elseif x > a and x < b and y > d and y < c then + -- CENTER + frame:SetPoint("BOTTOM", parent, "TOP", 0, off) + + elseif x > b and y > d and y < c then + -- RIGHT + frame:SetPoint("RIGHT", parent, "LEFT", -off, 0) + + elseif x < a and y < d then + -- BOTTOMLEFT + frame:SetPoint("BOTTOMLEFT", parent, "TOPLEFT", 0, off) + + elseif x > a and x < b and y < d then + -- BOTTOM + frame:SetPoint("BOTTOM", parent, "TOP", 0, off) + + elseif x > b and y < d then + -- BOTTOMRIGHT + frame:SetPoint("BOTTOMRIGHT", parent, "TOPRIGHT", 0, off) + end end -- [ GetBestAnchor ] @@ -811,33 +914,35 @@ end -- 'self' [frame] the frame that should be checked -- returns: [string] the name of the best anchor function pfUI.api.GetBestAnchor(self) - local scale = self:GetScale() - local x, y = self:GetCenter() - local a = GetScreenWidth() / scale / 3 - local b = GetScreenWidth() / scale / 3 * 2 - local c = GetScreenHeight() / scale / 3 * 2 - local d = GetScreenHeight() / scale / 3 - if not x or not y then return end - - if x < a and y > c then - return "TOPLEFT" - elseif x > a and x < b and y > c then - return "TOP" - elseif x > b and y > c then - return "TOPRIGHT" - elseif x < a and y > d and y < c then - return "LEFT" - elseif x > a and x < b and y > d and y < c then - return "CENTER" - elseif x > b and y > d and y < c then - return "RIGHT" - elseif x < a and y < d then - return "BOTTOMLEFT" - elseif x > a and x < b and y < d then - return "BOTTOM" - elseif x > b and y < d then - return "BOTTOMRIGHT" - end + local scale = self:GetScale() + local x, y = self:GetCenter() + local a = GetScreenWidth() / scale / 3 + local b = GetScreenWidth() / scale / 3 * 2 + local c = GetScreenHeight() / scale / 3 * 2 + local d = GetScreenHeight() / scale / 3 + if not x or not y then + return + end + + if x < a and y > c then + return "TOPLEFT" + elseif x > a and x < b and y > c then + return "TOP" + elseif x > b and y > c then + return "TOPRIGHT" + elseif x < a and y > d and y < c then + return "LEFT" + elseif x > a and x < b and y > d and y < c then + return "CENTER" + elseif x > b and y > d and y < c then + return "RIGHT" + elseif x < a and y < d then + return "BOTTOMLEFT" + elseif x > a and x < b and y < d then + return "BOTTOM" + elseif x > b and y < d then + return "BOTTOMRIGHT" + end end -- [ ConvertFrameAnchor ] @@ -846,34 +951,34 @@ end -- 'anchor' [string] the new anchor that shall be used -- returns: anchor, x, y can directly be used in SetPoint() function pfUI.api.ConvertFrameAnchor(self, anchor) - local scale, x, y, _ = self:GetScale(), nil, nil, nil - - if anchor == "CENTER" then - x, y = self:GetCenter() - x, y = x - GetScreenWidth()/2/scale, y - GetScreenHeight()/2/scale - elseif anchor == "TOPLEFT" then - x, y = self:GetLeft(), self:GetTop() - GetScreenHeight()/scale - elseif anchor == "TOP" then - x, _ = self:GetCenter() - x, y = x - GetScreenWidth()/2/scale, self:GetTop() - GetScreenHeight()/scale - elseif anchor == "TOPRIGHT" then - x, y = self:GetRight() - GetScreenWidth()/scale, self:GetTop() - GetScreenHeight()/scale - elseif anchor == "RIGHT" then - _, y = self:GetCenter() - x, y = self:GetRight() - GetScreenWidth()/scale, y - GetScreenHeight()/2/scale - elseif anchor == "BOTTOMRIGHT" then - x, y = self:GetRight() - GetScreenWidth()/scale, self:GetBottom() - elseif anchor == "BOTTOM" then - x, _ = self:GetCenter() - x, y = x - GetScreenWidth()/2/scale, self:GetBottom() - elseif anchor == "BOTTOMLEFT" then - x, y = self:GetLeft(), self:GetBottom() - elseif anchor == "LEFT" then - _, y = self:GetCenter() - x, y = self:GetLeft(), y - GetScreenHeight()/2/scale - end + local scale, x, y, _ = self:GetScale(), nil, nil, nil + + if anchor == "CENTER" then + x, y = self:GetCenter() + x, y = x - GetScreenWidth() / 2 / scale, y - GetScreenHeight() / 2 / scale + elseif anchor == "TOPLEFT" then + x, y = self:GetLeft(), self:GetTop() - GetScreenHeight() / scale + elseif anchor == "TOP" then + x, _ = self:GetCenter() + x, y = x - GetScreenWidth() / 2 / scale, self:GetTop() - GetScreenHeight() / scale + elseif anchor == "TOPRIGHT" then + x, y = self:GetRight() - GetScreenWidth() / scale, self:GetTop() - GetScreenHeight() / scale + elseif anchor == "RIGHT" then + _, y = self:GetCenter() + x, y = self:GetRight() - GetScreenWidth() / scale, y - GetScreenHeight() / 2 / scale + elseif anchor == "BOTTOMRIGHT" then + x, y = self:GetRight() - GetScreenWidth() / scale, self:GetBottom() + elseif anchor == "BOTTOM" then + x, _ = self:GetCenter() + x, y = x - GetScreenWidth() / 2 / scale, self:GetBottom() + elseif anchor == "BOTTOMLEFT" then + x, y = self:GetLeft(), self:GetBottom() + elseif anchor == "LEFT" then + _, y = self:GetCenter() + x, y = self:GetLeft(), y - GetScreenHeight() / 2 / scale + end - return anchor, round(x, 2), round(y, 2) + return anchor, round(x, 2), round(y, 2) end -- [ GetStringColor ] @@ -881,11 +986,11 @@ end -- returns r,g,b,a local color_cache = {} function pfUI.api.GetStringColor(colorstr) - if not color_cache[colorstr] then - local r, g, b, a = pfUI.api.strsplit(",", colorstr) - color_cache[colorstr] = { r, g, b, a } - end - return unpack(color_cache[colorstr]) + if not color_cache[colorstr] then + local r, g, b, a = pfUI.api.strsplit(",", colorstr) + color_cache[colorstr] = { r, g, b, a } + end + return unpack(color_cache[colorstr]) end -- [ rgbhex ] @@ -897,52 +1002,56 @@ end -- returns color string in the form of '|caarrggbb' local _r, _g, _b, _a function pfUI.api.rgbhex(r, g, b, a) - if type(r) == "table" then - if r.r then - _r, _g, _b, _a = r.r, r.g, r.b, (r.a or 1) - elseif table.getn(r) >= 3 then - _r, _g, _b, _a = r[1], r[2], r[3], (r[4] or 1) - end - elseif tonumber(r) then - _r, _g, _b, _a = r, g, b, (a or 1) - end + if type(r) == "table" then + if r.r then + _r, _g, _b, _a = r.r, r.g, r.b, (r.a or 1) + elseif table.getn(r) >= 3 then + _r, _g, _b, _a = r[1], r[2], r[3], (r[4] or 1) + end + elseif tonumber(r) then + _r, _g, _b, _a = r, g, b, (a or 1) + end - if _r and _g and _b and _a then - -- limit values to 0-1 - _r = _r + 0 > 1 and 1 or _r + 0 - _g = _g + 0 > 1 and 1 or _g + 0 - _b = _b + 0 > 1 and 1 or _b + 0 - _a = _a + 0 > 1 and 1 or _a + 0 - return string.format("|c%02x%02x%02x%02x", _a*255, _r*255, _g*255, _b*255) - end + if _r and _g and _b and _a then + -- limit values to 0-1 + _r = _r + 0 > 1 and 1 or _r + 0 + _g = _g + 0 > 1 and 1 or _g + 0 + _b = _b + 0 > 1 and 1 or _b + 0 + _a = _a + 0 > 1 and 1 or _a + 0 + return string.format("|c%02x%02x%02x%02x", _a * 255, _r * 255, _g * 255, _b * 255) + end - return "" + return "" end -- [ GetBorderSize ] -- Returns the configure value of a border and its pixel scaled version. -- 'pref' allows to specifiy a custom border (i.e unitframes, panel) function pfUI.api.GetBorderSize(pref) - if not pfUI.borders then pfUI.borders = {} end + if not pfUI.borders then + pfUI.borders = {} + end - -- set to default border if accessing a wrong border type - if not pref or not pfUI_config.appearance.border[pref] or pfUI_config.appearance.border[pref] == "-1" then - pref = "default" - end + -- set to default border if accessing a wrong border type + if not pref or not pfUI_config.appearance.border[pref] or pfUI_config.appearance.border[pref] == "-1" then + pref = "default" + end - if pfUI.borders[pref] then - -- return already cached values - return pfUI.borders[pref][1], pfUI.borders[pref][2] - else - -- add new borders to the pfUI tree - local raw = tonumber(pfUI_config.appearance.border[pref]) - if raw == -1 then raw = 3 end + if pfUI.borders[pref] then + -- return already cached values + return pfUI.borders[pref][1], pfUI.borders[pref][2] + else + -- add new borders to the pfUI tree + local raw = tonumber(pfUI_config.appearance.border[pref]) + if raw == -1 then + raw = 3 + end - local scaled = raw * GetPerfectPixel() - pfUI.borders[pref] = { raw, scaled } + local scaled = raw * GetPerfectPixel() + pfUI.borders[pref] = { raw, scaled } - return raw, scaled - end + return raw, scaled + end end -- [ GetPerfectPixel ] @@ -950,37 +1059,39 @@ end -- Respects the current UI-scale and calculates a real pixel based on -- the screen resolution and the 768px sized drawlayer. function pfUI.api.GetPerfectPixel() - if pfUI.pixel then return pfUI.pixel end + if pfUI.pixel then + return pfUI.pixel + end - if pfUI_config.appearance.border.pixelperfect == "1" then - local scale = GetCVar("uiScale") - local resolution = GetCVar("gxResolution") - local _, _, screenwidth, screenheight = strfind(resolution, "(.+)x(.+)") + if pfUI_config.appearance.border.pixelperfect == "1" then + local scale = GetCVar("uiScale") + local resolution = GetCVar("gxResolution") + local _, _, screenwidth, screenheight = strfind(resolution, "(.+)x(.+)") - pfUI.pixel = 768 / screenheight / scale - pfUI.pixel = pfUI.pixel > 1 and 1 or pfUI.pixel + pfUI.pixel = 768 / screenheight / scale + pfUI.pixel = pfUI.pixel > 1 and 1 or pfUI.pixel - -- autodetect and zoom for HiDPI displays - if pfUI_config.appearance.border.hidpi == "1" then - pfUI.pixel = pfUI.pixel < .5 and pfUI.pixel * 2 or pfUI.pixel + -- autodetect and zoom for HiDPI displays + if pfUI_config.appearance.border.hidpi == "1" then + pfUI.pixel = pfUI.pixel < .5 and pfUI.pixel * 2 or pfUI.pixel + end + else + pfUI.pixel = .7 end - else - pfUI.pixel = .7 - end - pfUI.backdrop = { - bgFile = "Interface\\BUTTONS\\WHITE8X8", tile = false, tileSize = 0, - edgeFile = "Interface\\BUTTONS\\WHITE8X8", edgeSize = pfUI.pixel, - insets = {left = -pfUI.pixel, right = -pfUI.pixel, top = -pfUI.pixel, bottom = -pfUI.pixel}, - } + pfUI.backdrop = { + bgFile = "Interface\\BUTTONS\\WHITE8X8", tile = false, tileSize = 0, + edgeFile = "Interface\\BUTTONS\\WHITE8X8", edgeSize = pfUI.pixel, + insets = { left = -pfUI.pixel, right = -pfUI.pixel, top = -pfUI.pixel, bottom = -pfUI.pixel }, + } - pfUI.backdrop_thin = { - bgFile = "Interface\\BUTTONS\\WHITE8X8", tile = false, tileSize = 0, - edgeFile = "Interface\\BUTTONS\\WHITE8X8", edgeSize = pfUI.pixel, - insets = {left = 0, right = 0, top = 0, bottom = 0}, - } + pfUI.backdrop_thin = { + bgFile = "Interface\\BUTTONS\\WHITE8X8", tile = false, tileSize = 0, + edgeFile = "Interface\\BUTTONS\\WHITE8X8", edgeSize = pfUI.pixel, + insets = { left = 0, right = 0, top = 0, bottom = 0 }, + } - return pfUI.pixel + return pfUI.pixel end -- [ Create Backdrop ] @@ -991,134 +1102,144 @@ end -- 'transp' [number] set default transparency local backdrop, b, level, rawborder, border, br, bg, bb, ba, er, eg, eb, ea function pfUI.api.CreateBackdrop(f, inset, legacy, transp, backdropSetting) - -- exit if now frame was given - if not f then return end + -- exit if now frame was given + if not f then + return + end - -- load raw and pixel perfect scaled border - rawborder, border = GetBorderSize() + -- load raw and pixel perfect scaled border + rawborder, border = GetBorderSize() - -- load custom border if existing - if inset then - rawborder = inset / GetPerfectPixel() - border = inset - end + -- load custom border if existing + if inset then + rawborder = inset / GetPerfectPixel() + border = inset + end - -- detect if blizzard backdrops shall be used - local blizz = C.appearance.border.force_blizz == "1" and true or nil - backdrop = blizz and pfUI.backdrop_blizz_full or rawborder == 1 and pfUI.backdrop_thin or pfUI.backdrop - border = blizz and math.max(border, 3) or border + -- detect if blizzard backdrops shall be used + local blizz = C.appearance.border.force_blizz == "1" and true or nil + backdrop = blizz and pfUI.backdrop_blizz_full or rawborder == 1 and pfUI.backdrop_thin or pfUI.backdrop + border = blizz and math.max(border, 3) or border - -- get the color settings - br, bg, bb, ba = pfUI.api.GetStringColor(pfUI_config.appearance.border.background) - er, eg, eb, ea = pfUI.api.GetStringColor(pfUI_config.appearance.border.color) + -- get the color settings + br, bg, bb, ba = pfUI.api.GetStringColor(pfUI_config.appearance.border.background) + er, eg, eb, ea = pfUI.api.GetStringColor(pfUI_config.appearance.border.color) - if transp and transp < tonumber(ba) then ba = transp end + if transp and transp < tonumber(ba) then + ba = transp + end - -- use legacy backdrop handling - if legacy then - if backdropSetting then f:SetBackdrop(backdropSetting) end - f:SetBackdrop(backdrop) - f:SetBackdropColor(br, bg, bb, ba) - f:SetBackdropBorderColor(er, eg, eb , ea) - else - -- increase clickable area if available - if f.SetHitRectInsets and ( not InCombatLockdown or not InCombatLockdown()) then - f:SetHitRectInsets(-border,-border,-border,-border) - end - - -- use new backdrop behaviour - if not f.backdrop then - if f:GetBackdrop() then f:SetBackdrop(nil) end - - local b = CreateFrame("Frame", nil, f) - level = f:GetFrameLevel() - if level < 1 then - b:SetFrameLevel(level) - else - b:SetFrameLevel(level - 1) - end - - f.backdrop = b - end - - f.backdrop:SetPoint("TOPLEFT", f, "TOPLEFT", -border, border) - f.backdrop:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", border, -border) - f.backdrop:SetBackdrop(backdrop) - f.backdrop:SetBackdropColor(br, bg, bb, ba) - f.backdrop:SetBackdropBorderColor(er, eg, eb , ea) - - if blizz then - if not f.backdrop_border then - local border = CreateFrame("Frame", nil, f.backdrop) - border:SetFrameLevel(level + 2) - f.backdrop_border = border - - local hookSetBackdropBorderColor = f.backdrop.SetBackdropBorderColor - f.backdrop.SetBackdropBorderColor = function(self, r, g, b, a) - f.backdrop_border:SetBackdropBorderColor(r, g, b, a) - hookSetBackdropBorderColor(f.backdrop, r, g, b, a) + -- use legacy backdrop handling + if legacy then + if backdropSetting then + f:SetBackdrop(backdropSetting) + end + f:SetBackdrop(backdrop) + f:SetBackdropColor(br, bg, bb, ba) + f:SetBackdropBorderColor(er, eg, eb, ea) + else + -- increase clickable area if available + if f.SetHitRectInsets and (not InCombatLockdown or not InCombatLockdown()) then + f:SetHitRectInsets(-border, -border, -border, -border) + end + + -- use new backdrop behaviour + if not f.backdrop then + if f:GetBackdrop() then + f:SetBackdrop(nil) + end + + local b = CreateFrame("Frame", nil, f) + level = f:GetFrameLevel() + if level < 1 then + b:SetFrameLevel(level) + else + b:SetFrameLevel(level - 1) + end + + f.backdrop = b end - end - f.backdrop_border:SetAllPoints(f.backdrop) - f.backdrop_border:SetBackdrop(pfUI.backdrop_blizz_border) - f.backdrop_border:SetBackdropBorderColor(er, eg, eb , ea) + f.backdrop:SetPoint("TOPLEFT", f, "TOPLEFT", -border, border) + f.backdrop:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", border, -border) + f.backdrop:SetBackdrop(backdrop) + f.backdrop:SetBackdropColor(br, bg, bb, ba) + f.backdrop:SetBackdropBorderColor(er, eg, eb, ea) + + if blizz then + if not f.backdrop_border then + local border = CreateFrame("Frame", nil, f.backdrop) + border:SetFrameLevel(level + 2) + f.backdrop_border = border + + local hookSetBackdropBorderColor = f.backdrop.SetBackdropBorderColor + f.backdrop.SetBackdropBorderColor = function(self, r, g, b, a) + f.backdrop_border:SetBackdropBorderColor(r, g, b, a) + hookSetBackdropBorderColor(f.backdrop, r, g, b, a) + end + end + + f.backdrop_border:SetAllPoints(f.backdrop) + f.backdrop_border:SetBackdrop(pfUI.backdrop_blizz_border) + f.backdrop_border:SetBackdropBorderColor(er, eg, eb, ea) + end end - end end -- [ Create Shadow ] -- Creates a pfUI compatible frame as shadow element -- 'f' [frame] the frame which should get a backdrop. function pfUI.api.CreateBackdropShadow(f) - -- exit if now frame was given - if not f then return end + -- exit if now frame was given + if not f then + return + end - if f.backdrop_shadow or pfUI_config.appearance.border.shadow ~= "1" then - return - end + if f.backdrop_shadow or pfUI_config.appearance.border.shadow ~= "1" then + return + end - local anchor = f.backdrop or f - f.backdrop_shadow = CreateFrame("Frame", nil, anchor) - f.backdrop_shadow:SetFrameStrata("BACKGROUND") - f.backdrop_shadow:SetFrameLevel(1) - f.backdrop_shadow:SetPoint("TOPLEFT", anchor, "TOPLEFT", -5, 5) - f.backdrop_shadow:SetPoint("BOTTOMRIGHT", anchor, "BOTTOMRIGHT", 5, -5) - f.backdrop_shadow:SetBackdrop(pfUI.backdrop_shadow) - f.backdrop_shadow:SetBackdropBorderColor(0, 0, 0, tonumber(pfUI_config.appearance.border.shadow_intensity)) + local anchor = f.backdrop or f + f.backdrop_shadow = CreateFrame("Frame", nil, anchor) + f.backdrop_shadow:SetFrameStrata("BACKGROUND") + f.backdrop_shadow:SetFrameLevel(1) + f.backdrop_shadow:SetPoint("TOPLEFT", anchor, "TOPLEFT", -5, 5) + f.backdrop_shadow:SetPoint("BOTTOMRIGHT", anchor, "BOTTOMRIGHT", 5, -5) + f.backdrop_shadow:SetBackdrop(pfUI.backdrop_shadow) + f.backdrop_shadow:SetBackdropBorderColor(0, 0, 0, tonumber(pfUI_config.appearance.border.shadow_intensity)) end -- [ Bar Layout Options ] -- -- 'barsize' size of bar in number of buttons -- returns: array of options as strings for pfUI.gui.bar function pfUI.api.BarLayoutOptions(barsize) - assert(barsize > 0 and barsize <= NUM_ACTIONBAR_BUTTONS,"BarLayoutOptions: barsize "..tostring(barsize).." is invalid") - local options = {} - for i,layout in ipairs(pfGridmath[barsize]) do - options[i] = string.format("%d x %d",layout[1],layout[2]) - end - return options + assert(barsize > 0 and barsize <= NUM_ACTIONBAR_BUTTONS, "BarLayoutOptions: barsize " .. tostring(barsize) .. " is invalid") + local options = {} + for i, layout in ipairs(pfGridmath[barsize]) do + options[i] = string.format("%d x %d", layout[1], layout[2]) + end + return options end -- [ Bar Layout Formfactor ] -- -- 'option' string option as used in pfUI_config.bars[bar].option -- returns: integer formfactor local formfactors = {} -setmetatable(formfactors, {__mode = "v"}) -- weak table so values not referenced are collected on next gc +setmetatable(formfactors, { __mode = "v" }) -- weak table so values not referenced are collected on next gc function pfUI.api.BarLayoutFormfactor(option) - if formfactors[option] then - return formfactors[option] - else - for barsize,_ in ipairs(pfGridmath) do - local options = pfUI.api.BarLayoutOptions(barsize) - for i,opt in ipairs(options) do - if opt == option then - formfactors[option] = i - return formfactors[option] + if formfactors[option] then + return formfactors[option] + else + for barsize, _ in ipairs(pfGridmath) do + local options = pfUI.api.BarLayoutOptions(barsize) + for i, opt in ipairs(options) do + if opt == option then + formfactors[option] = i + return formfactors[option] + end + end end - end end - end end -- [ Bar Layout Size ] -- @@ -1126,14 +1247,14 @@ end -- 'barsize' integer number of buttons, -- 'formfactor' string formfactor in cols x rows, -- 'padding' the spacing between buttons -function pfUI.api.BarLayoutSize(bar,barsize,formfactor,iconsize,bordersize,padding) - assert(barsize > 0 and barsize <= NUM_ACTIONBAR_BUTTONS,"BarLayoutSize: barsize "..tostring(barsize).." is invalid") - local formfactor = pfUI.api.BarLayoutFormfactor(formfactor) - local cols, rows = unpack(pfGridmath[barsize][formfactor]) - local width = (iconsize + bordersize*2+padding) * cols + padding - local height = (iconsize + bordersize*2+padding) * rows + padding - bar._size = {width,height} - return bar._size +function pfUI.api.BarLayoutSize(bar, barsize, formfactor, iconsize, bordersize, padding) + assert(barsize > 0 and barsize <= NUM_ACTIONBAR_BUTTONS, "BarLayoutSize: barsize " .. tostring(barsize) .. " is invalid") + local formfactor = pfUI.api.BarLayoutFormfactor(formfactor) + local cols, rows = unpack(pfGridmath[barsize][formfactor]) + local width = (iconsize + bordersize * 2 + padding) * cols + padding + local height = (iconsize + bordersize * 2 + padding) * rows + padding + bar._size = { width, height } + return bar._size end -- [ Bar Button Anchor ] -- @@ -1144,72 +1265,80 @@ end -- 'iconsize' size of the button -- 'bordersize' default bordersize -- 'padding' the spacing between buttons -function pfUI.api.BarButtonAnchor(button,basename,buttonindex,barsize,formfactor,iconsize,bordersize,padding) - assert(barsize > 0 and barsize <= NUM_ACTIONBAR_BUTTONS,"BarButtonAnchor: barsize "..tostring(barsize).." is invalid") - local formfactor = pfUI.api.BarLayoutFormfactor(formfactor) - local parent = button:GetParent() - local cols, rows = unpack(pfGridmath[barsize][formfactor]) - if buttonindex == 1 then - button._anchor = {"TOPLEFT", parent, "TOPLEFT", bordersize+padding, -bordersize-padding} - else - local col = buttonindex-((math.ceil(buttonindex/cols)-1)*cols) - button._anchor = col==1 and {"TOP",_G[basename..(buttonindex-cols)],"BOTTOM",0,-(bordersize*2+padding)} or {"LEFT",_G[basename..(buttonindex-1)],"RIGHT",(bordersize*2+padding),0} - end - return button._anchor +function pfUI.api.BarButtonAnchor(button, basename, buttonindex, barsize, formfactor, iconsize, bordersize, padding) + assert(barsize > 0 and barsize <= NUM_ACTIONBAR_BUTTONS, "BarButtonAnchor: barsize " .. tostring(barsize) .. " is invalid") + local formfactor = pfUI.api.BarLayoutFormfactor(formfactor) + local parent = button:GetParent() + local cols, rows = unpack(pfGridmath[barsize][formfactor]) + if buttonindex == 1 then + button._anchor = { "TOPLEFT", parent, "TOPLEFT", bordersize + padding, -bordersize - padding } + else + local col = buttonindex - ((math.ceil(buttonindex / cols) - 1) * cols) + button._anchor = col == 1 and { "TOP", _G[basename .. (buttonindex - cols)], "BOTTOM", 0, -(bordersize * 2 + padding) } or { "LEFT", _G[basename .. (buttonindex - 1)], "RIGHT", (bordersize * 2 + padding), 0 } + end + return button._anchor end -- [ Enable Autohide ] -- -- 'frame' the frame that should be hidden function pfUI.api.EnableAutohide(frame, timeout, combat) - if not frame then return end - local timeout = timeout - - frame.hover = frame.hover or CreateFrame("Frame", frame:GetName() .. "Autohide", frame) - frame.hover:SetParent(frame) - frame.hover:SetAllPoints(frame) - frame.hover.parent = frame - frame.hover:Show() - - if combat then - frame.hover:RegisterEvent("PLAYER_REGEN_ENABLED") - frame.hover:RegisterEvent("PLAYER_REGEN_DISABLED") - frame.hover:SetScript("OnEvent", function() - if event == "PLAYER_REGEN_DISABLED" then - this.parent:SetAlpha(1) - this.activeTo = "keep" - elseif event == "PLAYER_REGEN_ENABLED" then - this.activeTo = GetTime() + timeout - end - end) - end - - frame.hover:SetScript("OnUpdate", function() - if this.activeTo == "keep" then return end - - if MouseIsOver(this, 10, -10, -10, 10) then - this.activeTo = GetTime() + timeout - this.parent:SetAlpha(1) - elseif this.activeTo then - if this.activeTo < GetTime() and this.parent:GetAlpha() > 0 then - local fps = (60 / math.max(GetFramerate(), 1)) - this.parent:SetAlpha(this.parent:GetAlpha() - 0.05*fps) - end - else - this.activeTo = GetTime() + timeout + if not frame then + return end - end) + local timeout = timeout + + frame.hover = frame.hover or CreateFrame("Frame", frame:GetName() .. "Autohide", frame) + frame.hover:SetParent(frame) + frame.hover:SetAllPoints(frame) + frame.hover.parent = frame + frame.hover:Show() + + if combat then + frame.hover:RegisterEvent("PLAYER_REGEN_ENABLED") + frame.hover:RegisterEvent("PLAYER_REGEN_DISABLED") + frame.hover:SetScript("OnEvent", function() + if event == "PLAYER_REGEN_DISABLED" then + this.parent:SetAlpha(1) + this.activeTo = "keep" + elseif event == "PLAYER_REGEN_ENABLED" then + this.activeTo = GetTime() + timeout + end + end) + end + + frame.hover:SetScript("OnUpdate", function() + if this.activeTo == "keep" then + return + end + + if MouseIsOver(this, 10, -10, -10, 10) then + this.activeTo = GetTime() + timeout + this.parent:SetAlpha(1) + elseif this.activeTo then + if this.activeTo < GetTime() and this.parent:GetAlpha() > 0 then + local fps = (60 / math.max(GetFramerate(), 1)) + this.parent:SetAlpha(this.parent:GetAlpha() - 0.05 * fps) + end + else + this.activeTo = GetTime() + timeout + end + end) end -- [ Disable Autohide ] -- -- 'frame' the frame that should get the autohide removed function pfUI.api.DisableAutohide(frame) - if not frame then return end - if not frame.hover then return end + if not frame then + return + end + if not frame.hover then + return + end - frame.hover:SetScript("OnEvent", nil) - frame.hover:SetScript("OnUpdate", nil) - frame.hover:Hide() - frame:SetAlpha(1) + frame.hover:SetScript("OnEvent", nil) + frame.hover:SetScript("OnUpdate", nil) + frame.hover:Hide() + frame:SetAlpha(1) end -- [ GetColoredTime ] -- @@ -1217,65 +1346,67 @@ end -- return a colored string including a time unit (m/h/d) local color_day, color_hour, color_minute, color_low, color_normal function pfUI.api.GetColoredTimeString(remaining) - if not remaining then return "" end - - -- Show days if remaining is > 99 Hours (99 * 60 * 60) - if remaining > 356400 then - if not color_day then - local r,g,b,a = pfUI.api.GetStringColor(C.appearance.cd.daycolor) - color_day = pfUI.api.rgbhex(r,g,b) + if not remaining then + return "" end - return color_day .. round(remaining / 86400) .. "|rd" + -- Show days if remaining is > 99 Hours (99 * 60 * 60) + if remaining > 356400 then + if not color_day then + local r, g, b, a = pfUI.api.GetStringColor(C.appearance.cd.daycolor) + color_day = pfUI.api.rgbhex(r, g, b) + end - -- Show hours if remaining is > 99 Minutes (99 * 60) - elseif remaining > 5940 then - if not color_hour then - local r,g,b,a = pfUI.api.GetStringColor(C.appearance.cd.hourcolor) - color_hour = pfUI.api.rgbhex(r,g,b) - end + return color_day .. round(remaining / 86400) .. "|rd" - return color_hour .. round(remaining / 3600) .. "|rh" + -- Show hours if remaining is > 99 Minutes (99 * 60) + elseif remaining > 5940 then + if not color_hour then + local r, g, b, a = pfUI.api.GetStringColor(C.appearance.cd.hourcolor) + color_hour = pfUI.api.rgbhex(r, g, b) + end - -- Show minutes if remaining is > 99 Seconds (99) - elseif remaining > 99 then - if not color_minute then - local r,g,b,a = pfUI.api.GetStringColor(C.appearance.cd.minutecolor) - color_minute = pfUI.api.rgbhex(r,g,b) - end + return color_hour .. round(remaining / 3600) .. "|rh" - return color_minute .. round(remaining / 60) .. "|rm" + -- Show minutes if remaining is > 99 Seconds (99) + elseif remaining > 99 then + if not color_minute then + local r, g, b, a = pfUI.api.GetStringColor(C.appearance.cd.minutecolor) + color_minute = pfUI.api.rgbhex(r, g, b) + end - -- Show milliseconds on low - elseif remaining <= 5 and pfUI_config.appearance.cd.milliseconds == "1" then - if not color_low then - local r,g,b,a = pfUI.api.GetStringColor(C.appearance.cd.lowcolor) - color_low = pfUI.api.rgbhex(r,g,b) - end + return color_minute .. round(remaining / 60) .. "|rm" - return color_low .. string.format("%.1f", round(remaining,1)) + -- Show milliseconds on low + elseif remaining <= 5 and pfUI_config.appearance.cd.milliseconds == "1" then + if not color_low then + local r, g, b, a = pfUI.api.GetStringColor(C.appearance.cd.lowcolor) + color_low = pfUI.api.rgbhex(r, g, b) + end - -- Show seconds on low - elseif remaining <= 5 then - if not color_low then - local r,g,b,a = pfUI.api.GetStringColor(C.appearance.cd.lowcolor) - color_low = pfUI.api.rgbhex(r,g,b) - end + return color_low .. string.format("%.1f", round(remaining, 1)) - return color_low .. round(remaining) + -- Show seconds on low + elseif remaining <= 5 then + if not color_low then + local r, g, b, a = pfUI.api.GetStringColor(C.appearance.cd.lowcolor) + color_low = pfUI.api.rgbhex(r, g, b) + end - -- Show seconds on normal - elseif remaining >= 0 then - if not color_normal then - local r, g, b, a = pfUI.api.GetStringColor(C.appearance.cd.normalcolor) - color_normal = pfUI.api.rgbhex(r,g,b) - end - return color_normal .. round(remaining) + return color_low .. round(remaining) - -- Return empty - else - return "" - end + -- Show seconds on normal + elseif remaining >= 0 then + if not color_normal then + local r, g, b, a = pfUI.api.GetStringColor(C.appearance.cd.normalcolor) + color_normal = pfUI.api.rgbhex(r, g, b) + end + return color_normal .. round(remaining) + + -- Return empty + else + return "" + end end -- [ GetColorGradient ] -- @@ -1283,37 +1414,37 @@ end -- return r,g,b and hexcolor local gradientcolors = {} function pfUI.api.GetColorGradient(perc) - perc = perc > 1 and 1 or perc - perc = perc < 0 and 0 or perc - perc = floor(perc*100)/100 - - local index = perc - if not gradientcolors[index] then - local r1, g1, b1, r2, g2, b2 - - if perc <= 0.5 then - perc = perc * 2 - r1, g1, b1 = 1, 0, 0 - r2, g2, b2 = 1, 1, 0 - else - perc = perc * 2 - 1 - r1, g1, b1 = 1, 1, 0 - r2, g2, b2 = 0, 1, 0 - end + perc = perc > 1 and 1 or perc + perc = perc < 0 and 0 or perc + perc = floor(perc * 100) / 100 + + local index = perc + if not gradientcolors[index] then + local r1, g1, b1, r2, g2, b2 + + if perc <= 0.5 then + perc = perc * 2 + r1, g1, b1 = 1, 0, 0 + r2, g2, b2 = 1, 1, 0 + else + perc = perc * 2 - 1 + r1, g1, b1 = 1, 1, 0 + r2, g2, b2 = 0, 1, 0 + end - local r = round(r1 + (r2 - r1) * perc, 4) - local g = round(g1 + (g2 - g1) * perc, 4) - local b = round(b1 + (b2 - b1) * perc, 4) - local h = pfUI.api.rgbhex(r,g,b) + local r = round(r1 + (r2 - r1) * perc, 4) + local g = round(g1 + (g2 - g1) * perc, 4) + local b = round(b1 + (b2 - b1) * perc, 4) + local h = pfUI.api.rgbhex(r, g, b) - gradientcolors[index] = {} - gradientcolors[index].r = r - gradientcolors[index].g = g - gradientcolors[index].b = b - gradientcolors[index].h = h - end + gradientcolors[index] = {} + gradientcolors[index].r = r + gradientcolors[index].g = g + gradientcolors[index].b = b + gradientcolors[index].h = h + end - return gradientcolors[index].r, + return gradientcolors[index].r, gradientcolors[index].g, gradientcolors[index].b, gradientcolors[index].h @@ -1327,47 +1458,104 @@ end -- 'arg1' [string] -- return object function pfUI.api.GetNoNameObject(frame, objtype, layer, arg1, arg2) - local arg1 = arg1 and gsub(arg1, "([%+%-%*%(%)%?%[%]%^])", "%%%1") - local arg2 = arg2 and gsub(arg2, "([%+%-%*%(%)%?%[%]%^])", "%%%1") - - local objects - if objtype == "Texture" or objtype == "FontString" then - objects = {frame:GetRegions()} - else - objects = {frame:GetChildren()} - end + local arg1 = arg1 and gsub(arg1, "([%+%-%*%(%)%?%[%]%^])", "%%%1") + local arg2 = arg2 and gsub(arg2, "([%+%-%*%(%)%?%[%]%^])", "%%%1") - for _, object in ipairs(objects) do - local check = true - if object:GetObjectType() ~= objtype or (layer and object:GetDrawLayer() ~= layer) then check = false end - - if check then - if objtype == "Texture" and object.SetTexture and object:GetTexture() ~= "Interface\\BUTTONS\\WHITE8X8" then - if arg1 then - local texture = object:GetTexture() - if texture and not string.find(texture, arg1, 1) then check = false end - end + local objects + if objtype == "Texture" or objtype == "FontString" then + objects = { frame:GetRegions() } + else + objects = { frame:GetChildren() } + end - if check then return object end - elseif objtype == "FontString" and object.SetText then - if arg1 then - local text = object:GetText() - if text and not string.find(text, arg1, 1) then check = false end + for _, object in ipairs(objects) do + local check = true + if object:GetObjectType() ~= objtype or (layer and object:GetDrawLayer() ~= layer) then + check = false end - if check then return object end - elseif objtype == "Button" and object.GetNormalTexture and object:GetNormalTexture() then - if arg1 then - local texture = object:GetNormalTexture():GetTexture() - if texture and not string.find(texture, arg1, 1) then check = false end + if check then + if objtype == "Texture" and object.SetTexture and object:GetTexture() ~= "Interface\\BUTTONS\\WHITE8X8" then + if arg1 then + local texture = object:GetTexture() + if texture and not string.find(texture, arg1, 1) then + check = false + end + end + + if check then + return object + end + elseif objtype == "FontString" and object.SetText then + if arg1 then + local text = object:GetText() + if text and not string.find(text, arg1, 1) then + check = false + end + end + + if check then + return object + end + elseif objtype == "Button" and object.GetNormalTexture and object:GetNormalTexture() then + if arg1 then + local texture = object:GetNormalTexture():GetTexture() + if texture and not string.find(texture, arg1, 1) then + check = false + end + end + if arg2 then + local text = object:GetText() + if text and not string.find(text, arg2, 1) then + check = false + end + end + + if check then + return object + end + end end - if arg2 then - local text = object:GetText() - if text and not string.find(text, arg2, 1) then check = false end - end - - if check then return object end - end end - end end + +pfUI.api.PLAYER_BUFF_NOT_FOUND = pfUI.expansion == "vanilla" and -1 or 0 + +-- [ GetPlayerBuffX ] -- +-- +-- 'buffId' [number] The id of the buff to retrieve. Starts at 1 and can go up to at least 16. In turtle wow it can go up to 24. +-- Bare in mind that in Vanilla the GetPlayerBuff() method, that we are wrapping around, had 'buffId' being 0-based +-- and was changed to be 1-based in TBC. We enforce the TBC behavior here even in Vanilla for the sake of uniformity. +-- +-- 'buffFilter' [string] The "filter" to use when selecting buffs. Will affect what types of buffs are retrieved. Theoretically, it can +-- be any combination of HELPFUL, HARMFUL, PASSIVE, CANCELABLE, NOT_CANCELABLE according to comments in the source +-- code of the original GetPlayerBuff() we are wrapping around. +-- +-- return 'buffIndex', 'untilCancelled' +-- +-- 'buffIndex' [number] The index of the buff - if it's not found it will return 'pfUI.api.PLAYER_BUFF_NOT_FOUND' (which is -1 in Vanilla and 0 in TBC onwards) +-- 'untilCancelled' [number] If 1 this buff will last until it is cancelled (aura, aspect, stealth) +-- +-- Example of usage: +-- +-- for i=1,32,1 do +-- local buffIndex, untilCancelled = pfUI.api.GetPlayerBuffX(i, "HELPFUL") +-- if buffIndex == pfUI.api.PLAYER_BUFF_NOT_FOUND then +-- [...handle buff not found...] +-- else +-- [...handle buff found...] +-- +-- local buffTexture = GetPlayerBuffTexture(buffIndex) -- this simply works properly on both vanilla in tbc +-- end +-- end +-- +pfUI.api.GetPlayerBuffX = pfUI.expansion == "vanilla" and (-- vanilla: + function(buffId, buffFilter) + + local buffIndex, untilCancelled = _G.GetPlayerBuff(buffId - 1, buffFilter) + + -- we intentionally leave 'buffIndex' as is (0-based) for Vanilla if we translate it into a 1-based index like in TBC we would + -- break the rest of the Vanilla API surface that expects 'buffIndex' to be 0-based in case of success and -1 in case of failure + return buffIndex, untilCancelled + end +) or _G.GetPlayerBuff -- tbc onwards diff --git a/api/ui-widgets.lua b/api/ui-widgets.lua index 3abc6765..f428e1f7 100644 --- a/api/ui-widgets.lua +++ b/api/ui-widgets.lua @@ -1199,10 +1199,11 @@ function pfUI.api.CreateInfoBox(text, time, parent, height) infobox:Hide() infobox:SetScript("OnUpdate", function() - local time = infobox.lastshow + infobox.duration - GetTime() + local now = GetTime() + local time = infobox.lastshow + infobox.duration - now infobox.timeout:SetValue(time) - if GetTime() > infobox.lastshow + infobox.duration then + if now > infobox.lastshow + infobox.duration then infobox:SetAlpha(infobox:GetAlpha()-0.05) if infobox:GetAlpha() <= 0.1 then diff --git a/api/unitframes.lua b/api/unitframes.lua index 9c1b9465..4c804291 100644 --- a/api/unitframes.lua +++ b/api/unitframes.lua @@ -33,9 +33,16 @@ local glow2 = { local maxdurations = {} local function BuffOnUpdate() - if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + .2 end - local timeleft = GetPlayerBuffTimeLeft(GetPlayerBuff(PLAYER_BUFF_START_ID+this.id,"HELPFUL")) - local texture = GetPlayerBuffTexture(GetPlayerBuff(PLAYER_BUFF_START_ID+this.id,"HELPFUL")) + local now = GetTime() + if (this.tick or 1) > now then + return + end + + this.tick = now + .2 + + local bid = pfUI.api.GetPlayerBuffX(this.id,"HELPFUL") + local timeleft = GetPlayerBuffTimeLeft(bid) + local texture = GetPlayerBuffTexture(bid) local start = 0 if timeleft > 0 then @@ -44,7 +51,8 @@ local function BuffOnUpdate() elseif maxdurations[texture] and maxdurations[texture] < timeleft then maxdurations[texture] = timeleft end - start = GetTime() + timeleft - maxdurations[texture] + + start = now + timeleft - maxdurations[texture] end CooldownFrame_SetTimer(this.cd, start, maxdurations[texture], timeleft > 0 and 1 or 0) @@ -65,13 +73,13 @@ local function BuffOnEnter() GameTooltip:SetOwner(this, "ANCHOR_BOTTOMRIGHT") if parent.label == "player" then - GameTooltip:SetPlayerBuff(GetPlayerBuff(PLAYER_BUFF_START_ID+this.id,"HELPFUL")) + GameTooltip:SetPlayerBuff(pfUI.api.GetPlayerBuffX(this.id,"HELPFUL")) else GameTooltip:SetUnitBuff(parent.label .. parent.id, this.id) end if IsShiftKeyDown() then - local texture = parent.label == "player" and GetPlayerBuffTexture(GetPlayerBuff(PLAYER_BUFF_START_ID+this.id,"HELPFUL")) or UnitBuff(parent.label .. parent.id, this.id) + local texture = parent.label == "player" and GetPlayerBuffTexture(pfUI.api.GetPlayerBuffX(this.id,"HELPFUL")) or UnitBuff(parent.label .. parent.id, this.id) local playerlist = "" local first = true @@ -114,14 +122,20 @@ end local function BuffOnClick() if this:GetParent().label == "player" then - CancelPlayerBuff(GetPlayerBuff(PLAYER_BUFF_START_ID+this.id,"HELPFUL")) + CancelPlayerBuff(pfUI.api.GetPlayerBuffX(this.id,"HELPFUL")) end end local function DebuffOnUpdate() - if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + .2 end - local timeleft = GetPlayerBuffTimeLeft(GetPlayerBuff(PLAYER_BUFF_START_ID+this.id,"HARMFUL")) - local texture = GetPlayerBuffTexture(GetPlayerBuff(PLAYER_BUFF_START_ID+this.id,"HARMFUL")) + local now = GetTime() + if (this.tick or 1) > now then + return + end + + this.tick = now + .2 + local bid = pfUI.api.GetPlayerBuffX(this.id,"HARMFUL") + local timeleft = GetPlayerBuffTimeLeft(bid) + local texture = GetPlayerBuffTexture(bid) local start = 0 if timeleft > 0 then @@ -130,7 +144,8 @@ local function DebuffOnUpdate() elseif maxdurations[texture] and maxdurations[texture] < timeleft then maxdurations[texture] = timeleft end - start = GetTime() + timeleft - maxdurations[texture] + + start = now + timeleft - maxdurations[texture] end CooldownFrame_SetTimer(this.cd, start, maxdurations[texture], timeleft > 0 and 1 or 0) @@ -141,7 +156,7 @@ local function DebuffOnEnter() GameTooltip:SetOwner(this, "ANCHOR_BOTTOMRIGHT") if this:GetParent().label == "player" then - GameTooltip:SetPlayerBuff(GetPlayerBuff(PLAYER_BUFF_START_ID+this.id,"HARMFUL")) + GameTooltip:SetPlayerBuff(pfUI.api.GetPlayerBuffX(this.id,"HARMFUL")) else GameTooltip:SetUnitDebuff(this:GetParent().label .. this:GetParent().id, this.id) end @@ -153,25 +168,33 @@ end local function DebuffOnClick() if this:GetParent().label == "player" then - CancelPlayerBuff(GetPlayerBuff(PLAYER_BUFF_START_ID+this.id,"HARMFUL")) + CancelPlayerBuff(pfUI.api.GetPlayerBuffX(this.id,"HARMFUL")) end end local visibilityscan = CreateFrame("Frame", "pfUnitFrameVisibility", UIParent) visibilityscan.frames = {} visibilityscan:SetScript("OnUpdate", function() - if ( this.limit or 1) > GetTime() then return else this.limit = GetTime() + .2 end - for frame in pairs(this.frames) do frame:UpdateVisibility() end + local now = GetTime() + if (this.limit or 1) > now then + return + end + + this.limit = now + .2 + for frame in pairs(this.frames) do + frame:UpdateVisibility() + end end) local aggrodata = { } function pfUI.api.UnitHasAggro(unit) - if aggrodata[unit] and GetTime() < aggrodata[unit].check + 1 then + local now = GetTime() + if aggrodata[unit] and now < aggrodata[unit].check + 1 then return aggrodata[unit].state end aggrodata[unit] = aggrodata[unit] or { } - aggrodata[unit].check = GetTime() + aggrodata[unit].check = now aggrodata[unit].state = 0 if UnitExists(unit) and UnitIsFriend(unit, "player") then @@ -1075,11 +1098,13 @@ function pfUI.uf.OnUpdate() end -- trigger eventless actions (online/offline/range) - if not this.lastTick then this.lastTick = GetTime() + (this.tick or .2) end - if this.lastTick and this.lastTick < GetTime() then + local now = GetTime() + if not this.lastTick then this.lastTick = now + (this.tick or .2) end + + if this.lastTick and this.lastTick < now then local unitstr = this.label .. this.id - this.lastTick = GetTime() + (this.tick or .2) + this.lastTick = now + (this.tick or .2) -- target target has a huge delay, make sure to not tick during range checks -- by waiting for a stable name over three ticks otherwise aborting the update. @@ -1510,14 +1535,15 @@ function pfUI.uf:RefreshUnit(unit, component) -- buffs if unit.buffs and ( component == "all" or component == "aura" ) then - local texture, stacks + local texture, stacks, bid for i=1, unit.config.bufflimit do if not unit.buffs[i] then break end if unit.label == "player" then - stacks = GetPlayerBuffApplications(GetPlayerBuff(PLAYER_BUFF_START_ID+i,"HELPFUL")) - texture = GetPlayerBuffTexture(GetPlayerBuff(PLAYER_BUFF_START_ID+i,"HELPFUL")) + bid = pfUI.api.GetPlayerBuffX(i,"HELPFUL") + stacks = GetPlayerBuffApplications(bid) + texture = GetPlayerBuffTexture(bid) else texture, stacks = pfUI.uf:DetectBuff(unitstr, i) end @@ -1577,10 +1603,11 @@ function pfUI.uf:RefreshUnit(unit, component) reposition = true end + local row, bid, now for i=1, unit.config.debufflimit do if not unit.debuffs[i] then break end - local row = floor((i-1) / unit.config.debuffperrow) + row = floor((i-1) / unit.config.debuffperrow) if reposition then local anchor = unit.config.portraitheight ~= "-1" and unit.hp or unit @@ -1594,9 +1621,10 @@ function pfUI.uf:RefreshUnit(unit, component) end if unit.label == "player" then - texture = GetPlayerBuffTexture(GetPlayerBuff(PLAYER_BUFF_START_ID+i, "HARMFUL")) - stacks = GetPlayerBuffApplications(GetPlayerBuff(PLAYER_BUFF_START_ID+i, "HARMFUL")) - dtype = GetPlayerBuffDispelType(GetPlayerBuff(PLAYER_BUFF_START_ID+i, "HARMFUL")) + bid = pfUI.api.GetPlayerBuffX(i, "HARMFUL") + texture = GetPlayerBuffTexture(bid) + stacks = GetPlayerBuffApplications(bid) + dtype = GetPlayerBuffDispelType(bid) elseif selfdebuff == "1" then _, _, texture, stacks, dtype = libdebuff:UnitOwnDebuff(unitstr, i) else @@ -1615,8 +1643,9 @@ function pfUI.uf:RefreshUnit(unit, component) unit.debuffs[i]:Show() if unit:GetName() == "pfPlayer" then - local timeleft = GetPlayerBuffTimeLeft(GetPlayerBuff(PLAYER_BUFF_START_ID+unit.debuffs[i].id, "HARMFUL"),"HARMFUL") - CooldownFrame_SetTimer(unit.debuffs[i].cd, GetTime(), timeleft, 1) + local timeleft = GetPlayerBuffTimeLeft(pfUI.api.GetPlayerBuffX(unit.debuffs[i].id, "HARMFUL"),"HARMFUL") + now = now or GetTime() + CooldownFrame_SetTimer(unit.debuffs[i].cd, now, timeleft, 1) elseif libdebuff and selfdebuff == "1" then local name, rank, texture, stacks, dtype, duration, timeleft, caster = libdebuff:UnitOwnDebuff(unitstr, i) if duration and timeleft then @@ -1625,7 +1654,8 @@ function pfUI.uf:RefreshUnit(unit, component) elseif libdebuff then local name, rank, texture, stacks, dtype, duration, timeleft, caster = libdebuff:UnitDebuff(unitstr, i) if duration and timeleft then - CooldownFrame_SetTimer(unit.debuffs[i].cd, GetTime() + timeleft - duration, duration, 1) + now = now or GetTime() + CooldownFrame_SetTimer(unit.debuffs[i].cd, now + timeleft - duration, duration, 1) end end @@ -2038,34 +2068,35 @@ function pfUI.uf:EnableClickCast() for bid, button in pairs(buttons) do for modifier, mconf in pairs(modifiers) do local bconf = bid == 1 and "" or bid - if pfUI_config.unitframes["clickcast"..bconf..mconf] ~= "" then + local macro_text = pfUI_config.unitframes["clickcast"..bconf..mconf] + if macro_text ~= "" then -- prepare click casting if pfUI.client > 11200 then -- set attributes for tbc+ local prefix = modifier == "" and "" or modifier .. "-" -- check for "/" in the beginning of the string, to detect macros - if string.find(pfUI_config.unitframes["clickcast"..bconf..mconf], "^%/(.+)") then + if string.sub(macro_text, 1, 1) == "/" then self:SetAttribute(prefix.."type"..bid, "macro") - self:SetAttribute(prefix.."macrotext"..bid, pfUI_config.unitframes["clickcast"..bconf..mconf]) + self:SetAttribute(prefix.."macrotext"..bid, macro_text) self:SetAttribute(prefix.."spell"..bid, nil) - elseif string.find(pfUI_config.unitframes["clickcast"..bconf..mconf], "^target") then + elseif string.find(macro_text, "^target") then self:SetAttribute(prefix.."type"..bid, "target") self:SetAttribute(prefix.."macrotext"..bid, nil) self:SetAttribute(prefix.."spell"..bid, nil) - elseif string.find(pfUI_config.unitframes["clickcast"..bconf..mconf], "^menu") then + elseif string.find(macro_text, "^menu") then self:SetAttribute(prefix.."type"..bid, "showmenu") self:SetAttribute(prefix.."macrotext"..bid, nil) self:SetAttribute(prefix.."spell"..bid, nil) else self:SetAttribute(prefix.."type"..bid, "spell") - self:SetAttribute(prefix.."spell"..bid, pfUI_config.unitframes["clickcast"..bconf..mconf]) + self:SetAttribute(prefix.."spell"..bid, macro_text) self:SetAttribute(prefix.."macro"..bid, nil) end else -- fill clickaction table for vanillla self.clickactions = self.clickactions or {} - self.clickactions[modifier..button] = pfUI_config.unitframes["clickcast"..bconf..mconf] + self.clickactions[modifier..button] = macro_text end end end @@ -2103,7 +2134,7 @@ function pfUI.uf:ClickAction(button) showmenu = nil else -- run click cast action - local is_macro = string.find(this.clickactions[modstring], "^%/(.+)") + local is_macro = string.sub(this.clickactions[modstring], 1, 1) == "/" if superwow_active and not is_macro then CastSpellByName(this.clickactions[modstring], unitstr) diff --git a/compat/tbc.lua b/compat/tbc.lua index 526d94bf..e0f0fe8a 100644 --- a/compat/tbc.lua +++ b/compat/tbc.lua @@ -27,7 +27,7 @@ FRIENDS_NAME_LOCATION = "ButtonTextLocation" COOLDOWN_FRAME_TYPE = "Cooldown" LOOT_BUTTON_FRAME_TYPE = "Button" -PLAYER_BUFF_START_ID = 0 +PLAYER_BUFF_START_ID = 0 -- deprecated kept around for 3rd party addons that might rely on it should be removed completely in the future ACTIONBAR_SECURE_TEMPLATE_BAR = "SecureStateHeaderTemplate" ACTIONBAR_SECURE_TEMPLATE_BUTTON = "SecureActionButtonTemplate" diff --git a/compat/vanilla.lua b/compat/vanilla.lua index abc817a5..16ebbb9f 100644 --- a/compat/vanilla.lua +++ b/compat/vanilla.lua @@ -27,7 +27,7 @@ FRIENDS_NAME_LOCATION = "ButtonTextNameLocation" COOLDOWN_FRAME_TYPE = "Model" LOOT_BUTTON_FRAME_TYPE = "LootButton" -PLAYER_BUFF_START_ID = -1 +PLAYER_BUFF_START_ID = -1 -- deprecated kept around for 3rd party addons that might rely on it should be removed completely in the future ACTIONBAR_SECURE_TEMPLATE_BAR = nil ACTIONBAR_SECURE_TEMPLATE_BUTTON = nil diff --git a/libs/libcast.lua b/libs/libcast.lua index 11cada49..c29e503e 100644 --- a/libs/libcast.lua +++ b/libs/libcast.lua @@ -373,18 +373,26 @@ libcast.customcast[strlower(multishot)] = function(begin, duration) end end +local lastRawSpellName, lastfunc +local function GetCustomCastFunc(rawSpellName) + -- to ultra-optimize cases in which paladins spam 'flash of light' all the time this allows us to skip strlower + table lookup completely + if rawequal(lastRawSpellName, rawSpellName) then return lastfunc end + + lastRawSpellName = rawSpellName + + return libcast.customcast[strlower(rawSpellName)] +end + local function CastCustom(id, bookType, rawSpellName, rank, texture, castingTime) if not id or not rawSpellName or not castingTime then return end -- ignore if the spell is not found or if it is instant-cast lastrank = rank lastcasttex = texture - local func = libcast.customcast[strlower(rawSpellName)] - if not func then return end - - if GetSpellCooldown(id, bookType) == 0 or UnitCastingInfo(player) then return end -- detect casting + lastfunc = GetCustomCastFunc(rawSpellName) + if not lastfunc or GetSpellCooldown(id, bookType) == 0 or UnitCastingInfo(player) then return end -- detect casting - func(true) + lastfunc(true) end hooksecurefunc("UseContainerItem", function(id, index) diff --git a/libs/libdebuff.lua b/libs/libdebuff.lua index 27924622..93067b95 100644 --- a/libs/libdebuff.lua +++ b/libs/libdebuff.lua @@ -266,15 +266,17 @@ function libdebuff:UnitDebuff(unit, id) effect = scanner:Line(1) or "" end + local now = GetTime() + -- read level based debuff table local data = libdebuff.objects[unitname] and libdebuff.objects[unitname][unitlevel] data = data or libdebuff.objects[unitname] and libdebuff.objects[unitname][0] if data and data[effect] then - if data[effect].duration and data[effect].start and data[effect].duration + data[effect].start > GetTime() then + if data[effect].duration and data[effect].start and data[effect].duration + data[effect].start > now then -- read valid debuff data duration = data[effect].duration - timeleft = duration + data[effect].start - GetTime() + timeleft = duration + data[effect].start - now caster = data[effect].caster else -- clean up invalid values diff --git a/libs/libpredict.lua b/libs/libpredict.lua index 448876c8..c7259da2 100644 --- a/libs/libpredict.lua +++ b/libs/libpredict.lua @@ -390,43 +390,77 @@ local function UpdateCache(spell, heal, crit) end -- Gather Data by User Actions + +local _cached_pfui_uf_mouseover -- will get cached upon first use in one of the hooks below + hooksecurefunc("CastSpell", function(id, bookType) if not libpredict.sender.enabled then return end - local effect, rank = libspell.GetSpellInfo(id, bookType) - if not effect then return end - spell_queue[1] = effect - spell_queue[2] = effect.. ( rank or "" ) - spell_queue[3] = UnitName("target") and UnitCanAssist("player", "target") and UnitName("target") or UnitName("player") + local effectRaw, rank = libspell.GetSpellInfo(id, bookType) + if not effectRaw then return end + + _cached_pfui_uf_mouseover = _cached_pfui_uf_mouseover or pfUI.uf.mouseover or {} + local pfui_uf_mouseover_unit = _cached_pfui_uf_mouseover.unit + + spell_queue[1] = effectRaw + spell_queue[2] = effectRaw .. (rank or "") + spell_queue[3] = pfui_uf_mouseover_unit == "player" + and player + or ( + (pfui_uf_mouseover_unit and UnitCanAssist("player", pfui_uf_mouseover_unit)) + and UnitName(pfui_uf_mouseover_unit) -- mouseover unit name + or (UnitCanAssist("player", "target") and UnitName("target") or player) -- or default + ) end) - + hooksecurefunc("CastSpellByName", function(effect, target) if not libpredict.sender.enabled then return end - local effect, rank = libspell.GetSpellInfo(effect) - if not effect then return end - local mouseover = pfUI and pfUI.uf and pfUI.uf.mouseover and pfUI.uf.mouseover.unit - mouseover = mouseover and UnitCanAssist("player", mouseover) and UnitName(mouseover) + local effectRaw, rank = libspell.GetSpellInfo(effect) + if not effectRaw then return end - local default = UnitName("target") and UnitCanAssist("player", "target") and UnitName("target") or UnitName("player") + _cached_pfui_uf_mouseover = _cached_pfui_uf_mouseover or pfUI.uf.mouseover or {} + local pfui_uf_mouseover_unit = _cached_pfui_uf_mouseover.unit target = target and type(target) == "string" and UnitName(target) or target target = target and target == true and UnitName("player") or target target = target and target == 1 and UnitName("player") or target - spell_queue[1] = effect - spell_queue[2] = effect.. ( rank or "" ) - spell_queue[3] = target or mouseover or default + spell_queue[1] = effectRaw + spell_queue[2] = effectRaw .. (rank or "") + spell_queue[3] = target + or pfui_uf_mouseover_unit == "player" and player + or ( + pfui_uf_mouseover_unit + and UnitCanAssist("player", pfui_uf_mouseover_unit) + and UnitName(pfui_uf_mouseover_unit) -- mouseover unit name + ) + or ( + UnitCanAssist("player", "target") + and UnitName("target") + ) + or player -- or default end) local scanner = libtipscan:GetScanner("prediction") hooksecurefunc("UseAction", function(slot, target, selfcast) if not libpredict.sender.enabled then return end if GetActionText(slot) or not IsCurrentAction(slot) then return end + scanner:SetAction(slot) local effect, rank = scanner:Line(1) if not effect then return end + + _cached_pfui_uf_mouseover = _cached_pfui_uf_mouseover or pfUI.uf.mouseover or {} + local pfui_uf_mouseover_unit = _cached_pfui_uf_mouseover.unit + spell_queue[1] = effect - spell_queue[2] = effect.. ( rank or "" ) - spell_queue[3] = selfcast and UnitName("player") or UnitName("target") and UnitCanAssist("player", "target") and UnitName("target") or UnitName("player") + spell_queue[2] = effect .. (rank or "") + spell_queue[3] = (selfcast or pfui_uf_mouseover_unit == "player") + and player + or ( + (pfui_uf_mouseover_unit and UnitCanAssist("player", pfui_uf_mouseover_unit)) + and UnitName(pfui_uf_mouseover_unit) -- mouseover unit name + or (UnitCanAssist("player", "target") and UnitName("target") or player) -- or default + ) end) libpredict.sender = CreateFrame("Frame", "pfPredictionSender", UIParent) diff --git a/libs/librange.lua b/libs/librange.lua index c6e9c1a2..2bea77a4 100644 --- a/libs/librange.lua +++ b/libs/librange.lua @@ -147,12 +147,13 @@ local target_event = TargetFrame_OnEvent local target_nop = function() return end librange:SetScript("OnUpdate", function() - if ( this.tick or 1) > GetTime() then + local now = GetTime() + if (this.tick or 1) > now then return - else - this.tick = GetTime() + this.interval end + this.tick = now + this.interval + -- skip invalid units while not this:NeedRangeScan(units[this.id]) and this.id <= numunits do this.id = this.id + 1 diff --git a/modules/actionbar.lua b/modules/actionbar.lua index eabebc55..9d820587 100644 --- a/modules/actionbar.lua +++ b/modules/actionbar.lua @@ -808,7 +808,12 @@ pfUI:RegisterModule("actionbar", "vanilla:tbc", function () updatecache[id] = nil end - if ( this.tick or .2) > GetTime() then return else this.tick = GetTime() + .2 end + local now = GetTime() + if (this.tick or .2) > now then + return + end + + this.tick = now + .2 for id, button in pairs(buttoncache) do if button:IsShown() then ButtonRangeUpdate(button) end @@ -1669,7 +1674,13 @@ pfUI:RegisterModule("actionbar", "vanilla:tbc", function () -- queue events to fire only once per second if not this.event then return end - if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + 1 end + + local now = GetTime() + if (this.tick or 1) > now then + return + end + + this.tick = now + 1 -- scan for all reagent item counts for item in pairs(reagent_counts) do diff --git a/modules/addoncompat.lua b/modules/addoncompat.lua index 8068f0a3..124deb0f 100644 --- a/modules/addoncompat.lua +++ b/modules/addoncompat.lua @@ -137,7 +137,12 @@ pfUI:RegisterModule("addoncompat", function () local delay = CreateFrame("Frame") delay:SetScript("OnUpdate", function() -- throttle to to one query per .1 second - if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + .1 end + local now = GetTime() + if (this.tick or 1) > now then + return + end + + this.tick = now + .1 -- make sure the firstrun dialog has finished if pfUI.firstrun and pfUI.firstrun.steps then diff --git a/modules/afkcam.lua b/modules/afkcam.lua index a9d55d0d..250a40db 100644 --- a/modules/afkcam.lua +++ b/modules/afkcam.lua @@ -167,7 +167,12 @@ pfUI:RegisterModule("afkcam", "vanilla:tbc", function () end) delay:SetScript("OnUpdate", function() - if ( this.tick or 0) > GetTime() then return else this.tick = GetTime() + 1 end + local now = GetTime() + if (this.tick or 0) > now then + return + end + + this.tick = now + 1 local name = UnitName("player") local cast = UnitCastingInfo(name) @@ -175,10 +180,10 @@ pfUI:RegisterModule("afkcam", "vanilla:tbc", function () if not this.delay then this.delay = 0 end if cast then - this.delay = GetTime() + 10 + this.delay = now + 10 end - if this.delay < GetTime() then + if this.delay < now then afkcam:start() this:Hide() end diff --git a/modules/autovendor.lua b/modules/autovendor.lua index 57c259ab..929646ec 100644 --- a/modules/autovendor.lua +++ b/modules/autovendor.lua @@ -46,7 +46,12 @@ pfUI:RegisterModule("autovendor", "vanilla:tbc", function () autovendor:SetScript("OnUpdate", function() -- throttle to to one item per .1 second - if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + .1 end + local now = GetTime() + if (this.tick or 1) > now then + return + end + + this.tick = now + .1 -- scan for the next grey item local bag, slot = GetNextGreyItem() diff --git a/modules/bags.lua b/modules/bags.lua index d343d98a..02bd1809 100644 --- a/modules/bags.lua +++ b/modules/bags.lua @@ -100,8 +100,13 @@ pfUI:RegisterModule("bags", "vanilla:tbc", function () pfUI.bag.delay = { UpdateBag = {} } pfUI.bag:SetScript("OnUpdate", function() - -- update delayed ones every 0.1s - if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + .1 end + -- update delayed once every 0.1s + local now = GetTime() + if (this.tick or 1) > now then + return + end + + this.tick = now + .1 if this.delay.RefreshSpells then this.delay.RefreshSpells = nil diff --git a/modules/buff.lua b/modules/buff.lua index f7114416..a5182805 100644 --- a/modules/buff.lua +++ b/modules/buff.lua @@ -17,7 +17,7 @@ pfUI:RegisterModule("buff", "vanilla:tbc", function () else buff.id = buff.gid end - buff.bid = GetPlayerBuff(PLAYER_BUFF_START_ID+buff.id, buff.btype) + buff.bid = pfUI.api.GetPlayerBuffX(buff.id, buff.btype) if not buff.backdrop then CreateBackdrop(buff) @@ -120,9 +120,10 @@ pfUI:RegisterModule("buff", "vanilla:tbc", function () buff.gid = i buff:SetScript("OnUpdate", function() - if not this.next then this.next = GetTime() + .1 end - if this.next > GetTime() then return end - this.next = GetTime() + .1 + local now = GetTime() + if not this.next then this.next = now + .1 end + if this.next > now then return end + this.next = now + .1 local timeleft = 0 local stacks = 0 @@ -211,19 +212,6 @@ pfUI:RegisterModule("buff", "vanilla:tbc", function () return buff end - local function GetNumBuffs() - local mh, mhtime, mhcharge, oh, ohtime, ohcharge = GetWeaponEnchantInfo() - local offset = (mh and 1 or 0) + (oh and 1 or 0) - - for i=1,32 do - local bid, untilCancelled = GetPlayerBuff(PLAYER_BUFF_START_ID+i, "HELPFUL") - if bid < 0 then - return i - 1 + offset - end - end - return 0 + offset - end - pfUI.buff = CreateFrame("Frame", "pfGlobalBuffFrame", UIParent) pfUI.buff:RegisterEvent("PLAYER_AURAS_CHANGED") pfUI.buff:RegisterEvent("UNIT_INVENTORY_CHANGED") diff --git a/modules/buffwatch.lua b/modules/buffwatch.lua index e9109b2a..2a430789 100644 --- a/modules/buffwatch.lua +++ b/modules/buffwatch.lua @@ -76,7 +76,7 @@ pfUI:RegisterModule("buffwatch", "vanilla:tbc", function () local function GetBuffData(unit, id, type, selfdebuff) if unit == "player" then - local bid = GetPlayerBuff(PLAYER_BUFF_START_ID+id, type) + local bid = pfUI.api.GetPlayerBuffX(id, type) local stacks = GetPlayerBuffApplications(bid) local remaining = GetPlayerBuffTimeLeft(bid) local texture = GetPlayerBuffTexture(bid) @@ -115,7 +115,7 @@ pfUI:RegisterModule("buffwatch", "vanilla:tbc", function () DEFAULT_CHAT_FRAME:AddMessage("|cff33ffcc" .. skill .. "|r" .. T["is now blacklisted."]) end elseif this.parent.unit == "player" then - CancelPlayerBuff(GetPlayerBuff(PLAYER_BUFF_START_ID+this.id,this.type)) + CancelPlayerBuff(pfUI.api.GetPlayerBuffX(this.id,this.type)) end end @@ -123,7 +123,7 @@ pfUI:RegisterModule("buffwatch", "vanilla:tbc", function () GameTooltip:SetOwner(this, "NONE") if this.unit == "player" then - GameTooltip:SetPlayerBuff(GetPlayerBuff(PLAYER_BUFF_START_ID+this.id,this.type)) + GameTooltip:SetPlayerBuff(pfUI.api.GetPlayerBuffX(this.id,this.type)) elseif this.type == "HARMFUL" then GameTooltip:SetUnitDebuff(this.unit, this.id) elseif this.type == "HELPFUL" then @@ -144,10 +144,13 @@ pfUI:RegisterModule("buffwatch", "vanilla:tbc", function () end local function StatusBarOnUpdate() - local remaining = this.endtime - GetTime() + local now = GetTime() + local remaining = this.endtime - now + this.bar:SetValue(remaining > 0 and remaining or 0) + if (this.tick or 1) > now then return end - if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + .1 end + this.tick = now + .1 this.time:SetText(remaining > 0 and GetColoredTimeString(remaining) or "") end @@ -255,7 +258,7 @@ pfUI:RegisterModule("buffwatch", "vanilla:tbc", function () table.sort(frame.buffs, frame.buffcmp) -- create a buff bar for each below threshold - local bar = 1 + local bar, now = 1, nil for id, data in pairs(frame.buffs) do if data[1] and ((data[1] ~= 0 and data[1] < frame.threshold) or frame.threshold == -1) -- timeleft checks and data[3] and data[3] ~= "" -- buff has a name @@ -268,7 +271,9 @@ pfUI:RegisterModule("buffwatch", "vanilla:tbc", function () frame.bars[bar].id = data[2] frame.bars[bar].unit = frame.unit frame.bars[bar].type = frame.type - frame.bars[bar].endtime = GetTime() + ( data[1] > 0 and data[1] or -1 ) + + now = now or GetTime() + frame.bars[bar].endtime = now + ( data[1] > 0 and data[1] or -1 ) -- update max duration the cached remaining values is less than -- the real one, indicates a buff renewal @@ -361,7 +366,10 @@ pfUI:RegisterModule("buffwatch", "vanilla:tbc", function () -- Create a new Buff Bar local function BuffBarFrameOnUpdate() - if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + .4 end + local now = GetTime() + if (this.tick or 1) > now then return end + + this.tick = now + .4 RefreshBuffBarFrame(this) this:RefreshPosition() end diff --git a/modules/castbar.lua b/modules/castbar.lua index bb2b071e..04e2b1d1 100644 --- a/modules/castbar.lua +++ b/modules/castbar.lua @@ -102,7 +102,9 @@ pfUI:RegisterModule("castbar", "vanilla:tbc", function () if cast then local duration = endTime - startTime local max = duration / 1000 - local cur = GetTime() - startTime / 1000 + + local now = GetTime() + local cur = now - startTime / 1000 this:SetAlpha(1) @@ -139,7 +141,7 @@ pfUI:RegisterModule("castbar", "vanilla:tbc", function () end if channel then - cur = max + startTime/1000 - GetTime() + cur = max + startTime/1000 - now end cur = cur > max and max or cur diff --git a/modules/chat.lua b/modules/chat.lua index ad09a24d..43d84eb3 100644 --- a/modules/chat.lua +++ b/modules/chat.lua @@ -127,16 +127,16 @@ pfUI:RegisterModule("chat", "vanilla:tbc", function () ["fm"]="%s.%s.%s"}, } - pfUI.chat.URLFuncs = { - ["WWW"] = function(a1,a2,a3) return pfUI.chat:FormatLink(pfUI.chat.URLPattern.WWW.fm,a1,a2,a3) end, - ["PROTOCOL"] = function(a1,a2) return pfUI.chat:FormatLink(pfUI.chat.URLPattern.PROTOCOL.fm,a1,a2) end, - ["EMAIL"] = function(a1,a2,a3,a4) return pfUI.chat:FormatLink(pfUI.chat.URLPattern.EMAIL.fm,a1,a2,a3,a4) end, - ["PORTIP"] = function(a1,a2,a3,a4,a5) return pfUI.chat:FormatLink(pfUI.chat.URLPattern.PORTIP.fm,a1,a2,a3,a4,a5) end, - ["IP"] = function(a1,a2,a3,a4) return pfUI.chat:FormatLink(pfUI.chat.URLPattern.IP.fm,a1,a2,a3,a4) end, - ["SHORTURL"] = function(a1,a2,a3) return pfUI.chat:FormatLink(pfUI.chat.URLPattern.SHORTURL.fm,a1,a2,a3) end, - ["URLIP"] = function(a1,a2,a3,a4) return pfUI.chat:FormatLink(pfUI.chat.URLPattern.URLIP.fm,a1,a2,a3,a4) end, - ["URL"] = function(a1,a2,a3) return pfUI.chat:FormatLink(pfUI.chat.URLPattern.URL.fm,a1,a2,a3) end, - } + pfUI.chat.URLFuncs = (function(urlPatternsSnapshot, pfuiChatSnapshot) + local funcs = {} + for patternName, _ in pairs(urlPatternsSnapshot) do + local patternNameSnapshot = patternName -- must snapshot + funcs[patternNameSnapshot] = function(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) + return pfuiChatSnapshot:FormatLink(urlPatternsSnapshot[patternNameSnapshot].fm, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) + end + end + return funcs + end)(pfUI.chat.URLPattern, pfUI.chat) -- url copy dialog function pfUI.chat:FormatLink(formatter,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) @@ -166,15 +166,10 @@ pfUI:RegisterModule("chat", "vanilla:tbc", function () end function pfUI.chat:HandleLink(text) - local URLPattern = pfUI.chat.URLPattern - text = string.gsub (text, URLPattern.WWW.rx, pfUI.chat.URLFuncs.WWW) - text = string.gsub (text, URLPattern.PROTOCOL.rx, pfUI.chat.URLFuncs.PROTOCOL) - text = string.gsub (text, URLPattern.EMAIL.rx, pfUI.chat.URLFuncs.EMAIL) - text = string.gsub (text, URLPattern.PORTIP.rx, pfUI.chat.URLFuncs.PORTIP) - text = string.gsub (text, URLPattern.IP.rx, pfUI.chat.URLFuncs.IP) - text = string.gsub (text, URLPattern.SHORTURL.rx, pfUI.chat.URLFuncs.SHORTURL) - text = string.gsub (text, URLPattern.URLIP.rx, pfUI.chat.URLFuncs.URLIP) - text = string.gsub (text, URLPattern.URL.rx, pfUI.chat.URLFuncs.URL) + local urlFuncs = pfUI.chat.URLFuncs + for patternName, patternSpecs in pairs(pfUI.chat.URLPattern) do + text = string.gsub(text, patternSpecs.rx, urlFuncs[patternName]) + end return text end diff --git a/modules/cooldown.lua b/modules/cooldown.lua index 0a293940..63177e53 100644 --- a/modules/cooldown.lua +++ b/modules/cooldown.lua @@ -20,16 +20,21 @@ pfUI:RegisterModule("cooldown", "vanilla:tbc", function () end -- only run every 0.1 seconds from here on - if ( this.tick or .1) > GetTime() then return else this.tick = GetTime() + .1 end + local now = GetTime() + if (this.tick or .1) > now then + return + end + + this.tick = now + .1 -- fix own alpha value (should be inherited, but somehow isn't always) if this:GetAlpha() ~= parent:GetAlpha() then this:SetAlpha(parent:GetAlpha()) end - if this.start < GetTime() then + if this.start < now then -- calculating remaining time as it should be - local remaining = this.duration - (GetTime() - this.start) + local remaining = this.duration - (now - this.start) if remaining >= 0 then this.text:SetText(GetColoredTimeString(remaining)) else @@ -39,7 +44,7 @@ pfUI:RegisterModule("cooldown", "vanilla:tbc", function () -- I have absolutely no idea, but it works: -- https://github.com/Stanzilla/WoWUIBugs/issues/47 local time = time() - local startupTime = time - GetTime() + local startupTime = time - GetTime() --better not to use the variable 'now' here -- just a simplification of: ((2^32) - (start * 1000)) / 1000 local cdTime = (2 ^ 32) / 1000 - this.start local cdStartTime = startupTime - cdTime diff --git a/modules/easteregg.lua b/modules/easteregg.lua index 35a76c6d..ec057f2d 100644 --- a/modules/easteregg.lua +++ b/modules/easteregg.lua @@ -115,7 +115,12 @@ pfUI:RegisterModule("easteregg", "vanilla:tbc", function () local r,g,b = this.text:GetTextColor() this.text:SetTextColor(r+(math.random()-.5)/10, g+(math.random()-.5)/10, b+(math.random()-.5)/10,1) - if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + math.random() - .2 end + local now = GetTime() + if (this.tick or 1) > now then + return + end + + this.tick = now + math.random() - .2 local x,y = math.random(1, width), -1*math.random(1,height) diff --git a/modules/energytick.lua b/modules/energytick.lua index 8e966afc..d7570df5 100644 --- a/modules/energytick.lua +++ b/modules/energytick.lua @@ -45,17 +45,20 @@ pfUI:RegisterModule("energytick", "vanilla:tbc", function () end) energytick:SetScript("OnUpdate", function() + local now if this.target then - this.start, this.max = GetTime(), this.target + now = now or GetTime() + this.start, this.max = now, this.target this.target = nil end if not this.start then return end - this.current = GetTime() - this.start + now = now or GetTime() + this.current = now - this.start if this.current > this.max then - this.start, this.max, this.current = GetTime(), 2, 0 + this.start, this.max, this.current = now, 2, 0 end local pos = (C.unitframes.player.pwidth ~= "-1" and C.unitframes.player.pwidth or C.unitframes.player.width) * (this.current / this.max) diff --git a/modules/focus.lua b/modules/focus.lua index a2e24582..2857f4cb 100644 --- a/modules/focus.lua +++ b/modules/focus.lua @@ -77,7 +77,7 @@ function SlashCmdList.PFCASTFOCUS(msg) end end - local func = loadstring(msg or "") + local func = pfUI.api.TryMemoizedFuncLoadstringForSpellCasts(msg) if func then func() else @@ -95,7 +95,7 @@ function SlashCmdList.PFCASTFOCUS(msg) end SLASH_PFSWAPFOCUS1, SLASH_PFSWAPFOCUS2 = '/swapfocus', '/pfswapfocus' -function SlashCmdList.PFSWAPFOCUS(msg) +function SlashCmdList.PFSWAPFOCUS() if not pfUI.uf or not pfUI.uf.focus then return end local oldunit = UnitExists("target") and strlower(UnitName("target")) diff --git a/modules/gui.lua b/modules/gui.lua index 2827caa8..5fc56f1d 100644 --- a/modules/gui.lua +++ b/modules/gui.lua @@ -1350,7 +1350,12 @@ pfUI:RegisterModule("gui", "vanilla:tbc", function () -- info updater local f = CreateFrame("Frame", nil, this) f:SetScript("OnUpdate", function() - if ( this.tick or 0) > GetTime() then return else this.tick = GetTime() + 1 end + local now = GetTime() + if (this.tick or 0) > now then + return + end + + this.tick = now + 1 local parent = this:GetParent() local localversion = tonumber(pfUI.version.major*10000 + pfUI.version.minor*100 + pfUI.version.fix) diff --git a/modules/hunterbar.lua b/modules/hunterbar.lua index af9b99ae..2c90d44d 100644 --- a/modules/hunterbar.lua +++ b/modules/hunterbar.lua @@ -14,8 +14,14 @@ pfUI:RegisterModule("hunterbar", "vanilla", function () pfUI.hunterbar:SetScript("OnEvent", function() if event == "PLAYER_ENTERING_WORLD" then this.event = GetTime() + 3 - elseif this.event and this.event < GetTime() + .2 then - this.event = GetTime() + .1 + return + end + + if this.event then + local now = GetTime() + if this.event < now + .2 then + this.event = now + .1 + end end end) diff --git a/modules/infight.lua b/modules/infight.lua index 95531a8e..b6216370 100644 --- a/modules/infight.lua +++ b/modules/infight.lua @@ -1,7 +1,11 @@ pfUI:RegisterModule("infight", "vanilla:tbc", function () local function OnUpdate() if not this.infight and not this.aggro and not this.health then return end - if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + .1 end + + local now = GetTime() + if (this.tick or 1) > now then return end + + this.tick = now + .1 if not this.fadeValue then this.fadeValue = 1 end if this.fadeValue >= 0.3 then diff --git a/modules/minimap.lua b/modules/minimap.lua index 53bc3374..56baedff 100644 --- a/modules/minimap.lua +++ b/modules/minimap.lua @@ -135,7 +135,14 @@ pfUI:RegisterModule("minimap", "vanilla:tbc", function () pfUI.minimapCoordinates = CreateFrame("Frame", "pfMinimapCoord", pfUI.minimap) pfUI.minimapCoordinates:SetScript("OnUpdate", function() -- update coords every 0.1 seconds - if C.appearance.minimap.coordstext ~= "off" and ( this.tick or .1) > GetTime() then return else this.tick = GetTime() + .1 end + if C.appearance.minimap.coordstext ~= "off" then + local now = GetTime() + if (this.tick or .1) > now then + return + end + + this.tick = now + .1 + end this.posX, this.posY = GetPlayerMapPosition("player") if this.posX ~= 0 and this.posY ~= 0 then diff --git a/modules/mouseover.lua b/modules/mouseover.lua index 918a3976..2a022603 100644 --- a/modules/mouseover.lua +++ b/modules/mouseover.lua @@ -34,7 +34,7 @@ pfUI:RegisterModule("mouseover", "vanilla", function () _G.SLASH_PFCAST1, _G.SLASH_PFCAST2 = "/pfcast", "/pfmouse" function SlashCmdList.PFCAST(msg) local restore_target = true - local func = loadstring(msg or "") + local func = pfUI.api.TryMemoizedFuncLoadstringForSpellCasts(msg or "") local unit = "mouseover" if not UnitExists(unit) then diff --git a/modules/nameplates.lua b/modules/nameplates.lua index d751ec9d..eeafa48e 100644 --- a/modules/nameplates.lua +++ b/modules/nameplates.lua @@ -199,6 +199,7 @@ pfUI:RegisterModule("nameplates", "vanilla:tbc", function () local function PlateCacheDebuffs(self, unitstr, verify) if not self.debuffcache then self.debuffcache = {} end + local now for id = 1, 16 do local effect, _, texture, stacks, _, duration, timeleft @@ -209,8 +210,10 @@ pfUI:RegisterModule("nameplates", "vanilla:tbc", function () end if effect and timeleft and timeleft > 0 then - local start = GetTime() - ( (duration or 0) - ( timeleft or 0) ) - local stop = GetTime() + ( timeleft or 0 ) + now = now or GetTime() + local start = now - ( (duration or 0) - ( timeleft or 0) ) + local stop = now + ( timeleft or 0 ) + self.debuffcache[id] = self.debuffcache[id] or {} self.debuffcache[id].effect = effect self.debuffcache[id].texture = texture @@ -816,6 +819,7 @@ pfUI:RegisterModule("nameplates", "vanilla:tbc", function () end -- update all debuff icons + local now for i = 1, 16 do local effect, rank, texture, stacks, dtype, duration, timeleft @@ -847,7 +851,8 @@ pfUI:RegisterModule("nameplates", "vanilla:tbc", function () if duration and timeleft and debuffdurations then plate.debuffs[index].cd:SetAlpha(0) plate.debuffs[index].cd:Show() - CooldownFrame_SetTimer(plate.debuffs[index].cd, GetTime() + timeleft - duration, duration, 1) + now = now or GetTime() + CooldownFrame_SetTimer(plate.debuffs[index].cd, now + timeleft - duration, duration, 1) end index = index + 1 @@ -956,9 +961,10 @@ pfUI:RegisterModule("nameplates", "vanilla:tbc", function () end -- scan for debuff timeouts + local now = GetTime() if nameplate.debuffcache then - for id, data in pairs(nameplate.debuffcache) do - if ( not data.stop or data.stop < GetTime() ) and not data.empty then + for _, data in pairs(nameplate.debuffcache) do + if ( not data.stop or data.stop < now ) and not data.empty then data.empty = true update = true end @@ -966,14 +972,14 @@ pfUI:RegisterModule("nameplates", "vanilla:tbc", function () end -- use timer based updates - if not nameplate.tick or nameplate.tick < GetTime() then + if not nameplate.tick or nameplate.tick < now then update = true end -- run full updates if required if update then nameplates:OnDataChanged(nameplate) - nameplate.tick = GetTime() + .5 + nameplate.tick = now + .5 end -- target zoom @@ -1049,12 +1055,12 @@ pfUI:RegisterModule("nameplates", "vanilla:tbc", function () local effect = cast or channel local duration = endTime - startTime local max = duration / 1000 - local cur = GetTime() - startTime / 1000 + local cur = now - startTime / 1000 -- invert castbar values while channeling - if channel then cur = max + startTime/1000 - GetTime() end + if channel then cur = max + startTime/1000 - now end - nameplate.castbar:SetMinMaxValues(0, duration/1000) + nameplate.castbar:SetMinMaxValues(0, duration/1000) nameplate.castbar:SetValue(cur) nameplate.castbar.text:SetText(round(cur,1)) if C.nameplates.spellname == "1" then diff --git a/modules/panel.lua b/modules/panel.lua index 755cd079..efbb0df0 100644 --- a/modules/panel.lua +++ b/modules/panel.lua @@ -63,7 +63,12 @@ pfUI:RegisterModule("panel", "vanilla:tbc", function() end end widget:SetScript("OnUpdate",function() - if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + 1 end + local now = GetTime() + if (this.tick or 1) > now then + return + end + + this.tick = now + 1 local h, m = GetGameTime() local noon = "AM" @@ -113,8 +118,9 @@ pfUI:RegisterModule("panel", "vanilla:tbc", function() widget.timerFrame.text:SetFont(font, font_size, "OUTLINE") widget.timerFrame.text:SetAllPoints(widget.timerFrame) widget.timerFrame:SetScript("OnUpdate", function() - if not widget.timerFrame.Snapshot then widget.timerFrame.Snapshot = GetTime() end - widget.timerFrame.curTime = SecondsToTime(floor(GetTime() - widget.timerFrame.Snapshot)) + local now = GetTime() + if not widget.timerFrame.Snapshot then widget.timerFrame.Snapshot = now end + widget.timerFrame.curTime = SecondsToTime(floor(now - widget.timerFrame.Snapshot)) if widget.timerFrame.curTime ~= "" then widget.timerFrame.text:SetText("|c33cccccc" .. widget.timerFrame.curTime) else @@ -145,10 +151,11 @@ pfUI:RegisterModule("panel", "vanilla:tbc", function() end end) widget.combat:SetScript("OnUpdate", function() - if not this.tick then this.tick = GetTime() end - if GetTime() <= this.tick + 1 then return else this.tick = GetTime() end + local now = GetTime() + if not this.tick then this.tick = now end + if now <= this.tick + 1 then return else this.tick = now end if this.combat then - pfUI.panel:OutputPanel("combat", "|cffffaaaa" .. SecondsToTime(ceil(GetTime() - this.combat))) + pfUI.panel:OutputPanel("combat", "|cffffaaaa" .. SecondsToTime(ceil(now - this.combat))) end end) end @@ -195,8 +202,12 @@ pfUI:RegisterModule("panel", "vanilla:tbc", function() end end widget:SetScript("OnUpdate",function() - if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + 1 end + local now = GetTime() + if (this.tick or 1) > now then + return + end + this.tick = now + 1 fps = floor(GetFramerate()) _, _, lag = GetNetStats() @@ -453,7 +464,12 @@ pfUI:RegisterModule("panel", "vanilla:tbc", function() end) widget:SetScript("OnUpdate",function() - if ( this.tick or 60) > GetTime() then return else this.tick = GetTime() + 60 end + local now = GetTime() + if (this.tick or 60) > now then + return + end + + this.tick = now + 60 if GetGuildInfo("player") then GuildRoster() end end) end diff --git a/modules/roll.lua b/modules/roll.lua index 7762774c..398182d6 100644 --- a/modules/roll.lua +++ b/modules/roll.lua @@ -45,13 +45,14 @@ pfUI:RegisterModule("roll", "vanilla:tbc", function () local itemName = GetItemInfo(itemLink) -- delete obsolete tables - if pfUI.roll.cache[itemName] and pfUI.roll.cache[itemName]["TIMESTAMP"] < GetTime() - 60 then + local now = GetTime() + if pfUI.roll.cache[itemName] and pfUI.roll.cache[itemName]["TIMESTAMP"] < now - 60 then pfUI.roll.cache[itemName] = nil end -- initialize itemtable if not pfUI.roll.cache[itemName] then - pfUI.roll.cache[itemName] = { ["GREED"] = {}, ["NEED"] = {}, ["PASS"] = {}, ["TIMESTAMP"] = GetTime() } + pfUI.roll.cache[itemName] = { ["GREED"] = {}, ["NEED"] = {}, ["PASS"] = {}, ["TIMESTAMP"] = now } end -- ignore already listed names diff --git a/modules/superwow.lua b/modules/superwow.lua index af30210d..8dd73960 100644 --- a/modules/superwow.lua +++ b/modules/superwow.lua @@ -36,7 +36,7 @@ pfUI:RegisterModule("superwow", "vanilla", function () QueueFunction(function() local pfCombatText_AddMessage = _G.CombatText_AddMessage _G.CombatText_AddMessage = function(message, a, b, c, d, e, f) - local match, _, hex = string.find(message, ".+ %[(0x.+)%]") + local _, _, hex = string.find(message, ".+ %[(0x.+)%]") if hex and UnitName(hex) then message = string.gsub(message, hex, UnitName(hex)) end @@ -49,7 +49,7 @@ pfUI:RegisterModule("superwow", "vanilla", function () -- Add native mouseover support if SUPERWOW_VERSION and pfUI.uf and pfUI.uf.mouseover then _G.SlashCmdList.PFCAST = function(msg) - local func = loadstring(msg or "") + local func = pfUI.api.TryMemoizedFuncLoadstringForSpellCasts(msg or "") local unit = "mouseover" if not UnitExists(unit) then @@ -90,7 +90,7 @@ pfUI:RegisterModule("superwow", "vanilla", function () local config = pfUI.uf.player.config local mana = config.defcolor == "0" and config.manacolor or pfUI_config.unitframes.manacolor local r, g, b, a = pfUI.api.strsplit(",", mana) - local rawborder, default_border = GetBorderSize("unitframes") + local _, default_border = GetBorderSize("unitframes") local _, class = UnitClass("player") local width = config.pwidth ~= "-1" and config.pwidth or config.width @@ -194,6 +194,34 @@ pfUI:RegisterModule("superwow", "vanilla", function () return guid end + -- optimize the builtin /castfocus and /pfcastfocus slash commands when possible by using superwow + -- to cast directly to the focus-target-unit-guid thus skipping the need for complex target-swapping + local legacy_cast_focus = SlashCmdList.PFCASTFOCUS + function SlashCmdList.PFCASTFOCUS(msg) + local func = pfUI.api.TryMemoizedFuncLoadstringForSpellCasts(msg) --10 caution + if func then --10 caution + legacy_cast_focus(func) + return + end + + if not pfUI.uf.focus.label then --50 + UIErrorsFrame:AddMessage(SPELL_FAILED_BAD_TARGETS, 1, 0, 0) + return + end + + CastSpellByName(msg, pfUI.uf.focus.label) --90 + + --10 if the spellcast is in fact raw lua-function we cant cast by guid we have to fallback + -- to the legacy method which does support func-scriptlets + -- + --50 the superwow-approach requires just the unit-guid-label it doesnt care about the focus.id + -- which is typically dud anyway + -- + --90 by using superwow to cast directly to a unit-guid we completely sidestep the complex mechanics + -- of target-swapping altogether which is the entire point here for vastly improved ui-performance + -- when spamming spells + end + -- extend the builtin /focus slash command local legacyfocus = SlashCmdList.PFFOCUS function SlashCmdList.PFFOCUS(msg) @@ -278,7 +306,7 @@ pfUI:RegisterModule("superwow", "vanilla", function () if arg3 == "START" or arg3 == "CAST" or arg3 == "CHANNEL" then -- human readable argument list local guid = arg1 - local target = arg2 + -- local target = arg2 local event_type = arg3 local spell_id = arg4 local timer = arg5 @@ -308,7 +336,7 @@ pfUI:RegisterModule("superwow", "vanilla", function () if not libcast.db[guid] then libcast.db[guid] = {} end libcast.db[guid].cast = spell libcast.db[guid].rank = nil - libcast.db[guid].start = GetTime() + libcast.db[guid].start = start libcast.db[guid].casttime = timer libcast.db[guid].icon = icon libcast.db[guid].channel = event_type == "CHANNEL" or false diff --git a/modules/totems.lua b/modules/totems.lua index 59eab973..dbf45dbc 100644 --- a/modules/totems.lua +++ b/modules/totems.lua @@ -19,7 +19,10 @@ pfUI:RegisterModule("totems", "vanilla:tbc", function () -- there's no totem event in vanilla using ticks instead local eventemu = CreateFrame("Frame") eventemu:SetScript("OnUpdate", function() - if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + .5 end + local now = GetTime() + if (this.tick or 1) > now then return end + + this.tick = now + .5 totems:RefreshList() end) end diff --git a/modules/xpbar.lua b/modules/xpbar.lua index 7b1ed36f..9cd65b64 100644 --- a/modules/xpbar.lua +++ b/modules/xpbar.lua @@ -41,14 +41,16 @@ local function OnEnter(self) self:SetAlpha(1) if mode == "XP" then + local now = GetTime() + local xp, xpmax, exh = UnitXP("player"), UnitXPMax("player"), GetXPExhaustion() local xp_perc = round(xp / xpmax * 100) local remaining = xpmax - xp local remaining_perc = round(remaining / xpmax * 100) local exh_perc = GetXPExhaustion() and round(GetXPExhaustion() / xpmax * 100) or 0 - local xp_persec = ((xp - data.startxp)/(GetTime() - data.starttime)) + local xp_persec = ((xp - data.startxp)/(now - data.starttime)) local session = UnitXP("player") - data.startxp - local avg_hour = floor(((UnitXP("player") - data.startxp) / (GetTime() - data.starttime)) * 60 * 60) + local avg_hour = floor(((UnitXP("player") - data.startxp) / (now - data.starttime)) * 60 * 60) local time_remaining = xp_persec > 0 and SecondsToTime(remaining/xp_persec) or 0 -- fill gametooltip data @@ -125,7 +127,13 @@ end if self.always then return end if self:GetAlpha() == 0 or MouseIsOver(self) then return end - if ( self.tick or 1) > GetTime() then return else self.tick = GetTime() + .01 end + + local now = GetTime() + if (self.tick or 1) > now then + return + end + + self.tick = now + .01 self:SetAlpha(self:GetAlpha() - .05) end