local dversion = 379 local major, minor = "DetailsFramework-1.0", dversion local DF, oldminor = LibStub:NewLibrary(major, minor) if (not DF) then DetailsFrameworkCanLoad = false return end _G["DetailsFramework"] = DF DetailsFrameworkCanLoad = true local SharedMedia = LibStub:GetLibrary("LibSharedMedia-3.0") local _ local type = type local unpack = unpack local upper = string.upper local string_match = string.match local tinsert = _G.tinsert local abs = _G.abs local tremove = _G.tremove local IS_WOW_PROJECT_MAINLINE = WOW_PROJECT_ID == WOW_PROJECT_MAINLINE local IS_WOW_PROJECT_NOT_MAINLINE = WOW_PROJECT_ID ~= WOW_PROJECT_MAINLINE local UnitPlayerControlled = UnitPlayerControlled local UnitIsTapDenied = UnitIsTapDenied SMALL_NUMBER = 0.000001 ALPHA_BLEND_AMOUNT = 0.8400251 DF.dversion = dversion DF.AuthorInfo = { Name = "Terciob", Discord = "https://discord.gg/AGSzAZX", } function DF:Msg(msg, ...) print("|cFFFFFFAA" .. (self.__name or "FW Msg:") .. "|r ", msg, ...) end local PixelUtil = PixelUtil or DFPixelUtil if (not PixelUtil) then --check if is in classic, TBC, or WotLK wow, if it is, build a replacement for PixelUtil local gameVersion = GetBuildInfo() if (gameVersion:match("%d") == "1" or gameVersion:match("%d") == "2" or gameVersion:match("%d") == "3") then PixelUtil = { SetWidth = function(self, width) self:SetWidth(width) end, SetHeight = function(self, height) self:SetHeight(height) end, SetSize = function(self, width, height) self:SetSize(width, height) end, SetPoint = function(self, ...) self:SetPoint(...) end, } end end function DF:GetDefaultBackdropColor() return 0.1215, 0.1176, 0.1294, 0.8 end function DF.IsDragonflightAndBeyond() return select(4, GetBuildInfo()) >= 100000 end function DF.IsDragonflight() local _, _, _, buildInfo = GetBuildInfo() if (buildInfo < 110000 and buildInfo >= 100000) then return true end end function DF.IsTimewalkWoW() local _, _, _, buildInfo = GetBuildInfo() if (buildInfo < 40000) then return true end end function DF.IsClassicWow() local _, _, _, buildInfo = GetBuildInfo() if (buildInfo < 20000) then return true end end function DF.IsTBCWow() local _, _, _, buildInfo = GetBuildInfo() if (buildInfo < 30000 and buildInfo >= 20000) then return true end end function DF.IsWotLKWow() local _, _, _, buildInfo = GetBuildInfo() if (buildInfo < 40000 and buildInfo >= 30000) then return true end end function DF.IsShadowlandsWow() local _, _, _, buildInfo = GetBuildInfo() if (buildInfo < 100000 and buildInfo >= 90000) then return true end end function DF.GetContainerItemInfo(containerIndex, slotIndex) if (DF.IsDragonflightAndBeyond()) then local itemInfo = C_Container.GetContainerItemInfo(containerIndex, slotIndex) return itemInfo.iconFileID, itemInfo.stackCount, itemInfo.isLocked, itemInfo.quality, itemInfo.isReadable, itemInfo.hasLoot, itemInfo.hyperlink, itemInfo.isFiltered, itemInfo.hasNoValue, itemInfo.itemID, itemInfo.isBound else return GetContainerItemInfo(containerIndex, slotIndex) end end local roleBySpecTextureName = { DruidBalance = "DAMAGER", DruidFeralCombat = "DAMAGER", DruidRestoration = "HEALER", HunterBeastMastery = "DAMAGER", HunterMarksmanship = "DAMAGER", HunterSurvival = "DAMAGER", MageArcane = "DAMAGER", MageFrost = "DAMAGER", MageFire = "DAMAGER", PaladinCombat = "DAMAGER", PaladinHoly = "HEALER", PaladinProtection = "TANK", PriestHoly = "HEALER", PriestDiscipline = "HEALER", PriestShadow = "DAMAGER", RogueAssassination = "DAMAGER", RogueCombat = "DAMAGER", RogueSubtlety = "DAMAGER", ShamanElementalCombat = "DAMAGER", ShamanEnhancement = "DAMAGER", ShamanRestoration = "HEALER", WarlockCurses = "DAMAGER", WarlockDestruction = "DAMAGER", WarlockSummoning = "DAMAGER", WarriorArm = "DAMAGER", WarriorArms = "DAMAGER", WarriorFury = "DAMAGER", WarriorProtection = "TANK", DeathKnightBlood = "TANK", DeathKnightFrost = "DAMAGER", DeathKnightUnholy = "DAMAGER", } --classic, tbc and wotlk role guesser based on the weights of each talent tree function DF:GetRoleByClassicTalentTree() if (not DF.IsTimewalkWoW()) then return "NONE" end --amount of tabs existing local numTabs = GetNumTalentTabs() or 3 --store the background textures for each tab local pointsPerSpec = {} for i = 1, (MAX_TALENT_TABS or 3) do if (i <= numTabs) then --tab information local name, iconTexture, pointsSpent, fileName = GetTalentTabInfo(i) if (name) then tinsert(pointsPerSpec, {name, pointsSpent, fileName}) end end end local MIN_SPECS = 4 --put the spec with more talent point to the top table.sort(pointsPerSpec, function(t1, t2) return t1[2] > t2[2] end) --get the spec with more points spent local spec = pointsPerSpec[1] if (spec and spec [2] >= MIN_SPECS) then local specName = spec[1] local spentPoints = spec[2] local specTexture = spec[3] local role = roleBySpecTextureName[specTexture] return role or "NONE" end return "DAMAGER" end function DF.UnitGroupRolesAssigned(unitId) if (not DF.IsTimewalkWoW()) then --Was function exist check. TBC has function, returns NONE. -Flamanis 5/16/2022 local role = UnitGroupRolesAssigned(unitId) if (role == "NONE" and UnitIsUnit(unitId, "player")) then local specializationIndex = GetSpecialization() or 0 local id, name, description, icon, role, primaryStat = GetSpecializationInfo(specializationIndex) return id and role or "NONE" end return role else --attempt to guess the role by the player spec local classLoc, className = UnitClass(unitId) if (className == "MAGE" or className == "ROGUE" or className == "HUNTER" or className == "WARLOCK") then return "DAMAGER" end if (Details) then --attempt to get the role from Details! Damage Meter local guid = UnitGUID(unitId) if (guid) then local role = Details.cached_roles[guid] if (role) then return role end end end local role = DF:GetRoleByClassicTalentTree() return role end end --return the specialization of the player it self function DF.GetSpecialization() if (GetSpecialization) then return GetSpecialization() end return nil end function DF.GetSpecializationInfoByID(...) if (GetSpecializationInfoByID) then return GetSpecializationInfoByID(...) end return nil end function DF.GetSpecializationInfo(...) if (GetSpecializationInfo) then return GetSpecializationInfo(...) end return nil end function DF.GetSpecializationRole(...) if (GetSpecializationRole) then return GetSpecializationRole(...) end return nil end --build dummy encounter journal functions if they doesn't exists --this is done for compatibility with classic and if in the future EJ_ functions are moved to C_ DF.EncounterJournal = { EJ_GetCurrentInstance = EJ_GetCurrentInstance or function() return nil end, EJ_GetInstanceForMap = EJ_GetInstanceForMap or function() return nil end, EJ_GetInstanceInfo = EJ_GetInstanceInfo or function() return nil end, EJ_SelectInstance = EJ_SelectInstance or function() return nil end, EJ_GetEncounterInfoByIndex = EJ_GetEncounterInfoByIndex or function() return nil end, EJ_GetEncounterInfo = EJ_GetEncounterInfo or function() return nil end, EJ_SelectEncounter = EJ_SelectEncounter or function() return nil end, EJ_GetSectionInfo = EJ_GetSectionInfo or function() return nil end, EJ_GetCreatureInfo = EJ_GetCreatureInfo or function() return nil end, EJ_SetDifficulty = EJ_SetDifficulty or function() return nil end, EJ_GetNumLoot = EJ_GetNumLoot or function() return 0 end, EJ_GetLootInfoByIndex = EJ_GetLootInfoByIndex or function() return nil end, } --will always give a very random name for our widgets local init_counter = math.random(1, 1000000) DF.LabelNameCounter = DF.LabelNameCounter or init_counter DF.PictureNameCounter = DF.PictureNameCounter or init_counter DF.BarNameCounter = DF.BarNameCounter or init_counter DF.DropDownCounter = DF.DropDownCounter or init_counter DF.PanelCounter = DF.PanelCounter or init_counter DF.SimplePanelCounter = DF.SimplePanelCounter or init_counter DF.ButtonCounter = DF.ButtonCounter or init_counter DF.SliderCounter = DF.SliderCounter or init_counter DF.SwitchCounter = DF.SwitchCounter or init_counter DF.SplitBarCounter = DF.SplitBarCounter or init_counter DF.FRAMELEVEL_OVERLAY = 750 DF.FRAMELEVEL_BACKGROUND = 150 DF.FrameWorkVersion = tostring(dversion) function DF:PrintVersion() print("Details! Framework Version:", DF.FrameWorkVersion) end --get the working folder do local path = string.match(debugstack(1, 1, 0), "AddOns\\(.+)fw.lua") if (path) then DF.folder = "Interface\\AddOns\\" .. path else --if not found, try to use the last valid one DF.folder = DF.folder or "" end end DF.debug = false function DF:GetFrameworkFolder() return DF.folder end function DF:SetFrameworkDebugState(state) DF.debug = state end DF.embeds = DF.embeds or {} local embedFunctions = { "RemoveRealName", "table", "BuildDropDownFontList", "SetFontSize", "SetFontFace", "SetFontColor", "GetFontSize", "GetFontFace", "SetFontOutline", "trim", "Msg", "CreateFlashAnimation", "Fade", "NewColor", "IsHtmlColor", "ParseColors", "BuildMenu", "ShowTutorialAlertFrame", "GetNpcIdFromGuid", "ShowFeedbackPanel", "SetAsOptionsPanel", "GetPlayerRole", "GetCharacterTalents", "GetCharacterPvPTalents", "CreateDropDown", "CreateButton", "CreateColorPickButton", "CreateLabel", "CreateBar", "CreatePanel", "CreateFillPanel", "ColorPick", "IconPick", "CreateSimplePanel", "CreateChartPanel", "CreateImage", "CreateScrollBar", "CreateSwitch", "CreateSlider", "CreateSplitBar", "CreateTextEntry", "Create1PxPanel", "CreateFeedbackButton", "CreateOptionsFrame", "NewSpecialLuaEditorEntry", "ShowPromptPanel", "ShowTextPromptPanel", "www_icons", "GetTemplate", "InstallTemplate", "GetFrameworkFolder", "ShowPanicWarning", "SetFrameworkDebugState", "FindHighestParent", "OpenInterfaceProfile", "CreateInCombatTexture", "CreateAnimationHub", "CreateAnimation", "CreateScrollBox", "CreateBorder", "FormatNumber", "IntegerToTimer", "QuickDispatch", "Dispatch", "CommaValue", "RemoveRealmName", "Trim", "CreateGlowOverlay", "CreateAnts", "CreateFrameShake", "RegisterScriptComm", "SendScriptComm", } function DF:Embed(target) for k, v in pairs(embedFunctions) do target[v] = self[v] end self.embeds[target] = true return target end function DF:FadeFrame(frame, t) if (t == 0) then frame.hidden = false frame.faded = false frame.fading_out = false frame.fading_in = false frame:Show() frame:SetAlpha (1) elseif (t == 1) then frame.hidden = true frame.faded = true frame.fading_out = false frame.fading_in = false frame:SetAlpha (0) frame:Hide() end end ------------------------------------------------------------------------------------------------------------ --table DF.table = {} function DF.table.find(t, value) for i = 1, #t do if (t[i] == value) then return i end end end function DF.table.addunique(t, index, value) if (not value) then value = index index = #t + 1 end for i = 1, #t do if (t[i] == value) then return false end end tinsert(t, index, value) return true end function DF.table.reverse(t) local new = {} local index = 1 for i = #t, 1, -1 do new[index] = t[i] index = index + 1 end return new end function DF.table.duplicate(t1, t2) for key, value in pairs(t2) do if (key ~= "__index" and key ~= "__newindex") then --preserve a wowObject passing it to the new table with copying it if (type(value) == "table" and table.GetObjectType and table:GetObjectType()) then t1[key] = value elseif (type(value) == "table") then t1[key] = t1[key] or {} DF.table.copy(t1[key], t2[key]) else t1[key] = value end end end return t1 end --> copy from table2 to table1 overwriting values function DF.table.copy(t1, t2) for key, value in pairs(t2) do if (key ~= "__index" and key ~= "__newindex") then if (type(value) == "table") then t1[key] = t1[key] or {} DF.table.copy(t1[key], t2[key]) else t1[key] = value end end end return t1 end --> copy from table2 to table1 overwriting values but do not copy data that cannot be compressed function DF.table.copytocompress(t1, t2) for key, value in pairs(t2) do if (key ~= "__index" and type(value) ~= "function") then if (type(value) == "table") then if (not value.GetObjectType) then t1[key] = t1[key] or {} DF.table.copytocompress(t1[key], t2[key]) end else t1 [key] = value end end end return t1 end --add the indexes of table2 into table1 function DF.table.append(t1, t2) for i = 1, #t2 do t1[#t1+1] = t2[i] end return t1 end --> copy values that does exist on table2 but not on table1 function DF.table.deploy(t1, t2) for key, value in pairs (t2) do if (type(value) == "table") then t1 [key] = t1 [key] or {} DF.table.deploy(t1 [key], t2 [key]) elseif (t1 [key] == nil) then t1 [key] = value end end return t1 end function DF.table.dump(t, resultString, deep) resultString = resultString or "" deep = deep or 0 local space = "" for i = 1, deep do space = space .. " " end for key, value in pairs(t) do local valueType = type(value) if (type(key) == "function") then key = "#function#" elseif (type(key) == "table") then key = "#table#" end if (type(key) ~= "string" and type(key) ~= "number") then key = "unknown?" end if (valueType == "table") then if (type(key) == "number") then resultString = resultString .. space .. "[" .. key .. "] = |cFFa9ffa9 {|r\n" else resultString = resultString .. space .. "[\"" .. key .. "\"] = |cFFa9ffa9 {|r\n" end resultString = resultString .. DF.table.dump (value, nil, deep+1) resultString = resultString .. space .. "|cFFa9ffa9},|r\n" elseif (valueType == "string") then resultString = resultString .. space .. "[\"" .. key .. "\"] = \"|cFFfff1c1" .. value .. "|r\",\n" elseif (valueType == "number") then resultString = resultString .. space .. "[\"" .. key .. "\"] = |cFFffc1f4" .. value .. "|r,\n" elseif (valueType == "function") then resultString = resultString .. space .. "[\"" .. key .. "\"] = function()end,\n" elseif (valueType == "boolean") then resultString = resultString .. space .. "[\"" .. key .. "\"] = |cFF99d0ff" .. (value and "true" or "false") .. "|r,\n" end end return resultString end --grab a text and split it into lines adding each line to a indexed table function DF:SplitTextInLines(text) local lines = {} local position = 1 local startScope, endScope = text:find("\n", position, true) while (startScope) do if (startScope ~= 1) then tinsert(lines, text:sub(position, startScope-1)) end position = endScope + 1 startScope, endScope = text:find("\n", position, true) end if (position <= #text) then tinsert(lines, text:sub(position)) end return lines end DF.www_icons = { texture = "feedback_sites", wowi = {0, 0.7890625, 0, 37/128}, curse = {0, 0.7890625, 38/123, 79/128}, mmoc = {0, 0.7890625, 80/123, 123/128}, } local symbol_1K, symbol_10K, symbol_1B if (GetLocale() == "koKR") then symbol_1K, symbol_10K, symbol_1B = "천", "만", "억" elseif (GetLocale() == "zhCN") then symbol_1K, symbol_10K, symbol_1B = "千", "万", "亿" elseif (GetLocale() == "zhTW") then symbol_1K, symbol_10K, symbol_1B = "千", "萬", "億" end function DF:GetAsianNumberSymbols() if (GetLocale() == "koKR") then return "천", "만", "억" elseif (GetLocale() == "zhCN") then return "千", "万", "亿" elseif (GetLocale() == "zhTW") then return "千", "萬", "億" else --return korean as default (if the language is western) return "천", "만", "억" end end if (symbol_1K) then function DF.FormatNumber(number) if (number > 99999999) then return format("%.2f", number/100000000) .. symbol_1B elseif (number > 999999) then return format("%.2f", number/10000) .. symbol_10K elseif (number > 99999) then return floor (number/10000) .. symbol_10K elseif (number > 9999) then return format("%.1f", (number/10000)) .. symbol_10K elseif (number > 999) then return format("%.1f", (number/1000)) .. symbol_1K end return format("%.1f", number) end else function DF.FormatNumber (number) if (number > 999999999) then return format("%.2f", number/1000000000) .. "B" elseif (number > 999999) then return format("%.2f", number/1000000) .. "M" elseif (number > 99999) then return floor (number/1000) .. "K" elseif (number > 999) then return format("%.1f", (number/1000)) .. "K" end return floor(number) end end function DF:CommaValue(value) if (not value) then return "0" end value = floor(value) if (value == 0) then return "0" end --source http://richard.warburton.it local left, num, right = string_match (value, '^([^%d]*%d)(%d*)(.-)$') return left .. (num:reverse():gsub ('(%d%d%d)','%1,'):reverse()) .. right end function DF:GroupIterator(callback, ...) if (IsInRaid()) then for i = 1, GetNumGroupMembers() do DF:QuickDispatch(callback, "raid" .. i, ...) end elseif (IsInGroup()) then for i = 1, GetNumGroupMembers() - 1 do DF:QuickDispatch(callback, "party" .. i, ...) end DF:QuickDispatch(callback, "player", ...) else DF:QuickDispatch(callback, "player", ...) end end function DF:IntegerToTimer(value) return "" .. floor(value/60) .. ":" .. format("%02.f", value%60) end function DF:RemoveRealmName(name) return name:gsub(("%-.*"), "") end function DF:RemoveRealName(name) return name:gsub(("%-.*"), "") end function DF:SetFontSize(fontString, ...) local font, _, flags = fontString:GetFont() fontString:SetFont(font, max(...), flags) end function DF:SetFontFace(fontString, fontface) local font = SharedMedia:Fetch("font", fontface, true) if (font) then fontface = font end local _, size, flags = fontString:GetFont() fontString:SetFont(fontface, size, flags) end function DF:SetFontColor (fontString, r, g, b, a) r, g, b, a = DF:ParseColors (r, g, b, a) fontString:SetTextColor (r, g, b, a) end function DF:SetFontShadow (fontString, r, g, b, a, x, y) r, g, b, a = DF:ParseColors (r, g, b, a) fontString:SetShadowColor (r, g, b, a) local offSetX, offSetY = fontString:GetShadowOffset() x = x or offSetX y = y or offSetY fontString:SetShadowOffset (x, y) end function DF:SetFontRotation(fontString, degrees) if (type(degrees) == "number") then if (not fontString.__rotationAnimation) then fontString.__rotationAnimation = DF:CreateAnimationHub(fontString) fontString.__rotationAnimation.rotator = DF:CreateAnimation(fontString.__rotationAnimation, "rotation", 1, 0, 0) fontString.__rotationAnimation.rotator:SetEndDelay(10^8) fontString.__rotationAnimation.rotator:SetSmoothProgress(1) end fontString.__rotationAnimation.rotator:SetDegrees(degrees) fontString.__rotationAnimation:Play() fontString.__rotationAnimation:Pause() end end function DF:AddClassColorToText(text, className) if (type(className) ~= "string") then return DF:RemoveRealName(text) elseif (className == "UNKNOW" or className == "PET") then return DF:RemoveRealName(text) end local color = RAID_CLASS_COLORS[className] if (color) then text = "|c" .. color.colorStr .. DF:RemoveRealName(text) .. "|r" else return DF:RemoveRealName(text) end return text end function DF:GetClassTCoordsAndTexture(class) local l, r, t, b = unpack(CLASS_ICON_TCOORDS[class]) return l, r, t, b, [[Interface\WORLDSTATEFRAME\Icons-Classes]] end function DF:AddClassIconToText(text, playerName, class, useSpec, iconSize) local size = iconSize or 16 local spec if (useSpec) then if (Details) then local GUID = UnitGUID(playerName) if (GUID) then spec = Details.cached_specs[GUID] if (spec) then spec = spec end end end end if (spec) then --if spec is valid, the user has Details! installed local specString = "" local L, R, T, B = unpack(Details.class_specs_coords[spec]) if (L) then specString = "|TInterface\\AddOns\\Details\\images\\spec_icons_normal:" .. size .. ":" .. size .. ":0:0:512:512:" .. (L * 512) .. ":" .. (R * 512) .. ":" .. (T * 512) .. ":" .. (B * 512) .. "|t" return specString .. " " .. text end end if (class) then local classString = "" local L, R, T, B = unpack(Details.class_coords[class]) if (L) then local imageSize = 128 classString = "|TInterface\\AddOns\\Details\\images\\classes_small:" .. size .. ":" .. size .. ":0:0:" .. imageSize .. ":" .. imageSize .. ":" .. (L * imageSize) .. ":" .. (R * imageSize) .. ":" .. (T * imageSize) .. ":" .. (B * imageSize) .. "|t" return classString .. " " .. text end end return text end function DF:GetFontSize(fontString) local _, size = fontString:GetFont() return size end function DF:GetFontFace(fontString) local fontface = fontString:GetFont() return fontface end local ValidOutlines = { ["NONE"] = true, ["MONOCHROME"] = true, ["OUTLINE"] = true, ["THICKOUTLINE"] = true, } function DF:SetFontOutline(fontString, outline) local font, fontSize = fontString:GetFont() if (outline) then if (type(outline) == "string") then outline = outline:upper() end if (ValidOutlines[outline]) then outline = outline elseif (type(outline) == "boolean" and outline) then outline = "OUTLINE" elseif (type(outline) == "boolean" and not outline) then outline = "NONE" elseif (outline == 1) then outline = "OUTLINE" elseif (outline == 2) then outline = "THICKOUTLINE" end end fontString:SetFont(font, fontSize, outline) end function DF:Trim(string) return DF:trim(string) end function DF:trim(string) local from = string:match"^%s*()" return from > #string and "" or string:match(".*%S", from) end --truncated revoming at a maximum of 10 character from the string function DF:TruncateTextSafe(fontString, maxWidth) local text = fontString:GetText() local numIterations = 10 while (fontString:GetStringWidth() > maxWidth) do text = strsub(text, 1, #text-1) fontString:SetText(text) if (#text <= 1) then break end numIterations = numIterations - 1 if (numIterations <= 0) then break end end text = DF:CleanTruncateUTF8String(text) fontString:SetText(text) end function DF:TruncateText(fontString, maxWidth) local text = fontString:GetText() while (fontString:GetStringWidth() > maxWidth) do text = strsub(text, 1, #text - 1) fontString:SetText(text) if (string.len(text) <= 1) then break end end text = DF:CleanTruncateUTF8String(text) fontString:SetText (text) end function DF:CleanTruncateUTF8String(text) if type(text) == "string" and text ~= "" then local b1 = (#text > 0) and strbyte(strsub(text, #text, #text)) or nil local b2 = (#text > 1) and strbyte(strsub(text, #text-1, #text)) or nil local b3 = (#text > 2) and strbyte(strsub(text, #text-2, #text)) or nil if b1 and b1 >= 194 and b1 <= 244 then text = strsub (text, 1, #text - 1) elseif b2 and b2 >= 224 and b2 <= 244 then text = strsub (text, 1, #text - 2) elseif b3 and b3 >= 240 and b3 <= 244 then text = strsub (text, 1, #text - 3) end end return text end --DF:TruncateNumber(number, fractionDigits): truncate the amount of numbers used to show fraction. function DF:TruncateNumber(number, fractionDigits) fractionDigits = fractionDigits or 2 local truncatedNumber = number --local truncatedNumber = format("%." .. fractionDigits .. "f", number) --4x slower than: --http://lua-users.org/wiki/SimpleRound local mult = 10 ^ fractionDigits if (number >= 0) then truncatedNumber = floor(number * mult + 0.5) / mult else truncatedNumber = ceil(number * mult + 0.5) / mult end return truncatedNumber end function DF:GetNpcIdFromGuid(GUID) local npcId = select(6, strsplit("-", GUID )) if (npcId) then return tonumber(npcId) end return 0 end function DF.SortOrder1(t1, t2) return t1[1] > t2[1] end function DF.SortOrder2(t1, t2) return t1[2] > t2[2] end function DF.SortOrder3(t1, t2) return t1[3] > t2[3] end function DF.SortOrder1R(t1, t2) return t1[1] < t2[1] end function DF.SortOrder2R(t1, t2) return t1[2] < t2[2] end function DF.SortOrder3R(t1, t2) return t1[3] < t2[3] end --return a list of spells from the player spellbook function DF:GetSpellBookSpells() local spellNamesInSpellBook = {} local spellIdsInSpellBook = {} for i = 1, GetNumSpellTabs() do local tabName, tabTexture, offset, numSpells, isGuild, offspecId = GetSpellTabInfo(i) if (offspecId == 0 and tabTexture ~= 136830) then --don't add spells found in the General tab offset = offset + 1 local tabEnd = offset + numSpells for j = offset, tabEnd - 1 do local spellType, spellId = GetSpellBookItemInfo(j, "player") if (spellId) then if (spellType ~= "FLYOUT") then local spellName = GetSpellInfo(spellId) if (spellName) then spellNamesInSpellBook[spellName] = true spellIdsInSpellBook[#spellIdsInSpellBook+1] = spellId end else local _, _, numSlots, isKnown = GetFlyoutInfo(spellId) if (isKnown and numSlots > 0) then for k = 1, numSlots do local spellID, overrideSpellID, isKnown = GetFlyoutSlotInfo(spellId, k) if (isKnown) then local spellName = GetSpellInfo(spellID) spellNamesInSpellBook[spellName] = true spellIdsInSpellBook[#spellIdsInSpellBook+1] = spellID end end end end end end end end return spellNamesInSpellBook, spellIdsInSpellBook end ------------------------------------------------------------------------------------------------------------------------ --flash animation local onFinishFlashAnimation = function(self) if (self.showWhenDone) then self.frame:SetAlpha(1) else self.frame:SetAlpha(0) self.frame:Hide() end if (self.onFinishFunc) then self:onFinishFunc(self.frame) end end local stopAnimation_Method = function(self) local FlashAnimation = self.FlashAnimation FlashAnimation:Stop() end local startFlash_Method = function(self, fadeInTime, fadeOutTime, flashDuration, showWhenDone, flashInHoldTime, flashOutHoldTime, loopType) local flashAnimation = self.FlashAnimation local fadeIn = flashAnimation.fadeIn local fadeOut = flashAnimation.fadeOut fadeIn:Stop() fadeOut:Stop() fadeIn:SetDuration(fadeInTime or 1) fadeIn:SetEndDelay(flashInHoldTime or 0) fadeOut:SetDuration(fadeOutTime or 1) fadeOut:SetEndDelay(flashOutHoldTime or 0) flashAnimation.duration = flashDuration flashAnimation.loopTime = flashAnimation:GetDuration() flashAnimation.finishAt = GetTime() + flashDuration flashAnimation.showWhenDone = showWhenDone flashAnimation:SetLooping(loopType or "REPEAT") self:Show() self:SetAlpha(0) flashAnimation:Play() end function DF:CreateFlashAnimation(frame, onFinishFunc, onLoopFunc) local flashAnimation = frame:CreateAnimationGroup() flashAnimation.fadeOut = flashAnimation:CreateAnimation("Alpha") flashAnimation.fadeOut:SetOrder(1) flashAnimation.fadeOut:SetFromAlpha(0) flashAnimation.fadeOut:SetToAlpha(1) flashAnimation.fadeIn = flashAnimation:CreateAnimation("Alpha") flashAnimation.fadeIn:SetOrder(2) flashAnimation.fadeIn:SetFromAlpha(1) flashAnimation.fadeIn:SetToAlpha(0) frame.FlashAnimation = flashAnimation flashAnimation.frame = frame flashAnimation.onFinishFunc = onFinishFunc flashAnimation:SetScript("OnLoop", onLoopFunc) flashAnimation:SetScript("OnFinished", onFinishFlashAnimation) frame.Flash = startFlash_Method frame.Stop = stopAnimation_Method return flashAnimation end ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --anchoring function DF:CheckPoints(point1, point2, point3, point4, point5, object) if (not point1 and not point2) then return "topleft", object.widget:GetParent(), "topleft", 0, 0 end if (type(point1) == "string") then local frameGlobal = _G[point1] if (frameGlobal and type(frameGlobal) == "table" and frameGlobal.GetObjectType) then return DF:CheckPoints(frameGlobal, point2, point3, point4, point5, object) end elseif (type(point2) == "string") then local frameGlobal = _G[point2] if (frameGlobal and type(frameGlobal) == "table" and frameGlobal.GetObjectType) then return DF:CheckPoints(point1, frameGlobal, point3, point4, point5, object) end end if (type(point1) == "string" and type(point2) == "table") then --setpoint("left", frame, _, _, _) if (not point3 or type(point3) == "number") then --setpoint("left", frame, 10, 10) point1, point2, point3, point4, point5 = point1, point2, point1, point3, point4 end elseif (type(point1) == "string" and type(point2) == "number") then --setpoint("topleft", x, y) point1, point2, point3, point4, point5 = point1, object.widget:GetParent(), point1, point2, point3 elseif (type(point1) == "number") then --setpoint(x, y) point1, point2, point3, point4, point5 = "topleft", object.widget:GetParent(), "topleft", point1, point2 elseif (type(point1) == "table") then --setpoint(frame, x, y) point1, point2, point3, point4, point5 = "topleft", point1, "topleft", point2, point3 end if (not point2) then point2 = object.widget:GetParent() elseif (point2.dframework) then point2 = point2.widget end return point1 or "topleft", point2, point3 or "topleft", point4 or 0, point5 or 0 end local anchoringFunctions = { function(frame, anchorTo, offSetX, offSetY) --1 TOP LEFT frame:ClearAllPoints() frame:SetPoint("bottomleft", anchorTo, "topleft", offSetX, offSetY) end, function(frame, anchorTo, offSetX, offSetY) --2 LEFT frame:ClearAllPoints() frame:SetPoint("right", anchorTo, "left", offSetX, offSetY) end, function(frame, anchorTo, offSetX, offSetY) --3 BOTTOM LEFT frame:ClearAllPoints() frame:SetPoint("topleft", anchorTo, "bottomleft", offSetX, offSetY) end, function(frame, anchorTo, offSetX, offSetY) --4 BOTTOM frame:ClearAllPoints() frame:SetPoint("top", anchorTo, "bottom", offSetX, offSetY) end, function(frame, anchorTo, offSetX, offSetY) --5 BOTTOM RIGHT frame:ClearAllPoints() frame:SetPoint("topright", anchorTo, "bottomright", offSetX, offSetY) end, function(frame, anchorTo, offSetX, offSetY) --6 RIGHT frame:ClearAllPoints() frame:SetPoint("left", anchorTo, "right", offSetX, offSetY) end, function(frame, anchorTo, offSetX, offSetY) --7 TOP RIGHT frame:ClearAllPoints() frame:SetPoint("bottomright", anchorTo, "topright", offSetX, offSetY) end, function(frame, anchorTo, offSetX, offSetY) --8 TOP frame:ClearAllPoints() frame:SetPoint("bottom", anchorTo, "top", offSetX, offSetY) end, function(frame, anchorTo, offSetX, offSetY) --9 CENTER frame:ClearAllPoints() frame:SetPoint("center", anchorTo, "center", offSetX, offSetY) end, function(frame, anchorTo, offSetX, offSetY) --10 frame:ClearAllPoints() frame:SetPoint("left", anchorTo, "left", offSetX, offSetY) end, function(frame, anchorTo, offSetX, offSetY) --11 frame:ClearAllPoints() frame:SetPoint("right", anchorTo, "right", offSetX, offSetY) end, function(frame, anchorTo, offSetX, offSetY) --12 frame:ClearAllPoints() frame:SetPoint("top", anchorTo, "top", offSetX, offSetY) end, function(frame, anchorTo, offSetX, offSetY) --13 frame:ClearAllPoints() frame:SetPoint("bottom", anchorTo, "bottom", offSetX, offSetY) end } function DF:SetAnchor(widget, config, anchorTo) anchorTo = anchorTo or widget:GetParent() anchoringFunctions[config.side](widget, anchorTo, config.x, config.y) end ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --colors --add a new color name, the color can be query using DetailsFramework:ParseColors(colorName) function DF:NewColor(colorName, red, green, blue, alpha) assert(type(colorName) == "string", "DetailsFramework:NewColor(): colorName must be a string.") assert(not DF.alias_text_colors[colorName], "DetailsFramework:NewColor(): colorName already exists.") red, green, blue, alpha = DetailsFramework:ParseColors(red, green, blue, alpha) local colorTable = DetailsFramework:FormatColor("table", red, green, blue, alpha) DF.alias_text_colors[colorName] = colorTable return colorTable end local colorTableMixin = { GetColor = function(self) return self.r, self.g, self.b, self.a end, SetColor = function(self, r, g, b, a) r, g, b, a = DF:ParseColors(r, g, b, a) self.r = r or self.r self.g = g or self.g self.b = b or self.b self.a = a or self.a end, IsColorTable = true, } --convert a any format of color to any other format of color function DF:FormatColor(newFormat, r, g, b, a, decimalsAmount) r, g, b, a = DF:ParseColors(r, g, b, a) decimalsAmount = decimalsAmount or 4 r = DF:TruncateNumber(r, decimalsAmount) g = DF:TruncateNumber(g, decimalsAmount) b = DF:TruncateNumber(b, decimalsAmount) a = DF:TruncateNumber(a, decimalsAmount) if (newFormat == "commastring") then return r .. ", " .. g .. ", " .. b .. ", " .. a elseif (newFormat == "tablestring") then return "{" .. r .. ", " .. g .. ", " .. b .. ", " .. a .. "}" elseif (newFormat == "table") then return {r, g, b, a} elseif (newFormat == "tablemembers") then return {["r"] = r, ["g"] = g, ["b"] = b, ["a"] = a} elseif (newFormat == "numbers") then return r, g, b, a elseif (newFormat == "hex") then return format("%.2x%.2x%.2x%.2x", a * 255, r * 255, g * 255, b * 255) end end function DF:CreateColorTable(r, g, b, a) local t = { r = r or 1, g = g or 1, b = b or 1, a = a or 1, } DF:Mixin(t, colorTableMixin) return t end function DF:IsHtmlColor(color) return DF.alias_text_colors[color] end function DF:ParseColors(red, green, blue, alpha) local firstParameter = red --the first value passed is a table? if (type(firstParameter) == "table") then local colorTable = red if (colorTable.IsColorTable) then --using colorTable mixin return colorTable:GetColor() elseif (not colorTable[1] and colorTable.r) then --{["r"] = 1, ["g"] = 1, ["b"] = 1} red, green, blue, alpha = colorTable.r, colorTable.g, colorTable.b, colorTable.a else --{1, .7, .2, 1} red, green, blue, alpha = unpack(colorTable) end --the first value passed is a string? elseif (type(firstParameter) == "string") then local colorString = red --hexadecimal if (string.find(colorString, "#")) then colorString = colorString:gsub("#","") if (string.len(colorString) == 8) then --with alpha red, green, blue, alpha = tonumber("0x" .. colorString:sub(3, 4))/255, tonumber("0x" .. colorString:sub(5, 6))/255, tonumber("0x" .. colorString:sub(7, 8))/255, tonumber("0x" .. colorString:sub(1, 2))/255 else red, green, blue, alpha = tonumber("0x" .. colorString:sub(1, 2))/255, tonumber("0x" .. colorString:sub(3, 4))/255, tonumber("0x" .. colorString:sub(5, 6))/255, 1 end else --name of the color local colorTable = DF.alias_text_colors[colorString] if (colorTable) then red, green, blue, alpha = unpack(colorTable) --string with number separated by comma elseif (colorString:find(",")) then local r, g, b, a = strsplit(",", colorString) red, green, blue, alpha = tonumber(r), tonumber(g), tonumber(b), tonumber(a) else --no color found within the string, return default color red, green, blue, alpha = unpack(DF.alias_text_colors.none) end end end if (not red or type(red) ~= "number") then red = 1 end if (not green) or type(green) ~= "number" then green = 1 end if (not blue or type(blue) ~= "number") then blue = 1 end if (not alpha or type(alpha) ~= "number") then alpha = 1 end return red, green, blue, alpha end ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --> menus --get the text for the widget name and description from the language system local parseWidgetNameAndDesc = function(languageTable, namePhraseId, descPhraseId, widgetName, widgetDesc) local returnedName = widgetName local returnedDesc = widgetDesc if (languageTable) then if (namePhraseId) then returnedName = languageTable[namePhraseId] or namePhraseId end if (descPhraseId) then returnedDesc = languageTable[descPhraseId] or descPhraseId end return returnedName, returnedDesc else return returnedName, returnedDesc end end local formatOptionNameWithColon = function(text, useColon) if (text) then if (useColon) then text = text .. ":" return text else return text end end end local disable_on_combat = {} local getMenuWidgetVolative = function(parent, widgetType, indexTable) local widgetObject if (widgetType == "label") then widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]] if (not widgetObject) then widgetObject = DF:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType], "overlay") tinsert(parent.widget_list, widgetObject) tinsert(parent.widget_list_by_type[widgetType], widgetObject) end indexTable[widgetType] = indexTable[widgetType] + 1 elseif (widgetType == "dropdown") then widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]] if (not widgetObject) then widgetObject = DF:CreateDropDown(parent, function() return {} end, nil, 140, 18, nil, "$parentWidget" .. widgetType .. indexTable[widgetType]) widgetObject.hasLabel = DF:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType] .. "label", "overlay") tinsert(parent.widget_list, widgetObject) tinsert(parent.widget_list_by_type[widgetType], widgetObject) else widgetObject:ClearHooks() widgetObject.hasLabel.text = "" end indexTable[widgetType] = indexTable[widgetType] + 1 elseif (widgetType == "switch") then widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]] if (not widgetObject) then widgetObject = DF:CreateSwitch(parent, nil, true, 20, 20, nil, nil, nil, "$parentWidget" .. widgetType .. indexTable[widgetType]) widgetObject.hasLabel = DF:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType] .. "label", "overlay") tinsert(parent.widget_list, widgetObject) tinsert(parent.widget_list_by_type[widgetType], widgetObject) else widgetObject:ClearHooks() end indexTable[widgetType] = indexTable[widgetType] + 1 elseif (widgetType == "slider") then widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]] if (not widgetObject) then widgetObject = DF:CreateSlider(parent, 140, 20, 1, 2, 1, 1, false, nil, "$parentWidget" .. widgetType .. indexTable[widgetType]) widgetObject.hasLabel = DF:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType] .. "label", "overlay") tinsert(parent.widget_list, widgetObject) tinsert(parent.widget_list_by_type[widgetType], widgetObject) else widgetObject:ClearHooks() end indexTable[widgetType] = indexTable[widgetType] + 1 elseif (widgetType == "color") then widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]] if (not widgetObject) then widgetObject = DF:CreateColorPickButton(parent, "$parentWidget" .. widgetType .. indexTable[widgetType], nil, function()end, 1) widgetObject.hasLabel = DF:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType] .. "label", "overlay") tinsert(parent.widget_list, widgetObject) tinsert(parent.widget_list_by_type[widgetType], widgetObject) else widgetObject:ClearHooks() end indexTable[widgetType] = indexTable[widgetType] + 1 elseif (widgetType == "button") then widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]] if (not widgetObject) then widgetObject = DF:CreateButton(parent, function()end, 120, 18, "", nil, nil, nil, nil, "$parentWidget" .. widgetType .. indexTable[widgetType]) widgetObject.hasLabel = DF:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType] .. "label", "overlay") tinsert(parent.widget_list, widgetObject) tinsert(parent.widget_list_by_type[widgetType], widgetObject) else widgetObject:ClearHooks() end indexTable[widgetType] = indexTable[widgetType] + 1 elseif (widgetType == "textentry") then widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]] if (not widgetObject) then widgetObject = DF:CreateTextEntry(parent, function()end, 120, 18, nil, "$parentWidget" .. widgetType .. indexTable[widgetType]) widgetObject.hasLabel = DF:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType] .. "label", "overlay") tinsert(parent.widget_list, widgetObject) tinsert(parent.widget_list_by_type[widgetType], widgetObject) else widgetObject:ClearHooks() end indexTable[widgetType] = indexTable[widgetType] + 1 end --if the widget is inside the no combat table, remove it for i = 1, #disable_on_combat do if (disable_on_combat[i] == widgetObject) then tremove(disable_on_combat, i) break end end return widgetObject end --volatile menu can be called several times, each time all settings are reset and a new menu is built using the same widgets function DF:BuildMenuVolatile(parent, menuOptions, xOffset, yOffset, height, useColon, textTemplate, dropdownTemplate, switchTemplate, switchIsCheckbox, sliderTemplate, buttonTemplate, valueChangeHook) if (not parent.widget_list) then DF:SetAsOptionsPanel(parent) end DF:ClearOptionsPanel(parent) local currentXOffset = xOffset local currentYOffset = yOffset local maxColumnWidth = 0 local latestInlineWidget local widgetIndexes = { label = 1, dropdown = 1, switch = 1, slider = 1, color = 1, button = 1, textentry = 1, } height = abs((height or parent:GetHeight()) - abs(yOffset) + 20) height = height * -1 --normalize format types for index, widgetTable in ipairs(menuOptions) do if (widgetTable.type == "space") then widgetTable.type = "blank" elseif (widgetTable.type == "dropdown") then widgetTable.type = "select" elseif (widgetTable.type == "switch") then widgetTable.type = "toggle" elseif (widgetTable.type == "slider") then widgetTable.type = "range" elseif (widgetTable.type == "button") then widgetTable.type = "execute" end end --catch some options added in the hash part of the menu table local useBoxFirstOnAllWidgets = menuOptions.always_boxfirst local languageAddonId = menuOptions.language_addonId local languageTable if (languageAddonId) then languageTable = DetailsFramework.Language.GetLanguageTable(languageAddonId) end for index, widgetTable in ipairs(menuOptions) do if (not widgetTable.hidden) then local widgetCreated if (latestInlineWidget) then if (not widgetTable.inline) then latestInlineWidget = nil currentYOffset = currentYOffset - 20 end end local extraPaddingY = 0 if (not widgetTable.novolatile) then --step a line if (widgetTable.type == "blank" or widgetTable.type == "space") then --do nothing elseif (widgetTable.type == "label" or widgetTable.type == "text") then local label = getMenuWidgetVolative(parent, "label", widgetIndexes) widgetCreated = label label.text = (languageTable and languageTable[widgetTable.namePhraseId]) or (widgetTable.get and widgetTable.get() or widgetTable.text) or (widgetTable.namePhraseId) or "" label.color = widgetTable.color if (widgetTable.font) then label.fontface = widgetTable.font end if (widgetTable.text_template or textTemplate) then label:SetTemplate(widgetTable.text_template or textTemplate) else label.fontsize = widgetTable.size or 10 end label._get = widgetTable.get label.widget_type = "label" label:ClearAllPoints() label:SetPoint (currentXOffset, currentYOffset) if (widgetTable.id) then parent.widgetids [widgetTable.id] = label end --dropdowns elseif (widgetTable.type == "select" or widgetTable.type == "dropdown") then assert(widgetTable.get, "DetailsFramework:BuildMenu(): .get not found in the widget table for 'select'") local dropdown = getMenuWidgetVolative(parent, "dropdown", widgetIndexes) widgetCreated = dropdown dropdown:SetFunction(widgetTable.values) dropdown:Refresh() dropdown:Select(widgetTable.get()) dropdown:SetTemplate(dropdownTemplate) dropdown:SetTooltip((languageTable and languageTable[widgetTable.namePhraseId]) or (widgetTable.desc) or (widgetTable.namePhraseId)) dropdown._get = widgetTable.get dropdown.widget_type = "select" dropdown.hasLabel.text = (languageTable and languageTable[widgetTable.namePhraseId]) or formatOptionNameWithColon(widgetTable.name, useColon) or widgetTable.namePhraseId or "" dropdown.hasLabel:SetTemplate(widgetTable.text_template or textTemplate) dropdown:ClearAllPoints() dropdown:SetPoint("left", dropdown.hasLabel, "right", 2) dropdown.hasLabel:ClearAllPoints() dropdown.hasLabel:SetPoint(currentXOffset, currentYOffset) --global callback if (valueChangeHook) then dropdown:SetHook("OnOptionSelected", valueChangeHook) end --hook list (hook list is wiped when getting the widget) if (widgetTable.hooks) then for hookName, hookFunc in pairs(widgetTable.hooks) do dropdown:SetHook(hookName, hookFunc) end end if (widgetTable.id) then parent.widgetids[widgetTable.id] = dropdown end local widgetTotalSize = dropdown.hasLabel.widget:GetStringWidth() + 140 + 4 if (widgetTotalSize > maxColumnWidth) then maxColumnWidth = widgetTotalSize end --switchs elseif (widgetTable.type == "toggle" or widgetTable.type == "switch") then local switch = getMenuWidgetVolative(parent, "switch", widgetIndexes) widgetCreated = switch switch:SetValue(widgetTable.get()) switch:SetTemplate(switchTemplate) switch:SetAsCheckBox() --it's always a checkbox on volatile menu switch:SetTooltip((languageTable and languageTable[widgetTable.namePhraseId]) or (widgetTable.desc) or (widgetTable.namePhraseId)) switch._get = widgetTable.get switch.widget_type = "toggle" switch.OnSwitch = widgetTable.set if (valueChangeHook) then switch:SetHook("OnSwitch", valueChangeHook) end --hook list if (widgetTable.hooks) then for hookName, hookFunc in pairs(widgetTable.hooks) do switch:SetHook(hookName, hookFunc) end end if (widgetTable.width) then switch:SetWidth(widgetTable.width) end if (widgetTable.height) then switch:SetHeight(widgetTable.height) end switch.hasLabel.text = (languageTable and languageTable[widgetTable.namePhraseId]) or formatOptionNameWithColon(widgetTable.name, useColon) or widgetTable.namePhraseId or "" switch.hasLabel:SetTemplate(widgetTable.text_template or textTemplate) switch:ClearAllPoints() switch.hasLabel:ClearAllPoints() if (widgetTable.boxfirst or useBoxFirstOnAllWidgets) then switch:SetPoint (currentXOffset, currentYOffset) switch.hasLabel:SetPoint ("left", switch, "right", 2) local nextWidgetTable = menuOptions[index+1] if (nextWidgetTable) then if (nextWidgetTable.type ~= "blank" and nextWidgetTable.type ~= "breakline" and nextWidgetTable.type ~= "toggle" and nextWidgetTable.type ~= "color") then extraPaddingY = 4 end end else switch.hasLabel:SetPoint (currentXOffset, currentYOffset) switch:SetPoint ("left", switch.hasLabel, "right", 2) end if (widgetTable.id) then parent.widgetids [widgetTable.id] = switch end local widgetTotalSize = switch.hasLabel:GetStringWidth() + 32 if (widgetTotalSize > maxColumnWidth) then maxColumnWidth = widgetTotalSize end --slider elseif (widgetTable.type == "range" or widgetTable.type == "slider") then local slider = getMenuWidgetVolative(parent, "slider", widgetIndexes) widgetCreated = slider if (widgetTable.usedecimals) then slider.slider:SetValueStep(0.01) else slider.slider:SetValueStep(widgetTable.step) end slider.useDecimals = widgetTable.usedecimals slider.slider:SetMinMaxValues(widgetTable.min, widgetTable.max) slider.slider:SetValue(widgetTable.get()) slider.ivalue = slider.slider:GetValue() slider:SetTemplate(sliderTemplate) slider:SetTooltip((languageTable and languageTable[widgetTable.namePhraseId]) or (widgetTable.desc) or (widgetTable.namePhraseId)) slider._get = widgetTable.get slider.widget_type = "range" slider:SetHook("OnValueChange", widgetTable.set) if (valueChangeHook) then slider:SetHook("OnValueChange", valueChangeHook) end if (widgetTable.thumbscale) then slider:SetThumbSize (slider.thumb.originalWidth * widgetTable.thumbscale, nil) else slider:SetThumbSize (slider.thumb.originalWidth * 1.3, nil) end --hook list if (widgetTable.hooks) then for hookName, hookFunc in pairs(widgetTable.hooks) do slider:SetHook(hookName, hookFunc) end end slider.hasLabel.text = (languageTable and languageTable[widgetTable.namePhraseId]) or formatOptionNameWithColon(widgetTable.name, useColon) or widgetTable.namePhraseId or "" slider.hasLabel:SetTemplate(widgetTable.text_template or textTemplate) slider:SetPoint("left", slider.hasLabel, "right", 2) slider.hasLabel:SetPoint(currentXOffset, currentYOffset) if (widgetTable.id) then parent.widgetids[widgetTable.id] = slider end local widgetTotalSize = slider.hasLabel:GetStringWidth() + 146 if (widgetTotalSize > maxColumnWidth) then maxColumnWidth = widgetTotalSize end --color elseif (widgetTable.type == "color" or widgetTable.type == "color") then local colorpick = getMenuWidgetVolative(parent, "color", widgetIndexes) widgetCreated = colorpick colorpick.color_callback = widgetTable.set --callback colorpick:SetTemplate(buttonTemplate) colorpick:SetSize(18, 18) colorpick:SetTooltip((languageTable and languageTable[widgetTable.namePhraseId]) or (widgetTable.desc) or (widgetTable.namePhraseId)) colorpick._get = widgetTable.get colorpick.widget_type = "color" local default_value, g, b, a = widgetTable.get() if (type(default_value) == "table") then colorpick:SetColor(unpack(default_value)) else colorpick:SetColor(default_value, g, b, a) end if (valueChangeHook) then colorpick:SetHook("OnColorChanged", valueChangeHook) end --hook list if (widgetTable.hooks) then for hookName, hookFunc in pairs(widgetTable.hooks) do colorpick:SetHook(hookName, hookFunc) end end local label = colorpick.hasLabel label.text = (languageTable and languageTable[widgetTable.namePhraseId]) or formatOptionNameWithColon(widgetTable.name, useColon) or widgetTable.namePhraseId or "" label:SetTemplate(widgetTable.text_template or textTemplate) if (widgetTable.boxfirst or useBoxFirstOnAllWidgets) then label:SetPoint("left", colorpick, "right", 2) colorpick:SetPoint(currentXOffset, currentYOffset) extraPaddingY = 1 else colorpick:SetPoint("left", label, "right", 2) label:SetPoint(currentXOffset, currentYOffset) end if (widgetTable.id) then parent.widgetids[widgetTable.id] = colorpick end local widgetTotalSize = label:GetStringWidth() + 32 if (widgetTotalSize > maxColumnWidth) then maxColumnWidth = widgetTotalSize end --button elseif (widgetTable.type == "execute" or widgetTable.type == "button") then local button = getMenuWidgetVolative(parent, "button", widgetIndexes) widgetCreated = button button:SetTemplate(buttonTemplate) button:SetSize(widgetTable.width or 120, widgetTable.height or 18) button:SetClickFunction(widgetTable.func, widgetTable.param1, widgetTable.param2) local textTemplate = widgetTable.text_template or textTemplate or DF.font_templates["ORANGE_FONT_TEMPLATE"] button.textcolor = textTemplate.color button.textfont = textTemplate.font button.textsize = textTemplate.size button.text = (languageTable and languageTable[widgetTable.namePhraseId]) or (widgetTable.name) or (widgetTable.namePhraseId) or "" if (widgetTable.inline) then if (latestInlineWidget) then button:SetPoint("left", latestInlineWidget, "right", 2, 0) latestInlineWidget = button else button:SetPoint(currentXOffset, currentYOffset) latestInlineWidget = button end else button:SetPoint(currentXOffset, currentYOffset) end button:SetTooltip((languageTable and languageTable[widgetTable.namePhraseId]) or (widgetTable.desc) or (widgetTable.namePhraseId)) button.widget_type = "execute" --hook list if (widgetTable.hooks) then for hookName, hookFunc in pairs(widgetTable.hooks) do button:SetHook(hookName, hookFunc) end end if (widgetTable.width) then button:SetWidth(widgetTable.width) end if (widgetTable.height) then button:SetHeight(widgetTable.height) end if (widgetTable.id) then parent.widgetids[widgetTable.id] = button end local widgetTotalSize = button:GetWidth() + 4 if (widgetTotalSize > maxColumnWidth) then maxColumnWidth = widgetTotalSize end --textentry elseif (widgetTable.type == "textentry") then local textentry = getMenuWidgetVolative(parent, "textentry", widgetIndexes) widgetCreated = textentry textentry:SetCommitFunction(widgetTable.func or widgetTable.set) textentry:SetTemplate(widgetTable.template or widgetTable.button_template or buttonTemplate) textentry:SetSize(widgetTable.width or 120, widgetTable.height or 18) textentry:SetTooltip((languageTable and languageTable[widgetTable.namePhraseId]) or (widgetTable.desc) or (widgetTable.namePhraseId)) textentry.text = widgetTable.get() textentry._get = widgetTable.get textentry.widget_type = "textentry" textentry:SetHook("OnEnterPressed", widgetTable.func or widgetTable.set) textentry:SetHook("OnEditFocusLost", widgetTable.func or widgetTable.set) textentry.hasLabel.text = (languageTable and languageTable[widgetTable.namePhraseId]) or formatOptionNameWithColon(widgetTable.name, useColon) or widgetTable.namePhraseId or "" textentry.hasLabel:SetTemplate(widgetTable.text_template or textTemplate) textentry:SetPoint("left", textentry.hasLabel, "right", 2) textentry.hasLabel:SetPoint(currentXOffset, currentYOffset) --hook list if (widgetTable.hooks) then for hookName, hookFunc in pairs(widgetTable.hooks) do textentry:SetHook(hookName, hookFunc) end end if (widgetTable.id) then parent.widgetids[widgetTable.id] = textentry end local widgetTotalSize = textentry.hasLabel:GetStringWidth() + 64 if (widgetTotalSize > maxColumnWidth) then maxColumnWidth = widgetTotalSize end end --end loop if (widgetTable.nocombat) then tinsert(disable_on_combat, widgetCreated) end if (not widgetTable.inline) then if (widgetTable.spacement) then currentYOffset = currentYOffset - 30 else currentYOffset = currentYOffset - 20 end end if (extraPaddingY > 0) then currentYOffset = currentYOffset - extraPaddingY end if (widgetTable.type == "breakline" or currentYOffset < height) then currentYOffset = yOffset currentXOffset = currentXOffset + maxColumnWidth + 20 maxColumnWidth = 0 end if widgetCreated then widgetCreated:Show() end end end end DF.RefreshUnsafeOptionsWidgets() end function DF:BuildMenu(parent, menuOptions, xOffset, yOffset, height, useColon, textTemplate, dropdownTemplate, switchTemplate, switchIsCheckbox, sliderTemplate, buttonTemplate, valueChangeHook) if (not parent.widget_list) then DF:SetAsOptionsPanel(parent) end local currentXOffset = xOffset local currentYOffset = yOffset local maxColumnWidth = 0 --how many widgets has been created on this line loop pass local amountLineWidgetCreated = 0 local latestInlineWidget height = abs((height or parent:GetHeight()) - abs(yOffset) + 20) height = height * -1 --normalize format types for index, widgetTable in ipairs(menuOptions) do if (widgetTable.type == "space") then widgetTable.type = "blank" elseif (widgetTable.type == "dropdown") then widgetTable.type = "select" elseif (widgetTable.type == "switch") then widgetTable.type = "toggle" elseif (widgetTable.type == "slider") then widgetTable.type = "range" elseif (widgetTable.type == "button") then widgetTable.type = "execute" end end --catch some options added in the hash part of the menu table local useBoxFirstOnAllWidgets = menuOptions.always_boxfirst local languageAddonId = menuOptions.language_addonId local languageTable if (languageAddonId) then languageTable = DetailsFramework.Language.GetLanguageTable(languageAddonId) end for index, widgetTable in ipairs(menuOptions) do if (not widgetTable.hidden) then local widgetCreated if (latestInlineWidget) then if (not widgetTable.inline) then latestInlineWidget = nil currentYOffset = currentYOffset - 28 end end local extraPaddingY = 0 if (widgetTable.type == "blank") then --do nothing elseif (widgetTable.type == "label" or widgetTable.type == "text") then local label = DF:CreateLabel(parent, "", widgetTable.text_template or textTemplate or widgetTable.size, widgetTable.color, widgetTable.font, nil, "$parentWidget" .. index, "overlay") label._get = widgetTable.get label.widget_type = "label" label:SetPoint(currentXOffset, currentYOffset) if (widgetTable.namePhraseId) then DetailsFramework.Language.RegisterFontString(languageAddonId, label.widget, widgetTable.namePhraseId) else local textToSet = (widgetTable.get and widgetTable.get()) or widgetTable.text or "" label:SetText(textToSet) end --store the widget created into the overall table and the widget by type tinsert(parent.widget_list, label) tinsert(parent.widget_list_by_type.label, label) amountLineWidgetCreated = amountLineWidgetCreated + 1 if (widgetTable.id) then parent.widgetids[widgetTable.id] = label end elseif (widgetTable.type == "select") then assert(widgetTable.get, "DetailsFramework:BuildMenu(): .get not found in the widget table for 'select'") local dropdown = DF:NewDropDown(parent, nil, "$parentWidget" .. index, nil, 140, 18, widgetTable.values, widgetTable.get(), dropdownTemplate) DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, dropdown, "have_tooltip", widgetTable.descPhraseId, widgetTable.desc) dropdown._get = widgetTable.get dropdown.widget_type = "select" local label = DF:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12) DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, widgetTable.namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) dropdown:SetPoint("left", label, "right", 2) label:SetPoint(currentXOffset, currentYOffset) dropdown.hasLabel = label --global callback if (valueChangeHook) then dropdown:SetHook("OnOptionSelected", valueChangeHook) end --hook list if (widgetTable.hooks) then for hookName, hookFunc in pairs(widgetTable.hooks) do dropdown:SetHook(hookName, hookFunc) end end if (widgetTable.id) then parent.widgetids[widgetTable.id] = dropdown end local widgetTotalSize = label.widget:GetStringWidth() + 144 if (widgetTotalSize > maxColumnWidth) then maxColumnWidth = widgetTotalSize end --store the widget created into the overall table and the widget by type tinsert(parent.widget_list, dropdown) tinsert(parent.widget_list_by_type.dropdown, dropdown) widgetCreated = dropdown amountLineWidgetCreated = amountLineWidgetCreated + 1 elseif (widgetTable.type == "toggle") then local switch = DF:NewSwitch(parent, nil, "$parentWidget" .. index, nil, 60, 20, nil, nil, widgetTable.get(), nil, nil, nil, nil, switchTemplate) DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, switch, "have_tooltip", widgetTable.descPhraseId, widgetTable.desc) switch._get = widgetTable.get switch.widget_type = "toggle" switch.OnSwitch = widgetTable.set if (switchIsCheckbox) then switch:SetAsCheckBox() end if (valueChangeHook) then switch:SetHook("OnSwitch", valueChangeHook) end --hook list if (widgetTable.hooks) then for hookName, hookFunc in pairs(widgetTable.hooks) do switch:SetHook(hookName, hookFunc) end end if (widgetTable.width) then switch:SetWidth(widgetTable.width) end if (widgetTable.height) then switch:SetHeight(widgetTable.height) end local label = DF:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12) DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, widgetTable.namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) if (widgetTable.boxfirst or useBoxFirstOnAllWidgets) then switch:SetPoint(currentXOffset, currentYOffset) label:SetPoint("left", switch, "right", 2) local nextWidgetTable = menuOptions[index+1] if (nextWidgetTable) then if (nextWidgetTable.type ~= "blank" and nextWidgetTable.type ~= "breakline" and nextWidgetTable.type ~= "toggle" and nextWidgetTable.type ~= "color") then extraPaddingY = 4 end end else label:SetPoint(currentXOffset, currentYOffset) switch:SetPoint("left", label, "right", 2, 0) end switch.hasLabel = label if (widgetTable.id) then parent.widgetids[widgetTable.id] = switch end local widgetTotalSize = label.widget:GetStringWidth() + 32 if (widgetTotalSize > maxColumnWidth) then maxColumnWidth = widgetTotalSize end --store the widget created into the overall table and the widget by type tinsert(parent.widget_list, switch) tinsert(parent.widget_list_by_type.switch, switch) widgetCreated = switch amountLineWidgetCreated = amountLineWidgetCreated + 1 elseif (widgetTable.type == "range") then assert(widgetTable.get, "DetailsFramework:BuildMenu(): .get not found in the widget table for 'range'") local isDecimanls = widgetTable.usedecimals local slider = DF:NewSlider(parent, nil, "$parentWidget" .. index, nil, 140, 20, widgetTable.min, widgetTable.max, widgetTable.step, widgetTable.get(), isDecimanls, nil, nil, sliderTemplate) DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, slider, "have_tooltip", widgetTable.descPhraseId, widgetTable.desc) slider._get = widgetTable.get slider.widget_type = "range" slider:SetHook("OnValueChange", widgetTable.set) if (widgetTable.thumbscale) then slider:SetThumbSize(slider.thumb:GetWidth() * widgetTable.thumbscale, nil) else slider:SetThumbSize(slider.thumb:GetWidth() * 1.3, nil) end if (valueChangeHook) then slider:SetHook("OnValueChange", valueChangeHook) end --hook list if (widgetTable.hooks) then for hookName, hookFunc in pairs(widgetTable.hooks) do slider:SetHook(hookName, hookFunc) end end local label = DF:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12) DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, widgetTable.namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) slider:SetPoint("left", label, "right", 2) label:SetPoint(currentXOffset, currentYOffset) slider.hasLabel = label if (widgetTable.id) then parent.widgetids[widgetTable.id] = slider end local widgetTotalSize = label.widget:GetStringWidth() + 146 if (widgetTotalSize > maxColumnWidth) then maxColumnWidth = widgetTotalSize end --store the widget created into the overall table and the widget by type tinsert(parent.widget_list, slider) tinsert(parent.widget_list_by_type.slider, slider) widgetCreated = slider amountLineWidgetCreated = amountLineWidgetCreated + 1 elseif (widgetTable.type == "color") then assert(widgetTable.get, "DetailsFramework:BuildMenu(): .get not found in the widget table for 'color'") local colorpick = DF:NewColorPickButton(parent, "$parentWidget" .. index, nil, widgetTable.set, nil, buttonTemplate) DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, colorpick, "have_tooltip", widgetTable.descPhraseId, widgetTable.desc) colorpick._get = widgetTable.get colorpick.widget_type = "color" colorpick:SetSize(18, 18) local r, g, b, a = DF:ParseColors(widgetTable.get()) colorpick:SetColor(r, g, b, a) if (valueChangeHook) then colorpick:SetHook("OnColorChanged", valueChangeHook) end --hook list if (widgetTable.hooks) then for hookName, hookFunc in pairs(widgetTable.hooks) do colorpick:SetHook(hookName, hookFunc) end end local label = DF:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12) DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, widgetTable.namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) if (widgetTable.boxfirst or useBoxFirstOnAllWidgets) then label:SetPoint("left", colorpick, "right", 2) colorpick:SetPoint(currentXOffset, currentYOffset) extraPaddingY = 1 else colorpick:SetPoint("left", label, "right", 2) label:SetPoint(currentXOffset, currentYOffset) end colorpick.hasLabel = label if (widgetTable.id) then parent.widgetids[widgetTable.id] = colorpick end local widgetTotalSize = label.widget:GetStringWidth() + 32 if (widgetTotalSize > maxColumnWidth) then maxColumnWidth = widgetTotalSize end --store the widget created into the overall table and the widget by type tinsert(parent.widget_list, colorpick) tinsert(parent.widget_list_by_type.color, colorpick) widgetCreated = colorpick amountLineWidgetCreated = amountLineWidgetCreated + 1 elseif (widgetTable.type == "execute") then local button = DF:NewButton(parent, nil, "$parentWidget" .. index, nil, 120, 18, widgetTable.func, widgetTable.param1, widgetTable.param2, nil, "", nil, buttonTemplate, textTemplate) DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, button.widget, widgetTable.namePhraseId, widgetTable.name) if (not buttonTemplate) then button:InstallCustomTexture() end if (widgetTable.inline) then if (latestInlineWidget) then button:SetPoint("left", latestInlineWidget, "right", 2, 0) latestInlineWidget = button else button:SetPoint(currentXOffset, currentYOffset) latestInlineWidget = button end else button:SetPoint(currentXOffset, currentYOffset) end DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, button, "have_tooltip", widgetTable.descPhraseId, widgetTable.desc) button.widget_type = "execute" --button icon if (widgetTable.icontexture) then button:SetIcon(widgetTable.icontexture, nil, nil, nil, widgetTable.icontexcoords, nil, nil, 2) end --hook list if (widgetTable.hooks) then for hookName, hookFunc in pairs(widgetTable.hooks) do button:SetHook(hookName, hookFunc) end end if (widgetTable.id) then parent.widgetids [widgetTable.id] = button end if (widgetTable.width) then button:SetWidth(widgetTable.width) end if (widgetTable.height) then button:SetHeight(widgetTable.height) end local widgetTotalSize = button:GetWidth() + 4 if (widgetTotalSize > maxColumnWidth) then maxColumnWidth = widgetTotalSize end --store the widget created into the overall table and the widget by type tinsert(parent.widget_list, button) tinsert(parent.widget_list_by_type.button, button) widgetCreated = button amountLineWidgetCreated = amountLineWidgetCreated + 1 elseif (widgetTable.type == "textentry") then local textentry = DF:CreateTextEntry(parent, widgetTable.func or widgetTable.set, 120, 18, nil, "$parentWidget" .. index, nil, buttonTemplate) DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, textentry, "have_tooltip", widgetTable.descPhraseId, widgetTable.desc) textentry.text = widgetTable.get() textentry._get = widgetTable.get textentry.widget_type = "textentry" textentry:SetHook("OnEnterPressed", widgetTable.func or widgetTable.set) textentry:SetHook("OnEditFocusLost", widgetTable.func or widgetTable.set) local label = DF:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12) DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, widgetTable.namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) textentry:SetPoint("left", label, "right", 2) label:SetPoint(currentXOffset, currentYOffset) textentry.hasLabel = label --hook list if (widgetTable.hooks) then for hookName, hookFunc in pairs(widgetTable.hooks) do textentry:SetHook(hookName, hookFunc) end end if (widgetTable.id) then parent.widgetids [widgetTable.id] = textentry end local widgetTotalSize = label.widget:GetStringWidth() + 64 if (widgetTotalSize > maxColumnWidth) then maxColumnWidth = widgetTotalSize end --store the widget created into the overall table and the widget by type tinsert(parent.widget_list, textentry) tinsert(parent.widget_list_by_type.textentry, textentry) widgetCreated = textentry amountLineWidgetCreated = amountLineWidgetCreated + 1 end if (widgetTable.nocombat) then tinsert(disable_on_combat, widgetCreated) end if (not widgetTable.inline) then if (widgetTable.spacement) then currentYOffset = currentYOffset - 30 else currentYOffset = currentYOffset - 20 end end if (extraPaddingY > 0) then currentYOffset = currentYOffset - extraPaddingY end if (widgetTable.type == "breakline" or currentYOffset < height) then currentYOffset = yOffset currentXOffset = currentXOffset + maxColumnWidth + 20 amountLineWidgetCreated = 0 maxColumnWidth = 0 end end end DF.RefreshUnsafeOptionsWidgets() end local lock_notsafe_widgets = function() for _, widget in ipairs(disable_on_combat) do widget:Disable() end end local unlock_notsafe_widgets = function() for _, widget in ipairs(disable_on_combat) do widget:Enable() end end function DF.RefreshUnsafeOptionsWidgets() if (DF.PlayerHasCombatFlag) then lock_notsafe_widgets() else unlock_notsafe_widgets() end end DF.PlayerHasCombatFlag = false local ProtectCombatFrame = CreateFrame("frame") ProtectCombatFrame:RegisterEvent("PLAYER_REGEN_ENABLED") ProtectCombatFrame:RegisterEvent("PLAYER_REGEN_DISABLED") ProtectCombatFrame:RegisterEvent("PLAYER_ENTERING_WORLD") ProtectCombatFrame:SetScript("OnEvent", function(self, event) if (event == "PLAYER_ENTERING_WORLD") then if (InCombatLockdown()) then DF.PlayerHasCombatFlag = true else DF.PlayerHasCombatFlag = false end DF.RefreshUnsafeOptionsWidgets() elseif (event == "PLAYER_REGEN_ENABLED") then DF.PlayerHasCombatFlag = false DF.RefreshUnsafeOptionsWidgets() elseif (event == "PLAYER_REGEN_DISABLED") then DF.PlayerHasCombatFlag = true DF.RefreshUnsafeOptionsWidgets() end end) function DF:CreateInCombatTexture(frame) if (DF.debug and not frame) then error ("Details! Framework: CreateInCombatTexture invalid frame on parameter 1.") end local in_combat_background = DF:CreateImage (frame) in_combat_background:SetColorTexture (.6, 0, 0, .1) in_combat_background:Hide() local in_combat_label = Plater:CreateLabel (frame, "you are in combat", 24, "silver") in_combat_label:SetPoint ("right", in_combat_background, "right", -10, 0) in_combat_label:Hide() frame:RegisterEvent ("PLAYER_REGEN_DISABLED") frame:RegisterEvent ("PLAYER_REGEN_ENABLED") frame:SetScript ("OnEvent", function(self, event) if (event == "PLAYER_REGEN_DISABLED") then in_combat_background:Show() in_combat_label:Show() elseif (event == "PLAYER_REGEN_ENABLED") then in_combat_background:Hide() in_combat_label:Hide() end end) return in_combat_background end ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --> tutorials function DF:ShowTutorialAlertFrame (maintext, desctext, clickfunc) local TutorialAlertFrame = _G.DetailsFrameworkAlertFrame if (not TutorialAlertFrame) then TutorialAlertFrame = CreateFrame ("frame", "DetailsFrameworkAlertFrame", UIParent, "MicroButtonAlertTemplate") TutorialAlertFrame.isFirst = true TutorialAlertFrame:SetPoint ("left", UIParent, "left", -20, 100) TutorialAlertFrame:SetFrameStrata ("TOOLTIP") TutorialAlertFrame:Hide() TutorialAlertFrame:SetScript ("OnMouseUp", function(self) if (self.clickfunc and type (self.clickfunc) == "function") then self.clickfunc() end self:Hide() end) TutorialAlertFrame:Hide() end -- TutorialAlertFrame.label = type (maintext) == "string" and maintext or type (desctext) == "string" and desctext or "" MicroButtonAlert_SetText (TutorialAlertFrame, alert.label) -- TutorialAlertFrame.clickfunc = clickfunc TutorialAlertFrame:Show() end local refresh_options = function(self) for _, widget in ipairs (self.widget_list) do if (widget._get) then if (widget.widget_type == "label") then if (widget._get()) then widget:SetText (widget._get()) end elseif (widget.widget_type == "select") then widget:Select (widget._get()) elseif (widget.widget_type == "toggle" or widget.widget_type == "range") then widget:SetValue (widget._get()) elseif (widget.widget_type == "textentry") then widget:SetText (widget._get()) elseif (widget.widget_type == "color") then local default_value, g, b, a = widget._get() if (type (default_value) == "table") then widget:SetColor (unpack (default_value)) else widget:SetColor (default_value, g, b, a) end end end end end local get_frame_by_id = function(self, id) return self.widgetids [id] end function DF:ClearOptionsPanel(frame) for i = 1, #frame.widget_list do frame.widget_list[i]:Hide() if (frame.widget_list[i].hasLabel) then frame.widget_list[i].hasLabel:SetText("") end end table.wipe(frame.widgetids) end function DF:SetAsOptionsPanel (frame) frame.RefreshOptions = refresh_options frame.widget_list = {} frame.widget_list_by_type = { ["dropdown"] = {}, -- "select" ["switch"] = {}, -- "toggle" ["slider"] = {}, -- "range" ["color"] = {}, -- ["button"] = {}, -- "execute" ["textentry"] = {}, -- ["label"] = {}, --"text" } frame.widgetids = {} frame.GetWidgetById = get_frame_by_id end function DF:CreateOptionsFrame (name, title, template) template = template or 1 if (template == 2) then local options_frame = CreateFrame ("frame", name, UIParent, "ButtonFrameTemplate") tinsert (UISpecialFrames, name) options_frame:SetSize (500, 200) options_frame.RefreshOptions = refresh_options options_frame.widget_list = {} options_frame:SetScript ("OnMouseDown", function(self, button) if (button == "RightButton") then if (self.moving) then self.moving = false self:StopMovingOrSizing() end return options_frame:Hide() elseif (button == "LeftButton" and not self.moving) then self.moving = true self:StartMoving() end end) options_frame:SetScript ("OnMouseUp", function(self) if (self.moving) then self.moving = false self:StopMovingOrSizing() end end) options_frame:SetMovable (true) options_frame:EnableMouse (true) options_frame:SetFrameStrata ("DIALOG") options_frame:SetToplevel (true) options_frame:Hide() options_frame:SetPoint ("center", UIParent, "center") options_frame.TitleText:SetText (title) --10.0 fuck --options_frame.portrait:SetTexture ([[Interface\CHARACTERFRAME\TEMPORARYPORTRAIT-FEMALE-BLOODELF]]) return options_frame elseif (template == 1) then local options_frame = CreateFrame ("frame", name, UIParent) tinsert (UISpecialFrames, name) options_frame:SetSize (500, 200) options_frame.RefreshOptions = refresh_options options_frame.widget_list = {} options_frame:SetScript ("OnMouseDown", function(self, button) if (button == "RightButton") then if (self.moving) then self.moving = false self:StopMovingOrSizing() end return options_frame:Hide() elseif (button == "LeftButton" and not self.moving) then self.moving = true self:StartMoving() end end) options_frame:SetScript ("OnMouseUp", function(self) if (self.moving) then self.moving = false self:StopMovingOrSizing() end end) options_frame:SetMovable (true) options_frame:EnableMouse (true) options_frame:SetFrameStrata ("DIALOG") options_frame:SetToplevel (true) options_frame:Hide() options_frame:SetPoint ("center", UIParent, "center") options_frame:SetBackdrop ({bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16, edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, insets = {left = 1, right = 1, top = 1, bottom = 1}}) options_frame:SetBackdropColor (0, 0, 0, .7) local texturetitle = options_frame:CreateTexture (nil, "artwork") texturetitle:SetTexture ([[Interface\CURSOR\Interact]]) texturetitle:SetTexCoord (0, 1, 0, 1) texturetitle:SetVertexColor (1, 1, 1, 1) texturetitle:SetPoint ("topleft", options_frame, "topleft", 2, -3) texturetitle:SetWidth (36) texturetitle:SetHeight (36) local title = DF:NewLabel (options_frame, nil, "$parentTitle", nil, title, nil, 20, "yellow") title:SetPoint ("left", texturetitle, "right", 2, -1) DF:SetFontOutline (title, true) local c = CreateFrame ("Button", nil, options_frame, "UIPanelCloseButton") c:SetWidth (32) c:SetHeight (32) c:SetPoint ("TOPRIGHT", options_frame, "TOPRIGHT", -3, -3) c:SetFrameLevel (options_frame:GetFrameLevel()+1) return options_frame end end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ --> ~templates --fonts DF.font_templates = DF.font_templates or {} --detect which language is the client and select the font accordingly local clientLanguage = GetLocale() if (clientLanguage == "enGB") then clientLanguage = "enUS" end DF.ClientLanguage = clientLanguage --returns which region the language the client is running, return "western", "russia" or "asia" function DF:GetClientRegion() if (clientLanguage == "zhCN" or clientLanguage == "koKR" or clientLanguage == "zhTW") then return "asia" elseif (clientLanguage == "ruRU") then return "russia" else return "western" end end --return the best font to use for the client language function DF:GetBestFontForLanguage (language, western, cyrillic, china, korean, taiwan) if (not language) then language = DF.ClientLanguage end if (language == "enUS" or language == "deDE" or language == "esES" or language == "esMX" or language == "frFR" or language == "itIT" or language == "ptBR") then return western or "Friz Quadrata TT" elseif (language == "ruRU") then return cyrillic or "Arial Narrow" elseif (language == "zhCN") then return china or "AR CrystalzcuheiGBK Demibold" elseif (language == "koKR") then return korean or "2002" elseif (language == "zhTW") then return taiwan or "AR CrystalzcuheiGBK Demibold" end end --DF.font_templates ["ORANGE_FONT_TEMPLATE"] = {color = "orange", size = 11, font = "Accidental Presidency"} --DF.font_templates ["OPTIONS_FONT_TEMPLATE"] = {color = "yellow", size = 12, font = "Accidental Presidency"} DF.font_templates["ORANGE_FONT_TEMPLATE"] = {color = "orange", size = 10, font = DF:GetBestFontForLanguage()} DF.font_templates["OPTIONS_FONT_TEMPLATE"] = {color = "yellow", size = 9.6, font = DF:GetBestFontForLanguage()} --dropdowns DF.dropdown_templates = DF.dropdown_templates or {} DF.dropdown_templates["OPTIONS_DROPDOWN_TEMPLATE"] = { backdrop = { edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true }, backdropcolor = {1, 1, 1, .7}, backdropbordercolor = {0, 0, 0, 1}, onentercolor = {1, 1, 1, .9}, onenterbordercolor = {1, 1, 1, 1}, dropicon = "Interface\\BUTTONS\\arrow-Down-Down", dropiconsize = {16, 16}, dropiconpoints = {-2, -3}, } DF.dropdown_templates["OPTIONS_DROPDOWNDARK_TEMPLATE"] = { backdrop = { edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true }, backdropcolor = {0.1215, 0.1176, 0.1294, 0.8000}, backdropbordercolor = {.2, .2, .2, 1}, onentercolor = {.5, .5, .5, .9}, onenterbordercolor = {.4, .4, .4, 1}, dropicon = "Interface\\BUTTONS\\arrow-Down-Down", dropiconsize = {16, 16}, dropiconpoints = {-2, -3}, } --switches DF.switch_templates = DF.switch_templates or {} DF.switch_templates["OPTIONS_CHECKBOX_TEMPLATE"] = { backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true}, backdropcolor = {1, 1, 1, .5}, backdropbordercolor = {0, 0, 0, 1}, width = 18, height = 18, enabled_backdropcolor = {1, 1, 1, .5}, disabled_backdropcolor = {1, 1, 1, .2}, onenterbordercolor = {1, 1, 1, 1}, } DF.switch_templates["OPTIONS_CHECKBOX_BRIGHT_TEMPLATE"] = { backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true}, backdropcolor = {1, 1, 1, .5}, backdropbordercolor = {0, 0, 0, 1}, width = 18, height = 18, enabled_backdropcolor = {1, 1, 1, .5}, disabled_backdropcolor = {1, 1, 1, .5}, onenterbordercolor = {1, 1, 1, 1}, } --buttons DF.button_templates = DF.button_templates or {} DF.button_templates["OPTIONS_BUTTON_TEMPLATE"] = { backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true}, backdropcolor = {1, 1, 1, .5}, backdropbordercolor = {0, 0, 0, 1}, } --sliders DF.slider_templates = DF.slider_templates or {} DF.slider_templates["OPTIONS_SLIDER_TEMPLATE"] = { backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true}, backdropcolor = {1, 1, 1, .5}, backdropbordercolor = {0, 0, 0, 1}, onentercolor = {1, 1, 1, .5}, onenterbordercolor = {1, 1, 1, 1}, thumbtexture = [[Interface\Tooltips\UI-Tooltip-Background]], thumbwidth = 16, thumbheight = 14, thumbcolor = {0, 0, 0, 0.5}, } function DF:InstallTemplate (widgetType, templateName, template, parentName) local newTemplate = {} --if has a parent, just copy the parent to the new template if (parentName and type(parentName) == "string") then local parentTemplate = DF:GetTemplate(widgetType, parentName) if (parentTemplate) then DF.table.copy(newTemplate, parentTemplate) end end --copy the template passed into the new template DF.table.copy(newTemplate, template) widgetType = string.lower(widgetType) local templateTable if (widgetType == "font") then templateTable = DF.font_templates local font = template.font if (font) then --fonts passed into the template has default to western --the framework will get the game client language and change the font if needed font = DF:GetBestFontForLanguage(nil, font) end elseif (widgetType == "dropdown") then templateTable = DF.dropdown_templates elseif (widgetType == "button") then templateTable = DF.button_templates elseif (widgetType == "switch") then templateTable = DF.switch_templates elseif (widgetType == "slider") then templateTable = DF.slider_templates end templateTable[templateName] = newTemplate return newTemplate end function DF:GetTemplate (widget_type, template_name) widget_type = string.lower (widget_type) local template_table if (widget_type == "font") then template_table = DF.font_templates elseif (widget_type == "dropdown") then template_table = DF.dropdown_templates elseif (widget_type == "button") then template_table = DF.button_templates elseif (widget_type == "switch") then template_table = DF.switch_templates elseif (widget_type == "slider") then template_table = DF.slider_templates end return template_table [template_name] end function DF.GetParentName (frame) local parentName = frame:GetName() if (not parentName) then error ("Details! FrameWork: called $parent but parent was no name.", 2) end return parentName end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ --> widget scripts and hooks function DF:RunHooksForWidget (event, ...) local hooks = self.HookList [event] if (not hooks) then print (self.widget:GetName(), "no hooks for", event) return end for i, func in ipairs (hooks) do local success, canInterrupt = pcall (func, ...) if (not success) then error ("Details! Framework: " .. event .. " hook for " .. self:GetName() .. ": " .. canInterrupt) elseif (canInterrupt) then return true end end end function DF:SetHook (hookType, func) if (self.HookList [hookType]) then if (type (func) == "function") then local isRemoval = false for i = #self.HookList [hookType], 1, -1 do if (self.HookList [hookType] [i] == func) then tremove (self.HookList [hookType], i) isRemoval = true break end end if (not isRemoval) then tinsert (self.HookList [hookType], func) end else if (DF.debug) then print (debugstack()) error ("Details! Framework: invalid function for widget " .. self.WidgetType .. ".") end end else if (DF.debug) then error ("Details! Framework: unknown hook type for widget " .. self.WidgetType .. ": '" .. hookType .. "'.") end end end function DF:HasHook (hookType, func) if (self.HookList [hookType]) then if (type (func) == "function") then for i = #self.HookList [hookType], 1, -1 do if (self.HookList [hookType] [i] == func) then return true end end end end end function DF:ClearHooks() for hookType, hookTable in pairs(self.HookList) do table.wipe(hookTable) end end function DF:Error (errortext) print ("|cFFFF2222Details! Framework Error|r:", errortext, self.GetName and self:GetName(), self.WidgetType, debugstack (2, 3, 0)) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ --> members DF.GlobalWidgetControlNames = { textentry = "DF_TextEntryMetaFunctions", button = "DF_ButtonMetaFunctions", panel = "DF_PanelMetaFunctions", dropdown = "DF_DropdownMetaFunctions", label = "DF_LabelMetaFunctions", normal_bar = "DF_NormalBarMetaFunctions", image = "DF_ImageMetaFunctions", slider = "DF_SliderMetaFunctions", split_bar = "DF_SplitBarMetaFunctions", aura_tracker = "DF_AuraTracker", healthBar = "DF_healthBarMetaFunctions", timebar = "DF_TimeBarMetaFunctions", } function DF:AddMemberForWidget (widgetName, memberType, memberName, func) if (DF.GlobalWidgetControlNames [widgetName]) then if (type (memberName) == "string" and (memberType == "SET" or memberType == "GET")) then if (func) then local widgetControlObject = _G [DF.GlobalWidgetControlNames [widgetName]] if (memberType == "SET") then widgetControlObject ["SetMembers"] [memberName] = func elseif (memberType == "GET") then widgetControlObject ["GetMembers"] [memberName] = func end else if (DF.debug) then error ("Details! Framework: AddMemberForWidget invalid function.") end end else if (DF.debug) then error ("Details! Framework: AddMemberForWidget unknown memberName or memberType.") end end else if (DF.debug) then error ("Details! Framework: AddMemberForWidget unknown widget type: " .. (widgetName or "") .. ".") end end end ----------------------------- function DF:OpenInterfaceProfile() -- OptionsFrame1/2 should be registered if created with DF:CreateAddOn, so open to them directly if self.OptionsFrame1 then if SettingsPanel then --SettingsPanel:OpenToCategory(self.OptionsFrame1.name) local category = SettingsPanel:GetCategoryList():GetCategory(self.OptionsFrame1.name) if category then SettingsPanel:Open() SettingsPanel:SelectCategory(category) if self.OptionsFrame2 and category:HasSubcategories() then for _, subcategory in pairs(category:GetSubcategories()) do if subcategory:GetName() == self.OptionsFrame2.name then SettingsPanel:SelectCategory(subcategory) break end end end end return elseif InterfaceOptionsFrame_OpenToCategory then InterfaceOptionsFrame_OpenToCategory (self.OptionsFrame1) if self.OptionsFrame2 then InterfaceOptionsFrame_OpenToCategory (self.OptionsFrame2) end return end end -- fallback (broken as of ElvUI Skins in version 12.18+... maybe fix/change will come) InterfaceOptionsFrame_OpenToCategory (self.__name) InterfaceOptionsFrame_OpenToCategory (self.__name) for i = 1, 100 do local button = _G ["InterfaceOptionsFrameAddOnsButton" .. i] if (button) then local text = _G ["InterfaceOptionsFrameAddOnsButton" .. i .. "Text"] if (text) then text = text:GetText() if (text == self.__name) then local toggle = _G ["InterfaceOptionsFrameAddOnsButton" .. i .. "Toggle"] if (toggle) then if (toggle:GetNormalTexture():GetTexture():find ("PlusButton")) then --is minimized, need expand toggle:Click() _G ["InterfaceOptionsFrameAddOnsButton" .. i+1]:Click() elseif (toggle:GetNormalTexture():GetTexture():find ("MinusButton")) then --isn't minimized _G ["InterfaceOptionsFrameAddOnsButton" .. i+1]:Click() end end break end end else self:Msg ("Couldn't not find the profile panel.") break end end end ----------------------------- --safe copy from blizz api function DF:Mixin(object, ...) for i = 1, select("#", ...) do local mixin = select(i, ...) for key, value in pairs(mixin) do object[key] = value end end return object end ----------------------------- --> animations function DF:CreateAnimationHub(parent, onPlay, onFinished) local newAnimation = parent:CreateAnimationGroup() newAnimation:SetScript("OnPlay", onPlay) newAnimation:SetScript("OnFinished", onFinished) newAnimation:SetScript("OnStop", onFinished) newAnimation.NextAnimation = 1 return newAnimation end function DF:CreateAnimation(animation, animationType, order, duration, arg1, arg2, arg3, arg4, arg5, arg6, arg7) local anim = animation:CreateAnimation(animationType) anim:SetOrder(order or animation.NextAnimation) anim:SetDuration(duration) animationType = string.upper(animationType) if (animationType == "ALPHA") then anim:SetFromAlpha(arg1) anim:SetToAlpha(arg2) elseif (animationType == "SCALE") then if (DF.IsDragonflight()) then anim:SetScaleFrom(arg1, arg2) anim:SetScaleTo(arg3, arg4) else anim:SetFromScale(arg1, arg2) anim:SetToScale(arg3, arg4) end anim:SetOrigin(arg5 or "center", arg6 or 0, arg7 or 0) --point, x, y elseif (animationType == "ROTATION") then anim:SetDegrees(arg1) --degree anim:SetOrigin(arg2 or "center", arg3 or 0, arg4 or 0) --point, x, y elseif (animationType == "TRANSLATION") then anim:SetOffset(arg1, arg2) end animation.NextAnimation = animation.NextAnimation + 1 return anim end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ --> frame shakes --> frame shakes rely on OnUpdate scripts, we are using a built-in OnUpdate so is guarantee it'll run local FrameshakeUpdateFrame = DetailsFrameworkFrameshakeControl or CreateFrame ("frame", "DetailsFrameworkFrameshakeControl", UIParent) --> store the frame which has frame shakes registered FrameshakeUpdateFrame.RegisteredFrames = FrameshakeUpdateFrame.RegisteredFrames or {} FrameshakeUpdateFrame.RegisterFrame = function(newFrame) --> add the frame into the registered frames to update DF.table.addunique (FrameshakeUpdateFrame.RegisteredFrames, newFrame) end --forward declared local frameshake_do_update FrameshakeUpdateFrame:SetScript ("OnUpdate", function(self, deltaTime) for i = 1, #FrameshakeUpdateFrame.RegisteredFrames do local parent = FrameshakeUpdateFrame.RegisteredFrames [i] --> check if there's a shake running if (parent.__frameshakes.enabled > 0) then --update all shakes for this frame for i = 1, #parent.__frameshakes do local shakeObject = parent.__frameshakes [i] if (shakeObject.IsPlaying) then frameshake_do_update (parent, shakeObject, deltaTime) end end end end end) local frameshake_shake_finished = function(parent, shakeObject) if (shakeObject.IsPlaying) then shakeObject.IsPlaying = false shakeObject.TimeLeft = 0 shakeObject.IsFadingOut = false shakeObject.IsFadingIn = false --> update the amount of shake running on this frame parent.__frameshakes.enabled = parent.__frameshakes.enabled - 1 --> restore the default anchors, in case where deltaTime was too small that didn't triggered an update for i = 1, #shakeObject.Anchors do local anchor = shakeObject.Anchors [i] --> automatic anchoring and reanching needs to the reviwed in the future if (#anchor == 1) then local anchorTo = unpack (anchor) parent:ClearAllPoints() parent:SetPoint (anchorTo) elseif (#anchor == 2) then local anchorTo, point1 = unpack (anchor) parent:ClearAllPoints() parent:SetPoint (anchorTo, point1) elseif (#anchor == 3) then local anchorTo, point1, point2 = unpack (anchor) parent:SetPoint (anchorTo, point1, point2) elseif (#anchor == 5) then local anchorName1, anchorTo, anchorName2, point1, point2 = unpack (anchor) parent:SetPoint (anchorName1, anchorTo, anchorName2, point1, point2) end end end end --already declared above the update function frameshake_do_update = function(parent, shakeObject, deltaTime) --> check delta time deltaTime = deltaTime or 0 --> update time left shakeObject.TimeLeft = max (shakeObject.TimeLeft - deltaTime, 0) if (shakeObject.TimeLeft > 0) then --> update fade in and out if (shakeObject.IsFadingIn) then shakeObject.IsFadingInTime = shakeObject.IsFadingInTime + deltaTime end if (shakeObject.IsFadingOut) then shakeObject.IsFadingOutTime = shakeObject.IsFadingOutTime + deltaTime end --> check if can disable fade in if (shakeObject.IsFadingIn and shakeObject.IsFadingInTime > shakeObject.FadeInTime) then shakeObject.IsFadingIn = false end --> check if can enable fade out if (not shakeObject.IsFadingOut and shakeObject.TimeLeft < shakeObject.FadeOutTime) then shakeObject.IsFadingOut = true shakeObject.IsFadingOutTime = shakeObject.FadeOutTime - shakeObject.TimeLeft end --> update position local scaleShake = min (shakeObject.IsFadingIn and (shakeObject.IsFadingInTime / shakeObject.FadeInTime) or 1, shakeObject.IsFadingOut and (1 - shakeObject.IsFadingOutTime / shakeObject.FadeOutTime) or 1) if (scaleShake > 0) then --> delate the time by the frequency on both X and Y offsets shakeObject.XSineOffset = shakeObject.XSineOffset + (deltaTime * shakeObject.Frequency) shakeObject.YSineOffset = shakeObject.YSineOffset + (deltaTime * shakeObject.Frequency) --> calc the new position local newX, newY if (shakeObject.AbsoluteSineX) then --absoluting only the sine wave, passing a negative scale will reverse the absolute direction newX = shakeObject.Amplitude * abs (math.sin (shakeObject.XSineOffset)) * scaleShake * shakeObject.ScaleX else newX = shakeObject.Amplitude * math.sin (shakeObject.XSineOffset) * scaleShake * shakeObject.ScaleX end if (shakeObject.AbsoluteSineY) then newY = shakeObject.Amplitude * abs (math.sin (shakeObject.YSineOffset)) * scaleShake * shakeObject.ScaleY else newY = shakeObject.Amplitude * math.sin (shakeObject.YSineOffset) * scaleShake * shakeObject.ScaleY end --> apply the offset to the frame anchors for i = 1, #shakeObject.Anchors do local anchor = shakeObject.Anchors [i] if (#anchor == 1 or #anchor == 3) then local anchorTo, point1, point2 = unpack (anchor) point1 = point1 or 0 point2 = point2 or 0 parent:SetPoint (anchorTo, point1 + newX, point2 + newY) elseif (#anchor == 5) then local anchorName1, anchorTo, anchorName2, point1, point2 = unpack (anchor) --parent:ClearAllPoints() parent:SetPoint (anchorName1, anchorTo, anchorName2, point1 + newX, point2 + newY) end end end else frameshake_shake_finished (parent, shakeObject) end end local frameshake_stop = function(parent, shakeObject) frameshake_shake_finished (parent, shakeObject) end --> scale direction scales the X and Y coordinates, scale strength scales the amplitude and frequency local frameshake_play = function(parent, shakeObject, scaleDirection, scaleAmplitude, scaleFrequency, scaleDuration) --> check if is already playing if (shakeObject.TimeLeft > 0) then --> reset the time left shakeObject.TimeLeft = shakeObject.Duration if (shakeObject.IsFadingOut) then if (shakeObject.FadeInTime > 0) then shakeObject.IsFadingIn = true --> scale the current fade out into fade in, so it starts the fade in at the point where it was fading out shakeObject.IsFadingInTime = shakeObject.FadeInTime * (1 - shakeObject.IsFadingOutTime / shakeObject.FadeOutTime) else shakeObject.IsFadingIn = false shakeObject.IsFadingInTime = 0 end --> disable fade out and enable fade in shakeObject.IsFadingOut = false shakeObject.IsFadingOutTime = 0 end else --> create a new random offset shakeObject.XSineOffset = math.pi * 2 * math.random() shakeObject.YSineOffset = math.pi * 2 * math.random() --> store the initial position if case it needs a reset shakeObject.StartedXSineOffset = shakeObject.XSineOffset shakeObject.StartedYSineOffset = shakeObject.YSineOffset --> check if there's a fade in time if (shakeObject.FadeInTime > 0) then shakeObject.IsFadingIn = true else shakeObject.IsFadingIn = false end shakeObject.IsFadingInTime = 0 shakeObject.IsFadingOut = false shakeObject.IsFadingOutTime = 0 --> apply custom scale shakeObject.ScaleX = (scaleDirection or 1) * shakeObject.OriginalScaleX shakeObject.ScaleY = (scaleDirection or 1) * shakeObject.OriginalScaleY shakeObject.Frequency = (scaleFrequency or 1) * shakeObject.OriginalFrequency shakeObject.Amplitude = (scaleAmplitude or 1) * shakeObject.OriginalAmplitude shakeObject.Duration = (scaleDuration or 1) * shakeObject.OriginalDuration --> update the time left shakeObject.TimeLeft = shakeObject.Duration --> check if is dynamic points if (shakeObject.IsDynamicAnchor) then wipe (shakeObject.Anchors) for i = 1, parent:GetNumPoints() do local p1, p2, p3, p4, p5 = parent:GetPoint (i) shakeObject.Anchors [#shakeObject.Anchors+1] = {p1, p2, p3, p4, p5} end end --> update the amount of shake running on this frame parent.__frameshakes.enabled = parent.__frameshakes.enabled + 1 if (not parent:GetScript ("OnUpdate")) then parent:SetScript ("OnUpdate", function()end) end end shakeObject.IsPlaying = true frameshake_do_update (parent, shakeObject) end local frameshake_set_config = function(parent, shakeObject, duration, amplitude, frequency, absoluteSineX, absoluteSineY, scaleX, scaleY, fadeInTime, fadeOutTime, anchorPoints) shakeObject.Amplitude = amplitude or shakeObject.Amplitude shakeObject.Frequency = frequency or shakeObject.Frequency shakeObject.Duration = duration or shakeObject.Duration shakeObject.FadeInTime = fadeInTime or shakeObject.FadeInTime shakeObject.FadeOutTime = fadeOutTime or shakeObject.FadeOutTime shakeObject.ScaleX = scaleX or shakeObject.ScaleX shakeObject.ScaleY = scaleY or shakeObject.ScaleY if (absoluteSineX ~= nil) then shakeObject.AbsoluteSineX = absoluteSineX end if (absoluteSineY ~= nil) then shakeObject.AbsoluteSineY = absoluteSineY end shakeObject.OriginalScaleX = shakeObject.ScaleX shakeObject.OriginalScaleY = shakeObject.ScaleY shakeObject.OriginalFrequency = shakeObject.Frequency shakeObject.OriginalAmplitude = shakeObject.Amplitude shakeObject.OriginalDuration = shakeObject.Duration end function DF:CreateFrameShake (parent, duration, amplitude, frequency, absoluteSineX, absoluteSineY, scaleX, scaleY, fadeInTime, fadeOutTime, anchorPoints) --> create the shake table local frameShake = { Amplitude = amplitude or 2, Frequency = frequency or 5, Duration = duration or 0.3, FadeInTime = fadeInTime or 0.01, FadeOutTime = fadeOutTime or 0.01, ScaleX = scaleX or 0.2, ScaleY = scaleY or 1, AbsoluteSineX = absoluteSineX, AbsoluteSineY = absoluteSineY, -- IsPlaying = false, TimeLeft = 0, } frameShake.OriginalScaleX = frameShake.ScaleX frameShake.OriginalScaleY = frameShake.ScaleY frameShake.OriginalFrequency = frameShake.Frequency frameShake.OriginalAmplitude = frameShake.Amplitude frameShake.OriginalDuration = frameShake.Duration if (type (anchorPoints) ~= "table") then frameShake.IsDynamicAnchor = true frameShake.Anchors = {} else frameShake.Anchors = anchorPoints end --> inject frame shake table into the frame if (not parent.__frameshakes) then parent.__frameshakes = { enabled = 0, } parent.PlayFrameShake = frameshake_play parent.StopFrameShake = frameshake_stop parent.UpdateFrameShake = frameshake_do_update parent.SetFrameShakeSettings = frameshake_set_config --> register the frame within the frame shake updater FrameshakeUpdateFrame.RegisterFrame (parent) end tinsert (parent.__frameshakes, frameShake) return frameShake end ----------------------------- --> glow overlay local glow_overlay_play = function(self) if (not self:IsShown()) then self:Show() end if (self.animOut:IsPlaying()) then self.animOut:Stop() end if (not self.animIn:IsPlaying()) then self.animIn:Stop() self.animIn:Play() end end local glow_overlay_stop = function(self) if (self.animOut:IsPlaying()) then self.animOut:Stop() end if (self.animIn:IsPlaying()) then self.animIn:Stop() end if (self:IsShown()) then self:Hide() end end local glow_overlay_setcolor = function(self, antsColor, glowColor) if (antsColor) then local r, g, b, a = DF:ParseColors (antsColor) self.ants:SetVertexColor (r, g, b, a) self.AntsColor.r = r self.AntsColor.g = g self.AntsColor.b = b self.AntsColor.a = a end if (glowColor) then local r, g, b, a = DF:ParseColors (glowColor) self.outerGlow:SetVertexColor (r, g, b, a) self.GlowColor.r = r self.GlowColor.g = g self.GlowColor.b = b self.GlowColor.a = a end end local glow_overlay_onshow = function(self) glow_overlay_play (self) end local glow_overlay_onhide = function(self) glow_overlay_stop (self) end --this is most copied from the wow client code, few changes applied to customize it function DF:CreateGlowOverlay (parent, antsColor, glowColor) local glowFrame = CreateFrame ("frame", parent:GetName() and "$parentGlow2" or "OverlayActionGlow" .. math.random (1, 10000000), parent, "ActionBarButtonSpellActivationAlert") glowFrame:HookScript ("OnShow", glow_overlay_onshow) glowFrame:HookScript ("OnHide", glow_overlay_onhide) glowFrame.Play = glow_overlay_play glowFrame.Stop = glow_overlay_stop glowFrame.SetColor = glow_overlay_setcolor glowFrame:Hide() parent.overlay = glowFrame local frameWidth, frameHeight = parent:GetSize() local scale = 1.4 --Make the height/width available before the next frame: parent.overlay:SetSize(frameWidth * scale, frameHeight * scale) parent.overlay:SetPoint("TOPLEFT", parent, "TOPLEFT", -frameWidth * 0.32, frameHeight * 0.36) parent.overlay:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", frameWidth * 0.32, -frameHeight * 0.36) local r, g, b, a = DF:ParseColors (antsColor or defaultColor) glowFrame.ants:SetVertexColor (r, g, b, a) glowFrame.AntsColor = {r, g, b, a} local r, g, b, a = DF:ParseColors (glowColor or defaultColor) glowFrame.outerGlow:SetVertexColor (r, g, b, a) glowFrame.GlowColor = {r, g, b, a} glowFrame.outerGlow:SetScale (1.2) glowFrame:EnableMouse(false) return glowFrame end --> custom glow with ants animation local ants_set_texture_offset = function(self, leftOffset, rightOffset, topOffset, bottomOffset) leftOffset = leftOffset or 0 rightOffset = rightOffset or 0 topOffset = topOffset or 0 bottomOffset = bottomOffset or 0 self:ClearAllPoints() self:SetPoint ("topleft", leftOffset, topOffset) self:SetPoint ("bottomright", rightOffset, bottomOffset) end function DF:CreateAnts (parent, antTable, leftOffset, rightOffset, topOffset, bottomOffset, antTexture) leftOffset = leftOffset or 0 rightOffset = rightOffset or 0 topOffset = topOffset or 0 bottomOffset = bottomOffset or 0 local f = CreateFrame ("frame", nil, parent) f:SetPoint ("topleft", leftOffset, topOffset) f:SetPoint ("bottomright", rightOffset, bottomOffset) f.SetOffset = ants_set_texture_offset local t = f:CreateTexture (nil, "overlay") t:SetAllPoints() t:SetTexture (antTable.Texture) t:SetBlendMode (antTable.BlendMode or "ADD") t:SetVertexColor (DF:ParseColors (antTable.Color or "white")) f.Texture = t f.AntTable = antTable f:SetScript ("OnUpdate", function(self, deltaTime) AnimateTexCoords (t, self.AntTable.TextureWidth, self.AntTable.TextureHeight, self.AntTable.TexturePartsWidth, self.AntTable.TexturePartsHeight, self.AntTable.AmountParts, deltaTime, self.AntTable.Throttle or 0.025) end) return f end --[=[ --test ants do local f = DF:CreateAnts (UIParent) end --]=] ----------------------------- --> borders local default_border_color1 = .5 local default_border_color2 = .3 local default_border_color3 = .1 local SetBorderAlpha = function(self, alpha1, alpha2, alpha3) self.Borders.Alpha1 = alpha1 or self.Borders.Alpha1 self.Borders.Alpha2 = alpha2 or self.Borders.Alpha2 self.Borders.Alpha3 = alpha3 or self.Borders.Alpha3 for _, texture in ipairs (self.Borders.Layer1) do texture:SetAlpha (self.Borders.Alpha1) end for _, texture in ipairs (self.Borders.Layer2) do texture:SetAlpha (self.Borders.Alpha2) end for _, texture in ipairs (self.Borders.Layer3) do texture:SetAlpha (self.Borders.Alpha3) end end local SetBorderColor = function(self, r, g, b) for _, texture in ipairs (self.Borders.Layer1) do texture:SetColorTexture (r, g, b) end for _, texture in ipairs (self.Borders.Layer2) do texture:SetColorTexture (r, g, b) end for _, texture in ipairs (self.Borders.Layer3) do texture:SetColorTexture (r, g, b) end end local SetLayerVisibility = function(self, layer1Shown, layer2Shown, layer3Shown) for _, texture in ipairs (self.Borders.Layer1) do texture:SetShown (layer1Shown) end for _, texture in ipairs (self.Borders.Layer2) do texture:SetShown (layer2Shown) end for _, texture in ipairs (self.Borders.Layer3) do texture:SetShown (layer3Shown) end end function DF:CreateBorder (parent, alpha1, alpha2, alpha3) parent.Borders = { Layer1 = {}, Layer2 = {}, Layer3 = {}, Alpha1 = alpha1 or default_border_color1, Alpha2 = alpha2 or default_border_color2, Alpha3 = alpha3 or default_border_color3, } parent.SetBorderAlpha = SetBorderAlpha parent.SetBorderColor = SetBorderColor parent.SetLayerVisibility = SetLayerVisibility local border1 = parent:CreateTexture (nil, "background") PixelUtil.SetPoint (border1, "topleft", parent, "topleft", -1, 1) PixelUtil.SetPoint (border1, "bottomleft", parent, "bottomleft", -1, -1) border1:SetColorTexture (0, 0, 0, alpha1 or default_border_color1) local border2 = parent:CreateTexture (nil, "background") PixelUtil.SetPoint (border2, "topleft", parent, "topleft", -2, 2) PixelUtil.SetPoint (border2, "bottomleft", parent, "bottomleft", -2, -2) border2:SetColorTexture (0, 0, 0, alpha2 or default_border_color2) local border3 = parent:CreateTexture (nil, "background") PixelUtil.SetPoint (border3, "topleft", parent, "topleft", -3, 3) PixelUtil.SetPoint (border3, "bottomleft", parent, "bottomleft", -3, -3) border3:SetColorTexture (0, 0, 0, alpha3 or default_border_color3) tinsert (parent.Borders.Layer1, border1) tinsert (parent.Borders.Layer2, border2) tinsert (parent.Borders.Layer3, border3) local border1 = parent:CreateTexture (nil, "background") PixelUtil.SetPoint (border1, "topleft", parent, "topleft", 0, 1) PixelUtil.SetPoint (border1, "topright", parent, "topright", 1, 1) border1:SetColorTexture (0, 0, 0, alpha1 or default_border_color1) local border2 = parent:CreateTexture (nil, "background") PixelUtil.SetPoint (border2, "topleft", parent, "topleft", -1, 2) PixelUtil.SetPoint (border2, "topright", parent, "topright", 2, 2) border2:SetColorTexture (0, 0, 0, alpha2 or default_border_color2) local border3 = parent:CreateTexture (nil, "background") PixelUtil.SetPoint (border3, "topleft", parent, "topleft", -2, 3) PixelUtil.SetPoint (border3, "topright", parent, "topright", 3, 3) border3:SetColorTexture (0, 0, 0, alpha3 or default_border_color3) tinsert (parent.Borders.Layer1, border1) tinsert (parent.Borders.Layer2, border2) tinsert (parent.Borders.Layer3, border3) local border1 = parent:CreateTexture (nil, "background") PixelUtil.SetPoint (border1, "topright", parent, "topright", 1, 0) PixelUtil.SetPoint (border1, "bottomright", parent, "bottomright", 1, -1) border1:SetColorTexture (0, 0, 0, alpha1 or default_border_color1) local border2 = parent:CreateTexture (nil, "background") PixelUtil.SetPoint (border2, "topright", parent, "topright", 2, 1) PixelUtil.SetPoint (border2, "bottomright", parent, "bottomright", 2, -2) border2:SetColorTexture (0, 0, 0, alpha2 or default_border_color2) local border3 = parent:CreateTexture (nil, "background") PixelUtil.SetPoint (border3, "topright", parent, "topright", 3, 2) PixelUtil.SetPoint (border3, "bottomright", parent, "bottomright", 3, -3) border3:SetColorTexture (0, 0, 0, alpha3 or default_border_color3) tinsert (parent.Borders.Layer1, border1) tinsert (parent.Borders.Layer2, border2) tinsert (parent.Borders.Layer3, border3) local border1 = parent:CreateTexture (nil, "background") PixelUtil.SetPoint (border1, "bottomleft", parent, "bottomleft", 0, -1) PixelUtil.SetPoint (border1, "bottomright", parent, "bottomright", 0, -1) border1:SetColorTexture (0, 0, 0, alpha1 or default_border_color1) local border2 = parent:CreateTexture (nil, "background") PixelUtil.SetPoint (border2, "bottomleft", parent, "bottomleft", -1, -2) PixelUtil.SetPoint (border2, "bottomright", parent, "bottomright", 1, -2) border2:SetColorTexture (0, 0, 0, alpha2 or default_border_color2) local border3 = parent:CreateTexture (nil, "background") PixelUtil.SetPoint (border3, "bottomleft", parent, "bottomleft", -2, -3) PixelUtil.SetPoint (border3, "bottomright", parent, "bottomright", 2, -3) border3:SetColorTexture (0, 0, 0, alpha3 or default_border_color3) tinsert (parent.Borders.Layer1, border1) tinsert (parent.Borders.Layer2, border2) tinsert (parent.Borders.Layer3, border3) end --DFNamePlateBorder as copy from "NameplateFullBorderTemplate" -> DF:CreateFullBorder (name, parent) local DFNamePlateBorderTemplateMixin = {}; function DFNamePlateBorderTemplateMixin:SetVertexColor(r, g, b, a) for i, texture in ipairs(self.Textures) do texture:SetVertexColor(r, g, b, a); end end function DFNamePlateBorderTemplateMixin:GetVertexColor() for i, texture in ipairs(self.Textures) do return texture:GetVertexColor(); end end function DFNamePlateBorderTemplateMixin:SetBorderSizes(borderSize, borderSizeMinPixels, upwardExtendHeightPixels, upwardExtendHeightMinPixels) self.borderSize = borderSize; self.borderSizeMinPixels = borderSizeMinPixels; self.upwardExtendHeightPixels = upwardExtendHeightPixels; self.upwardExtendHeightMinPixels = upwardExtendHeightMinPixels; end function DFNamePlateBorderTemplateMixin:UpdateSizes() local borderSize = self.borderSize or 1; local minPixels = self.borderSizeMinPixels or 2; local upwardExtendHeightPixels = self.upwardExtendHeightPixels or borderSize; local upwardExtendHeightMinPixels = self.upwardExtendHeightMinPixels or minPixels; PixelUtil.SetWidth(self.Left, borderSize, minPixels); PixelUtil.SetPoint(self.Left, "TOPRIGHT", self, "TOPLEFT", 0, upwardExtendHeightPixels, 0, upwardExtendHeightMinPixels); PixelUtil.SetPoint(self.Left, "BOTTOMRIGHT", self, "BOTTOMLEFT", 0, -borderSize, 0, minPixels); PixelUtil.SetWidth(self.Right, borderSize, minPixels); PixelUtil.SetPoint(self.Right, "TOPLEFT", self, "TOPRIGHT", 0, upwardExtendHeightPixels, 0, upwardExtendHeightMinPixels); PixelUtil.SetPoint(self.Right, "BOTTOMLEFT", self, "BOTTOMRIGHT", 0, -borderSize, 0, minPixels); PixelUtil.SetHeight(self.Bottom, borderSize, minPixels); PixelUtil.SetPoint(self.Bottom, "TOPLEFT", self, "BOTTOMLEFT", 0, 0); PixelUtil.SetPoint(self.Bottom, "TOPRIGHT", self, "BOTTOMRIGHT", 0, 0); if self.Top then PixelUtil.SetHeight(self.Top, borderSize, minPixels); PixelUtil.SetPoint(self.Top, "BOTTOMLEFT", self, "TOPLEFT", 0, 0); PixelUtil.SetPoint(self.Top, "BOTTOMRIGHT", self, "TOPRIGHT", 0, 0); end end function DF:CreateFullBorder (name, parent) local border = CreateFrame("Frame", name, parent) border:SetAllPoints() border:SetIgnoreParentScale(true) border:SetFrameLevel(border:GetParent():GetFrameLevel()) border.Textures = {} Mixin(border, DFNamePlateBorderTemplateMixin) local left = border:CreateTexture("$parentLeft", "BACKGROUND", nil, -8) --left:SetDrawLayer("BACKGROUND", -8) left:SetColorTexture(1, 1, 1, 1) left:SetWidth(1.0) left:SetPoint("TOPRIGHT", border, "TOPLEFT", 0, 1.0) left:SetPoint("BOTTOMRIGHT", border, "BOTTOMLEFT", 0, -1.0) border.Left = left tinsert(border.Textures, left) local right = border:CreateTexture("$parentRight", "BACKGROUND", nil, -8) --right:SetDrawLayer("BACKGROUND", -8) right:SetColorTexture(1, 1, 1, 1) right:SetWidth(1.0) right:SetPoint("TOPLEFT", border, "TOPRIGHT", 0, 1.0) right:SetPoint("BOTTOMLEFT", border, "BOTTOMRIGHT", 0, -1.0) border.Right = right tinsert(border.Textures, right) local bottom = border:CreateTexture("$parentBottom", "BACKGROUND", nil, -8) --bottom:SetDrawLayer("BACKGROUND", -8) bottom:SetColorTexture(1, 1, 1, 1) bottom:SetHeight(1.0) bottom:SetPoint("TOPLEFT", border, "BOTTOMLEFT", 0, 0) bottom:SetPoint("TOPRIGHT", border, "BOTTOMRIGHT", 0, 0) border.Bottom = bottom tinsert(border.Textures, bottom) local top = border:CreateTexture("$parentTop", "BACKGROUND", nil, -8) --top:SetDrawLayer("BACKGROUND", -8) top:SetColorTexture(1, 1, 1, 1) top:SetHeight(1.0) top:SetPoint("BOTTOMLEFT", border, "TOPLEFT", 0, 0) top:SetPoint("BOTTOMRIGHT", border, "TOPRIGHT", 0, 0) border.Top = top tinsert(border.Textures, top) return border end function DF:CreateBorderSolid (parent, size) end function DF:CreateBorderWithSpread (parent, alpha1, alpha2, alpha3, size, spread) parent.Borders = { Layer1 = {}, Layer2 = {}, Layer3 = {}, Alpha1 = alpha1 or default_border_color1, Alpha2 = alpha2 or default_border_color2, Alpha3 = alpha3 or default_border_color3, } parent.SetBorderAlpha = SetBorderAlpha parent.SetBorderColor = SetBorderColor parent.SetLayerVisibility = SetLayerVisibility size = size or 1 local minPixels = 1 local spread = 0 --left local border1 = parent:CreateTexture (nil, "background") border1:SetColorTexture (0, 0, 0, alpha1 or default_border_color1) PixelUtil.SetPoint (border1, "topleft", parent, "topleft", -1 + spread, 1 + (-spread), 0, 0) PixelUtil.SetPoint (border1, "bottomleft", parent, "bottomleft", -1 + spread, -1 + spread, 0, 0) PixelUtil.SetWidth (border1, size, minPixels) local border2 = parent:CreateTexture (nil, "background") PixelUtil.SetPoint (border2, "topleft", parent, "topleft", -2 + spread, 2 + (-spread)) PixelUtil.SetPoint (border2, "bottomleft", parent, "bottomleft", -2 + spread, -2 + spread) border2:SetColorTexture (0, 0, 0, alpha2 or default_border_color2) PixelUtil.SetWidth (border2, size, minPixels) local border3 = parent:CreateTexture (nil, "background") PixelUtil.SetPoint (border3, "topleft", parent, "topleft", -3 + spread, 3 + (-spread)) PixelUtil.SetPoint (border3, "bottomleft", parent, "bottomleft", -3 + spread, -3 + spread) border3:SetColorTexture (0, 0, 0, alpha3 or default_border_color3) PixelUtil.SetWidth (border3, size, minPixels) tinsert (parent.Borders.Layer1, border1) tinsert (parent.Borders.Layer2, border2) tinsert (parent.Borders.Layer3, border3) --top local border1 = parent:CreateTexture (nil, "background") PixelUtil.SetPoint (border1, "topleft", parent, "topleft", 0 + spread, 1 + (-spread)) PixelUtil.SetPoint (border1, "topright", parent, "topright", 1 + (-spread), 1 + (-spread)) border1:SetColorTexture (0, 0, 0, alpha1 or default_border_color1) PixelUtil.SetHeight (border1, size, minPixels) local border2 = parent:CreateTexture (nil, "background") PixelUtil.SetPoint (border2, "topleft", parent, "topleft", -1 + spread, 2 + (-spread)) PixelUtil.SetPoint (border2, "topright", parent, "topright", 2 + (-spread), 2 + (-spread)) border2:SetColorTexture (0, 0, 0, alpha2 or default_border_color2) PixelUtil.SetHeight (border2, size, minPixels) local border3 = parent:CreateTexture (nil, "background") PixelUtil.SetPoint (border3, "topleft", parent, "topleft", -2 + spread, 3 + (-spread)) PixelUtil.SetPoint (border3, "topright", parent, "topright", 3 + (-spread), 3 + (-spread)) border3:SetColorTexture (0, 0, 0, alpha3 or default_border_color3) PixelUtil.SetHeight (border3, size, minPixels) tinsert (parent.Borders.Layer1, border1) tinsert (parent.Borders.Layer2, border2) tinsert (parent.Borders.Layer3, border3) --right local border1 = parent:CreateTexture (nil, "background") PixelUtil.SetPoint (border1, "topright", parent, "topright", 1 + (-spread), 0 + (-spread)) PixelUtil.SetPoint (border1, "bottomright", parent, "bottomright", 1 + (-spread), -1 + spread) border1:SetColorTexture (0, 0, 0, alpha1 or default_border_color1) PixelUtil.SetWidth (border1, size, minPixels) local border2 = parent:CreateTexture (nil, "background") PixelUtil.SetPoint (border2, "topright", parent, "topright", 2 + (-spread), 1 + (-spread)) PixelUtil.SetPoint (border2, "bottomright", parent, "bottomright", 2 + (-spread), -2 + spread) border2:SetColorTexture (0, 0, 0, alpha2 or default_border_color2) PixelUtil.SetWidth (border2, size, minPixels) local border3 = parent:CreateTexture (nil, "background") PixelUtil.SetPoint (border3, "topright", parent, "topright", 3 + (-spread), 2 + (-spread)) PixelUtil.SetPoint (border3, "bottomright", parent, "bottomright", 3 + (-spread), -3 + spread) border3:SetColorTexture (0, 0, 0, alpha3 or default_border_color3) PixelUtil.SetWidth (border3, size, minPixels) tinsert (parent.Borders.Layer1, border1) tinsert (parent.Borders.Layer2, border2) tinsert (parent.Borders.Layer3, border3) local border1 = parent:CreateTexture (nil, "background") PixelUtil.SetPoint (border1, "bottomleft", parent, "bottomleft", 0 + spread, -1 + spread) PixelUtil.SetPoint (border1, "bottomright", parent, "bottomright", 0 + (-spread), -1 + spread) border1:SetColorTexture (0, 0, 0, alpha1 or default_border_color1) PixelUtil.SetHeight (border1, size, minPixels) local border2 = parent:CreateTexture (nil, "background") PixelUtil.SetPoint (border2, "bottomleft", parent, "bottomleft", -1 + spread, -2 + spread) PixelUtil.SetPoint (border2, "bottomright", parent, "bottomright", 1 + (-spread), -2 + spread) border2:SetColorTexture (0, 0, 0, alpha2 or default_border_color2) PixelUtil.SetHeight (border2, size, minPixels) local border3 = parent:CreateTexture (nil, "background") PixelUtil.SetPoint (border3, "bottomleft", parent, "bottomleft", -2 + spread, -3 + spread) PixelUtil.SetPoint (border3, "bottomright", parent, "bottomright", 2 + (-spread), -3 + spread) border3:SetColorTexture (0, 0, 0, alpha3 or default_border_color3) PixelUtil.SetHeight (border3, size, minPixels) tinsert (parent.Borders.Layer1, border1) tinsert (parent.Borders.Layer2, border2) tinsert (parent.Borders.Layer3, border3) end function DF:ReskinSlider (slider, heightOffset) if (slider.slider) then slider.cima:SetNormalTexture([[Interface\Buttons\Arrow-Up-Up]]) slider.cima:SetPushedTexture([[Interface\Buttons\Arrow-Up-Down]]) slider.cima:SetDisabledTexture([[Interface\Buttons\Arrow-Up-Disabled]]) slider.cima:GetNormalTexture():ClearAllPoints() slider.cima:GetPushedTexture():ClearAllPoints() slider.cima:GetDisabledTexture():ClearAllPoints() slider.cima:GetNormalTexture():SetPoint("center", slider.cima, "center", 1, 1) slider.cima:GetPushedTexture():SetPoint("center", slider.cima, "center", 1, 1) slider.cima:GetDisabledTexture():SetPoint("center", slider.cima, "center", 1, 1) slider.cima:SetSize(16, 16) slider.baixo:SetNormalTexture([[Interface\Buttons\Arrow-Down-Up]]) slider.baixo:SetPushedTexture([[Interface\Buttons\Arrow-Down-Down]]) slider.baixo:SetDisabledTexture([[Interface\Buttons\Arrow-Down-Disabled]]) slider.baixo:GetNormalTexture():ClearAllPoints() slider.baixo:GetPushedTexture():ClearAllPoints() slider.baixo:GetDisabledTexture():ClearAllPoints() slider.baixo:GetNormalTexture():SetPoint("center", slider.baixo, "center", 1, -5) slider.baixo:GetPushedTexture():SetPoint("center", slider.baixo, "center", 1, -5) slider.baixo:GetDisabledTexture():SetPoint("center", slider.baixo, "center", 1, -5) slider.baixo:SetSize(16, 16) slider.slider:cimaPoint(0, 13) slider.slider:baixoPoint(0, -13) slider.slider.thumb:SetTexture([[Interface\AddOns\Details\images\icons2]]) slider.slider.thumb:SetTexCoord(482/512, 492/512, 104/512, 120/512) slider.slider.thumb:SetSize(12, 12) slider.slider.thumb:SetVertexColor(0.6, 0.6, 0.6, 0.95) else --up button local offset = 1 --space between the scrollbox and the scrollar do local normalTexture = slider.ScrollBar.ScrollUpButton.Normal normalTexture:SetTexture([[Interface\Buttons\Arrow-Up-Up]]) normalTexture:SetTexCoord(0, 1, .2, 1) normalTexture:SetPoint("topleft", slider.ScrollBar.ScrollUpButton, "topleft", offset, 0) normalTexture:SetPoint("bottomright", slider.ScrollBar.ScrollUpButton, "bottomright", offset, 0) local pushedTexture = slider.ScrollBar.ScrollUpButton.Pushed pushedTexture:SetTexture([[Interface\Buttons\Arrow-Up-Down]]) pushedTexture:SetTexCoord(0, 1, .2, 1) pushedTexture:SetPoint("topleft", slider.ScrollBar.ScrollUpButton, "topleft", offset, 0) pushedTexture:SetPoint("bottomright", slider.ScrollBar.ScrollUpButton, "bottomright", offset, 0) local disabledTexture = slider.ScrollBar.ScrollUpButton.Disabled disabledTexture:SetTexture([[Interface\Buttons\Arrow-Up-Disabled]]) disabledTexture:SetTexCoord(0, 1, .2, 1) disabledTexture:SetAlpha(.5) disabledTexture:SetPoint("topleft", slider.ScrollBar.ScrollUpButton, "topleft", offset, 0) disabledTexture:SetPoint("bottomright", slider.ScrollBar.ScrollUpButton, "bottomright", offset, 0) slider.ScrollBar.ScrollUpButton:SetSize(16, 16) end --down button do local normalTexture = slider.ScrollBar.ScrollDownButton.Normal normalTexture:SetTexture([[Interface\Buttons\Arrow-Down-Up]]) normalTexture:SetTexCoord(0, 1, 0, .8) normalTexture:SetPoint("topleft", slider.ScrollBar.ScrollDownButton, "topleft", offset, -4) normalTexture:SetPoint("bottomright", slider.ScrollBar.ScrollDownButton, "bottomright", offset, -4) local pushedTexture = slider.ScrollBar.ScrollDownButton.Pushed pushedTexture:SetTexture([[Interface\Buttons\Arrow-Down-Down]]) pushedTexture:SetTexCoord(0, 1, 0, .8) pushedTexture:SetPoint("topleft", slider.ScrollBar.ScrollDownButton, "topleft", offset, -4) pushedTexture:SetPoint("bottomright", slider.ScrollBar.ScrollDownButton, "bottomright", offset, -4) local disabledTexture = slider.ScrollBar.ScrollDownButton.Disabled disabledTexture:SetTexture([[Interface\Buttons\Arrow-Down-Disabled]]) disabledTexture:SetTexCoord(0, 1, 0, .8) disabledTexture:SetAlpha(.5) disabledTexture:SetPoint("topleft", slider.ScrollBar.ScrollDownButton, "topleft", offset, -4) disabledTexture:SetPoint("bottomright", slider.ScrollBar.ScrollDownButton, "bottomright", offset, -4) slider.ScrollBar.ScrollDownButton:SetSize (16, 16) end --if the parent has a editbox, this is a code editor if (slider:GetParent().editbox) then slider.ScrollBar:SetPoint("TOPLEFT", slider, "TOPRIGHT", 12 + offset, -6) slider.ScrollBar:SetPoint("BOTTOMLEFT", slider, "BOTTOMRIGHT", 12 + offset, 6 + (heightOffset and heightOffset*-1 or 0)) else slider.ScrollBar:SetPoint("TOPLEFT", slider, "TOPRIGHT", 6, -16) slider.ScrollBar:SetPoint("BOTTOMLEFT", slider, "BOTTOMRIGHT", 6, 16 + (heightOffset and heightOffset*-1 or 0)) end slider.ScrollBar.ThumbTexture:SetColorTexture(.5, .5, .5, .3) slider.ScrollBar.ThumbTexture:SetSize(12, 8) end end function DF:GetCurrentSpec() local specIndex = DF.GetSpecialization() if (specIndex) then local specID = DF.GetSpecializationInfo (specIndex) if (specID and specID ~= 0) then return specID end end end function DF:GetCurrentSpecId() return DF:GetCurrentSpec() end local specs_per_class = { ["DEMONHUNTER"] = {577, 581}, ["DEATHKNIGHT"] = {250, 251, 252}, ["WARRIOR"] = {71, 72, 73}, ["MAGE"] = {62, 63, 64}, ["ROGUE"] = {259, 260, 261}, ["DRUID"] = {102, 103, 104, 105}, ["HUNTER"] = {253, 254, 255}, ["SHAMAN"] = {262, 263, 254}, ["PRIEST"] = {256, 257, 258}, ["WARLOCK"] = {265, 266, 267}, ["PALADIN"] = {65, 66, 70}, ["MONK"] = {268, 269, 270}, ["EVOKER"] = {1467, 1468}, } function DF:GetClassSpecIDs (class) return specs_per_class [class] end local dispatch_error = function(context, errortext) DF:Msg ( (context or "") .. " |cFFFF9900error|r: " .. (errortext or "")) end --> safe call an external func with payload and without telling who is calling function DF:QuickDispatch (func, ...) if (type (func) ~= "function") then return end local okay, errortext = pcall (func, ...) if (not okay) then --> trigger an error msg dispatch_error (_, errortext) return end return true end function DF:Dispatch(func, ...) if (type (func) ~= "function") then return dispatch_error (_, "DF:Dispatch expect a function as parameter 1.") end local dispatchResult = {xpcall (func, geterrorhandler(), ...)} local okay = dispatchResult[1] if (not okay) then return nil end tremove(dispatchResult, 1) return unpack(dispatchResult) end --[=[ DF:CoreDispatch (func, context, ...) safe call a function making a error window with what caused, the context and traceback of the error this func is only used inside the framework for sensitive calls where the func must run without errors @func = the function which will be called @context = what made the function be called ... parameters to pass in the function call --]=] function DF:CoreDispatch (context, func, ...) if (type (func) ~= "function") then local stack = debugstack(2) local errortext = "D!Framework " .. context .. " error: invalid function to call\n====================\n" .. stack .. "\n====================\n" error (errortext) end local okay, result1, result2, result3, result4 = xpcall(func, geterrorhandler(), ...) --if (not okay) then --when using pcall --local stack = debugstack(2) --local errortext = "D!Framework (" .. context .. ") error: " .. result1 .. "\n====================\n" .. stack .. "\n====================\n" --error (errortext) --end return result1, result2, result3, result4 end --/run local a, b =32,3; local f=function(c,d) return c+d, 2, 3;end; print (xpcall(f,geterrorhandler(),a,b)) function DF_CALC_PERFORMANCE() local F = CreateFrame ("frame") local T = GetTime() local J = false F:SetScript ("OnUpdate", function(self, deltaTime) if (not J) then J = true return end print ("Elapsed Time:", deltaTime) F:SetScript ("OnUpdate", nil) end) end DF.ClassIndexToFileName = { [6] = "DEATHKNIGHT", [1] = "WARRIOR", [4] = "ROGUE", [8] = "MAGE", [5] = "PRIEST", [3] = "HUNTER", [9] = "WARLOCK", [12] = "DEMONHUNTER", [7] = "SHAMAN", [11] = "DRUID", [10] = "MONK", [2] = "PALADIN", [13] = "EVOKER", } DF.ClassFileNameToIndex = { ["DEATHKNIGHT"] = 6, ["WARRIOR"] = 1, ["ROGUE"] = 4, ["MAGE"] = 8, ["PRIEST"] = 5, ["HUNTER"] = 3, ["WARLOCK"] = 9, ["DEMONHUNTER"] = 12, ["SHAMAN"] = 7, ["DRUID"] = 11, ["MONK"] = 10, ["PALADIN"] = 2, ["EVOKER"] = 13, } DF.ClassCache = {} function DF:GetClassList() if (next (DF.ClassCache)) then return DF.ClassCache end for className, classIndex in pairs (DF.ClassFileNameToIndex) do local classTable = C_CreatureInfo.GetClassInfo (classIndex) if classTable then local t = { ID = classIndex, Name = classTable.className, Texture = [[Interface\GLUES\CHARACTERCREATE\UI-CharacterCreate-Classes]], TexCoord = CLASS_ICON_TCOORDS [className], FileString = className, } tinsert (DF.ClassCache, t) end end return DF.ClassCache end --hardcoded race list DF.RaceList = { [1] = "Human", [2] = "Orc", [3] = "Dwarf", [4] = "NightElf", [5] = "Scourge", [6] = "Tauren", [7] = "Gnome", [8] = "Troll", [9] = "Goblin", [10] = "BloodElf", [11] = "Draenei", [22] = "Worgen", [24] = "Pandaren", } DF.AlliedRaceList = { [27] = "Nightborne", [29] = "HighmountainTauren", [31] = "VoidElf", [33] = "LightforgedDraenei", [35] = "ZandalariTroll", [36] = "KulTiran", [38] = "DarkIronDwarf", [40] = "Vulpera", [41] = "MagharOrc", } local slotIdToIcon = { [1] = "Interface\\ICONS\\" .. "INV_Helmet_29", --head [2] = "Interface\\ICONS\\" .. "INV_Jewelry_Necklace_07", --neck [3] = "Interface\\ICONS\\" .. "INV_Shoulder_25", --shoulder [5] = "Interface\\ICONS\\" .. "INV_Chest_Cloth_08", --chest [6] = "Interface\\ICONS\\" .. "INV_Belt_15", --waist [7] = "Interface\\ICONS\\" .. "INV_Pants_08", --legs [8] = "Interface\\ICONS\\" .. "INV_Boots_Cloth_03", --feet [9] = "Interface\\ICONS\\" .. "INV_Bracer_07", --wrist [10] = "Interface\\ICONS\\" .. "INV_Gauntlets_17", --hands [11] = "Interface\\ICONS\\" .. "INV_Jewelry_Ring_22", --finger 1 [12] = "Interface\\ICONS\\" .. "INV_Jewelry_Ring_22", --finger 2 [13] = "Interface\\ICONS\\" .. "INV_Jewelry_Talisman_07", --trinket 1 [14] = "Interface\\ICONS\\" .. "INV_Jewelry_Talisman_07", --trinket 2 [15] = "Interface\\ICONS\\" .. "INV_Misc_Cape_19", --back [16] = "Interface\\ICONS\\" .. "INV_Sword_39", --main hand [17] = "Interface\\ICONS\\" .. "INV_Sword_39", --off hand } function DF:GetArmorIconByArmorSlot(equipSlotId) return slotIdToIcon[equipSlotId] or "" end --> store and return a list of character races, always return the non-localized value DF.RaceCache = {} function DF:GetCharacterRaceList() if (next (DF.RaceCache)) then return DF.RaceCache end for i = 1, 100 do local raceInfo = C_CreatureInfo.GetRaceInfo (i) if (raceInfo and DF.RaceList [raceInfo.raceID]) then tinsert (DF.RaceCache, {Name = raceInfo.raceName, FileString = raceInfo.clientFileString, ID = raceInfo.raceID}) end if IS_WOW_PROJECT_MAINLINE then local alliedRaceInfo = C_AlliedRaces.GetRaceInfoByID (i) if (alliedRaceInfo and DF.AlliedRaceList [alliedRaceInfo.raceID]) then tinsert (DF.RaceCache, {Name = alliedRaceInfo.maleName, FileString = alliedRaceInfo.raceFileString, ID = alliedRaceInfo.raceID}) end end end return DF.RaceCache end --get a list of talents for the current spec the player is using --if onlySelected return an index table with only the talents the character has selected --if onlySelectedHash return a hash table with [spelID] = true function DF:GetCharacterTalents (onlySelected, onlySelectedHash) local talentList = {} for i = 1, 7 do for o = 1, 3 do local talentID, name, texture, selected, available = GetTalentInfo (i, o, 1) if (onlySelectedHash) then if (selected) then talentList [talentID] = true break end elseif (onlySelected) then if (selected) then tinsert (talentList, {Name = name, ID = talentID, Texture = texture, IsSelected = selected}) break end else tinsert (talentList, {Name = name, ID = talentID, Texture = texture, IsSelected = selected}) end end end return talentList end function DF:GetCharacterPvPTalents (onlySelected, onlySelectedHash) if (onlySelected or onlySelectedHash) then local talentsSelected = C_SpecializationInfo.GetAllSelectedPvpTalentIDs() local talentList = {} for _, talentID in ipairs (talentsSelected) do local _, talentName, texture = GetPvpTalentInfoByID (talentID) if (onlySelectedHash) then talentList [talentID] = true else tinsert (talentList, {Name = talentName, ID = talentID, Texture = texture, IsSelected = true}) end end return talentList else local alreadyAdded = {} local talentList = {} for i = 1, 4 do --4 slots - get talents available in each one local slotInfo = C_SpecializationInfo.GetPvpTalentSlotInfo (i) if (slotInfo) then for _, talentID in ipairs (slotInfo.availableTalentIDs) do if (not alreadyAdded [talentID]) then local _, talentName, texture, selected = GetPvpTalentInfoByID (talentID) tinsert (talentList, {Name = talentName, ID = talentID, Texture = texture, IsSelected = selected}) alreadyAdded [talentID] = true end end end end return talentList end end DF.GroupTypes = { {Name = "Arena", ID = "arena"}, {Name = "Battleground", ID = "pvp"}, {Name = "Raid", ID = "raid"}, {Name = "Dungeon", ID = "party"}, {Name = "Scenario", ID = "scenario"}, {Name = "Open World", ID = "none"}, } function DF:GetGroupTypes() return DF.GroupTypes end DF.RoleTypes = { {Name = _G.DAMAGER, ID = "DAMAGER", Texture = _G.INLINE_DAMAGER_ICON}, {Name = _G.HEALER, ID = "HEALER", Texture = _G.INLINE_HEALER_ICON}, {Name = _G.TANK, ID = "TANK", Texture = _G.INLINE_TANK_ICON}, } function DF:GetRoleTypes() return DF.RoleTypes end local roleTexcoord = { DAMAGER = "72:130:69:127", HEALER = "72:130:2:60", TANK = "5:63:69:127", NONE = "139:196:69:127", } local roleTextures = { DAMAGER = "Interface\\LFGFRAME\\UI-LFG-ICON-ROLES", TANK = "Interface\\LFGFRAME\\UI-LFG-ICON-ROLES", HEALER = "Interface\\LFGFRAME\\UI-LFG-ICON-ROLES", NONE = "Interface\\LFGFRAME\\UI-LFG-ICON-ROLES", } local roleTexcoord2 = { DAMAGER = {72/256, 130/256, 69/256, 127/256}, HEALER = {72/256, 130/256, 2/256, 60/256}, TANK = {5/256, 63/256, 69/256, 127/256}, NONE = {139/256, 196/256, 69/256, 127/256}, } function DF:GetRoleIconAndCoords(role) local texture = roleTextures[role] local coords = roleTexcoord2[role] return texture, unpack(coords) end function DF:AddRoleIconToText(text, role, size) if (role and type(role) == "string") then local coords = GetTexCoordsForRole(role) if (coords) then if (type (text) == "string" and role ~= "NONE") then size = size or 14 text = "|TInterface\\LFGFRAME\\UI-LFG-ICON-ROLES:" .. size .. ":" .. size .. ":0:0:256:256:" .. roleTexcoord[role] .. "|t " .. text return text end end end return text end function DF:GetRoleTCoordsAndTexture(roleID) local texture, l, r, t, b = DF:GetRoleIconAndCoords(roleID) return l, r, t, b, texture end -- TODO: maybe make this auto-generaded some day?... DF.CLEncounterID = { {ID = 2423, Name = "The Tarragrue"}, {ID = 2433, Name = "The Eye of the Jailer"}, {ID = 2429, Name = "The Nine"}, {ID = 2432, Name = "Remnant of Ner'zhul"}, {ID = 2434, Name = "Soulrender Dormazain"}, {ID = 2430, Name = "Painsmith Raznal"}, {ID = 2436, Name = "Guardian of the First Ones"}, {ID = 2431, Name = "Fatescribe Roh-Kalo"}, {ID = 2422, Name = "Kel'Thuzad"}, {ID = 2435, Name = "Sylvanas Windrunner"}, } function DF:GetPlayerRole() local assignedRole = DF.UnitGroupRolesAssigned ("player") if (assignedRole == "NONE") then local spec = DF.GetSpecialization() return spec and DF.GetSpecializationRole (spec) or "NONE" end return assignedRole end function DF:GetCLEncounterIDs() return DF.CLEncounterID end DF.ClassSpecs = { ["DEMONHUNTER"] = { [577] = true, [581] = true, }, ["DEATHKNIGHT"] = { [250] = true, [251] = true, [252] = true, }, ["WARRIOR"] = { [71] = true, [72] = true, [73] = true, }, ["MAGE"] = { [62] = true, [63] = true, [64] = true, }, ["ROGUE"] = { [259] = true, [260] = true, [261] = true, }, ["DRUID"] = { [102] = true, [103] = true, [104] = true, [105] = true, }, ["HUNTER"] = { [253] = true, [254] = true, [255] = true, }, ["SHAMAN"] = { [262] = true, [263] = true, [264] = true, }, ["PRIEST"] = { [256] = true, [257] = true, [258] = true, }, ["WARLOCK"] = { [265] = true, [266] = true, [267] = true, }, ["PALADIN"] = { [65] = true, [66] = true, [70] = true, }, ["MONK"] = { [268] = true, [269] = true, [270] = true, }, ["EVOKER"] = { [1467] = true, [1468] = true, }, } DF.SpecListByClass = { ["DEMONHUNTER"] = { 577, 581, }, ["DEATHKNIGHT"] = { 250, 251, 252, }, ["WARRIOR"] = { 71, 72, 73, }, ["MAGE"] = { 62, 63, 64, }, ["ROGUE"] = { 259, 260, 261, }, ["DRUID"] = { 102, 103, 104, 105, }, ["HUNTER"] = { 253, 254, 255, }, ["SHAMAN"] = { 262, 263, 264, }, ["PRIEST"] = { 256, 257, 258, }, ["WARLOCK"] = { 265, 266, 267, }, ["PALADIN"] = { 65, 66, 70, }, ["MONK"] = { 268, 269, 270, }, ["EVOKER"] = { 1467, 1468, }, } --given a class and a specId, return if the specId is a spec from the class passed function DF:IsSpecFromClass(class, specId) return DF.ClassSpecs[class] and DF.ClassSpecs[class][specId] end --return a has table where specid is the key and 'true' is the value function DF:GetClassSpecs(class) return DF.ClassSpecs [class] end --return a numeric table with spec ids function DF:GetSpecListFromClass(class) return DF.SpecListByClass [class] end --return a list with specIds as keys and spellId as value function DF:GetSpellsForRangeCheck() return SpellRangeCheckListBySpec end --return a list with specIds as keys and spellId as value function DF:GetRangeCheckSpellForSpec(specId) return SpellRangeCheckListBySpec[specId] end --key is instanceId from GetInstanceInfo() -- /dump GetInstanceInfo() DF.BattlegroundSizes = { [2245] = 15, --Deepwind Gorge [2106] = 10, --Warsong Gulch [2107] = 15, --Arathi Basin [566] = 15, --Eye of the Storm [30] = 40, --Alterac Valley [628] = 40, --Isle of Conquest [761] = 10, --The Battle for Gilneas [726] = 10, --Twin Peaks [727] = 10, --Silvershard Mines [998] = 10, --Temple of Kotmogu [2118] = 40, --Battle for Wintergrasp [1191] = 25, --Ashran [1803] = 10, --Seething Shore } function DF:GetBattlegroundSize(instanceInfoMapId) return DF.BattlegroundSizes[instanceInfoMapId] end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ --> execute range function DF.GetExecuteRange(unitId) unitId = unitId or "player" local classLoc, class = UnitClass(unitId) local spec = GetSpecialization() if (spec and class) then --prist if (class == "PRIEST") then --playing as shadow? local specID = GetSpecializationInfo(spec) if (specID and specID ~= 0) then if (specID == 258) then --shadow local _, _, _, using_SWDeath = GetTalentInfo(5, 2, 1) if (using_SWDeath) then return 0.20 end end end elseif (class == "MAGE") then --playing fire mage? local specID = GetSpecializationInfo(spec) if (specID and specID ~= 0) then if (specID == 63) then --fire local _, _, _, using_SearingTouch = GetTalentInfo(1, 3, 1) if (using_SearingTouch) then return 0.30 end end end elseif (class == "WARRIOR") then --is playing as a Arms warrior? local specID = GetSpecializationInfo(spec) if (specID and specID ~= 0) then if (specID == 71) then --arms local _, _, _, using_Massacre = GetTalentInfo(3, 1, 1) if (using_Massacre) then --if using massacre, execute can be used at 35% health in Arms spec return 0.35 end end if (specID == 71 or specID == 72) then --arms or fury return 0.20 end end elseif (class == "HUNTER") then local specID = GetSpecializationInfo(spec) if (specID and specID ~= 0) then if (specID == 253) then --beast mastery --> is using killer instinct? local _, _, _, using_KillerInstinct = GetTalentInfo(1, 1, 1) if (using_KillerInstinct) then return 0.35 end end end elseif (class == "PALADIN") then local specID = GetSpecializationInfo(spec) if (specID and specID ~= 0) then if (specID == 70) then --retribution paladin --> is using hammer of wrath? local _, _, _, using_HammerOfWrath = GetTalentInfo(2, 3, 1) if (using_HammerOfWrath) then return 0.20 end end end end end end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ --> delta seconds reader if (not DetailsFrameworkDeltaTimeFrame) then CreateFrame ("frame", "DetailsFrameworkDeltaTimeFrame", UIParent) end local deltaTimeFrame = DetailsFrameworkDeltaTimeFrame deltaTimeFrame:SetScript ("OnUpdate", function(self, deltaTime) self.deltaTime = deltaTime end) function GetWorldDeltaSeconds() return deltaTimeFrame.deltaTime end function DF:GetWorldDeltaSeconds() return deltaTimeFrame.deltaTime end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ --> build the global script channel for scripts communication --send and retrieve data sent by othe users in scripts --Usage: --DetailsFramework:RegisterScriptComm (ID, function(sourcePlayerName, ...) end) --DetailsFramework:SendScriptComm (ID, ...) local aceComm = LibStub:GetLibrary ("AceComm-3.0", true) local LibAceSerializer = LibStub:GetLibrary ("AceSerializer-3.0", true) local LibDeflate = LibStub:GetLibrary ("LibDeflate", true) DF.RegisteredScriptsComm = DF.RegisteredScriptsComm or {} function DF.OnReceiveScriptComm (...) local prefix, encodedString, channel, commSource = ... local decodedString = LibDeflate:DecodeForWoWAddonChannel (encodedString) if (decodedString) then local uncompressedString = LibDeflate:DecompressDeflate (decodedString) if (uncompressedString) then local data = {LibAceSerializer:Deserialize (uncompressedString)} if (data[1]) then local ID = data[2] if (ID) then local sourceName = data[4] if (Ambiguate (sourceName, "none") == commSource) then local func = DF.RegisteredScriptsComm [ID] if (func) then DF:MakeFunctionSecure(func) DF:Dispatch (func, commSource, select (5, unpack (data))) --this use xpcall end end end end end end end function DF:RegisterScriptComm (ID, func) if (ID) then if (type (func) == "function") then DF.RegisteredScriptsComm [ID] = func else DF.RegisteredScriptsComm [ID] = nil end end end function DF:SendScriptComm (ID, ...) if (DF.RegisteredScriptsComm [ID]) then local sourceName = UnitName ("player") .. "-" .. GetRealmName() local data = LibAceSerializer:Serialize (ID, UnitGUID ("player"), sourceName, ...) data = LibDeflate:CompressDeflate (data, {level = 9}) data = LibDeflate:EncodeForWoWAddonChannel (data) aceComm:SendCommMessage ("_GSC", data, "PARTY") end end if (aceComm and LibAceSerializer and LibDeflate) then aceComm:RegisterComm ("_GSC", DF.OnReceiveScriptComm) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ --> debug DF.DebugMixin = { debug = true, CheckPoint = function(self, checkPointName, ...) print (self:GetName(), checkPointName, ...) end, CheckVisibilityState = function(self, widget) self = widget or self local width, height = self:GetSize() width = floor (width) height = floor (height) local numPoints = self:GetNumPoints() print ("shown:", self:IsShown(), "visible:", self:IsVisible(), "alpha:", self:GetAlpha(), "size:", width, height, "points:", numPoints) end, CheckStack = function(self) local stack = debugstack() Details:Dump (stack) end, } ----------------------------------------------------------------------------------------------------------------------------------------------------------- --> returns if the unit is tapped (gray health color when another player hit the unit first) function DF:IsUnitTapDenied (unitId) return unitId and not UnitPlayerControlled (unitId) and UnitIsTapDenied (unitId) end ----------------------------------------------------------------------------------------------------------------------------------------------------------- --> pool do local get = function(self) local object = tremove(self.notUse, #self.notUse) if (object) then tinsert(self.inUse, object) if (self.onAcquire) then DF:QuickDispatch(self.onAcquire, object) end return object, false else --need to create the new object local newObject = self.newObjectFunc(self, unpack(self.payload)) if (newObject) then tinsert(self.inUse, newObject) if (self.onAcquire) then DF:QuickDispatch(self.onAcquire, object) end return newObject, true end end end local get_all_inuse = function(self) return self.inUse; end local release = function(self, object) for i = #self.inUse, 1, -1 do if (self.inUse[i] == object) then tremove(self.inUse, i) tinsert(self.notUse, object) if (self.onRelease) then DF:QuickDispatch(self.onRelease, object) end break end end end local reset = function(self) for i = #self.inUse, 1, -1 do local object = tremove(self.inUse, i) tinsert(self.notUse, object) if (self.onReset) then DF:QuickDispatch(self.onReset, object) end end end --only hide objects in use, do not disable them local hide = function(self) for i = #self.inUse, 1, -1 do self.inUse[i]:Hide() end end --only show objects in use, do not enable them local show = function(self) for i = #self.inUse, 1, -1 do self.inUse[i]:Show() end end --return the amount of objects local getamount = function(self) return #self.notUse + #self.inUse, #self.notUse, #self.inUse end local poolMixin = { Get = get, GetAllInUse = get_all_inuse, Acquire = get, Release = release, Reset = reset, ReleaseAll = reset, Hide = hide, Show = show, GetAmount = getamount, SetCallbackOnRelease = function(self, func) self.onRelease = func end, SetOnReset = function(self, func) self.onReset = func end, SetCallbackOnReleaseAll = function(self, func) self.onReset = func end, SetOnAcquire = function(self, func) self.onAcquire = func end, SetCallbackOnGet = function(self, func) self.onAcquire = func end, } function DF:CreatePool(func, ...) local t = {} DetailsFramework:Mixin(t, poolMixin) t.inUse = {} t.notUse = {} t.newObjectFunc = func t.payload = {...} return t end --alias function DF:CreateObjectPool(func, ...) return DF:CreatePool(func, ...) end end ----------------------------------------------------------------------------------------------------------------------------------------------------------- --> forbidden functions on scripts --these are functions which scripts cannot run due to security issues local forbiddenFunction = { --block mail, trades, action house, banks ["C_AuctionHouse"] = true, ["C_Bank"] = true, ["C_GuildBank"] = true, ["SetSendMailMoney"] = true, ["SendMail"] = true, ["SetTradeMoney"] = true, ["AddTradeMoney"] = true, ["PickupTradeMoney"] = true, ["PickupPlayerMoney"] = true, ["AcceptTrade"] = true, --frames ["BankFrame"] = true, ["TradeFrame"] = true, ["GuildBankFrame"] = true, ["MailFrame"] = true, ["EnumerateFrames"] = true, --block run code inside code ["RunScript"] = true, ["securecall"] = true, ["setfenv"] = true, ["getfenv"] = true, ["loadstring"] = true, ["pcall"] = true, ["xpcall"] = true, ["getglobal"] = true, ["setmetatable"] = true, ["DevTools_DumpCommand"] = true, --avoid creating macros ["SetBindingMacro"] = true, ["CreateMacro"] = true, ["EditMacro"] = true, ["hash_SlashCmdList"] = true, ["SlashCmdList"] = true, --block guild commands ["GuildDisband"] = true, ["GuildUninvite"] = true, --other things ["C_GMTicketInfo"] = true, --deny messing addons with script support ["PlaterDB"] = true, ["_detalhes_global"] = true, ["WeakAurasSaved"] = true, } local C_RestrictedSubFunctions = { ["C_GuildInfo"] = { ["RemoveFromGuild"] = true, }, } --not in use, can't find a way to check within the environment handle local addonRestrictedFunctions = { ["DetailsFramework"] = { ["SetEnvironment"] = true, }, ["Plater"] = { ["ImportScriptString"] = true, ["db"] = true, }, ["WeakAuras"] = { ["Add"] = true, ["AddMany"] = true, ["Delete"] = true, ["NewAura"] = true, }, } local C_SubFunctionsTable = {} for globalTableName, functionTable in pairs(C_RestrictedSubFunctions) do C_SubFunctionsTable [globalTableName] = {} for functionName, functionObject in pairs(_G[globalTableName]) do if (not functionTable[functionName]) then C_SubFunctionsTable [globalTableName][functionName] = functionObject end end end DF.DefaultSecureScriptEnvironmentHandle = { __index = function(env, key) if (forbiddenFunction[key]) then return nil elseif (key == "_G") then return env elseif (C_SubFunctionsTable[key]) then return C_SubFunctionsTable[key] end return _G[key] end } function DF:SetEnvironment(func, environmentHandle, newEnvironment) environmentHandle = environmentHandle or DF.DefaultSecureScriptEnvironmentHandle newEnvironment = newEnvironment or {} setmetatable(newEnvironment, environmentHandle) _G.setfenv(func, newEnvironment) end function DF:MakeFunctionSecure(func) return DF:SetEnvironment(func) end -----------------------------------------------------------------------------------------------------------------------------------------------------------