local _, addon = ... local TransitionAPI = addon.TransitionAPI; local RoundToDigit = addon.Math.RoundToDigit; local C_Item = C_Item; local After = C_Timer.After; local GetItemInfo = C_Item.GetItemInfo; local GetItemInfoInstant = C_Item.GetItemInfoInstant; local UIFrameFadeIn = UIFrameFadeIn; local UIFrameFadeOut = UIFrameFadeOut; local FadeFrame = NarciFadeUI.Fade; local PlaySound = PlaySound; local GetMouseFocus = addon.TransitionAPI.GetMouseFocus; local find = string.find; local match = string.match; local strsub = string.sub; local strsplit = strsplit; local min = math.min; local max = math.max; local abs = math.abs; local floor = math.floor; local unpack = unpack; local tinsert = table.insert; local TEXT_LOCALE = GetLocale(); local Narci = Narci; local NarciAPI = NarciAPI; local SecureContainer = CreateFrame("Frame", "NarciSecureFrameContainer"); SecureContainer:Hide(); --GetSlotVisualID local IGNORED_MOG_SLOT = { [11] = true, [12] = true, [13] = true, [14] = true, }; local function IsSlotValidForTransmog(slotID) return (slotID) and (not IGNORED_MOG_SLOT[slotID]) and slotID ~= 2 end NarciAPI.IsSlotValidForTransmog = IsSlotValidForTransmog; local function NarciAPI_GetSlotVisualID(slotID) if IGNORED_MOG_SLOT[slotID] then --slotID = 2 ~ Use neck to show right shoulder return 0, 0; end local isSecondaryAppearance; if slotID == 2 then isSecondaryAppearance = true; --Enum.TransmogModification.Secondary slotID = 3; end local itemLocation = ItemLocation:CreateFromEquipmentSlot(slotID); if not itemLocation or not C_Item.DoesItemExist(itemLocation) then return 0, 0; end local transmogLocation = CreateFromMixins(TransmogLocationMixin); local transmogType = 0; local modification = 0; if slotID == 3 then --Shoulders local itemTransmogInfo = C_Item.GetAppliedItemTransmogInfo(itemLocation); local hasSecondaryAppearance; if itemTransmogInfo then hasSecondaryAppearance = itemTransmogInfo.secondaryAppearanceID ~= 0; --show direction mark end if isSecondaryAppearance then if not hasSecondaryAppearance then return 0, 0; end modification = 1; --Enum.TransmogModification : 0 ~ Main, 1 ~ Secondary end transmogLocation:Set(slotID, transmogType, modification); local baseSourceID, baseVisualID, appliedSourceID, appliedVisualID, pendingSourceID, pendingVisualID, hasPendingUndo, isHideVisual, itemSubclass = C_Transmog.GetSlotVisualInfo(transmogLocation); if ( appliedSourceID == 0 ) then appliedSourceID = baseSourceID; appliedVisualID = baseVisualID; end return appliedSourceID, appliedVisualID, hasSecondaryAppearance; else transmogLocation:Set(slotID, transmogType, modification); local baseSourceID, baseVisualID, appliedSourceID, appliedVisualID, pendingSourceID, pendingVisualID, hasPendingUndo, isHideVisual, itemSubclass = C_Transmog.GetSlotVisualInfo(transmogLocation); if ( appliedSourceID == 0 ) then appliedSourceID = baseSourceID; appliedVisualID = baseVisualID; end return appliedSourceID, appliedVisualID; end end --/script for i =1, 17 do if C_Transmog.CanHaveSecondaryAppearanceForSlotID(i) then print(i) end end --/script C_Transmog.GetSlotVisualInfo( CreateFromMixins(TransmogLocationMixin):Set(3, 0, 0) ) --/dump C_Item.GetAppliedItemTransmogInfo(ItemLocation:CreateFromEquipmentSlot(3)) --/dump C_Transmog.GetSlotVisualInfo((CreateFromMixins(TransmogLocationMixin)):Set(3, 0, 0)); NarciAPI.GetSlotVisualID = NarciAPI_GetSlotVisualID; -------------------- ----API Datebase---- -------------------- --[[ function GetArtifactVisualModID(colorID) colorID = colorID or 42; local PRINT = false; local baseSourceID, baseVisualID, appliedSourceID, appliedVisualID, pendingSourceID, pendingVisualID, hasPendingUndo, hideVisual = C_Transmog.GetSlotVisualInfo(16, 0); if not appliedSourceID or appliedSourceID == 0 then appliedSourceID = baseSourceID; end local categoryID, visualID, canEnchant, icon, _, itemLink, transmogLink, _ = C_TransmogCollection.GetAppearanceSourceInfo(appliedSourceID) local sourceInfo = C_TransmogCollection.GetSourceInfo(appliedSourceID) if sourceInfo and PRINT then for k, v in pairs(sourceInfo) do print(k.." "..tostring(v)) end else print(sourceInfo.itemModID); end itemID = sourceInfo.itemID or 127829; itemLink = "\124cffe5cc80\124Hitem:".. itemID .."::::::::120::16777472::2:::"..colorID..":::::::::::::\124h[".. (sourceInfo.name or "") .."]\124h\124r" DEFAULT_CHAT_FRAME:AddMessage(itemLink) end --]] -----Color API------ local mapColors = { --[0] = { 35, 96, 147}, --default Blue 0.1372, 0.3765, 0.5765 [0] = {78, 78, 78}, --Default Black [1] = {121, 31, 35}, --Orgrimmar [2] = { 49, 176, 107}, --Zuldazar [3] = {187, 161, 134}, --Vol'dun [4] = { 89, 140, 123}, --Tiragarde Sound [5] = {127, 164, 114}, --Stormsong [6] = {156, 165, 153}, --Drustvar [7] = { 42, 63, 79}, --Halls of Shadow --[UiMapID] = {r, g, b} --Shadowlands [1970] = {137, 218, 247}, --Zereth Mortis [1670] = {76, 86, 109}, --Oribos [1533] = {197, 185, 172}, --Bastion [1707] = {193, 199, 210}, --Elysian Hold [1708] = {168, 188, 232}, --Sanctum of Binding [1701] = {57, 66, 154}, --Heart of the Forest [1565] = {57, 66, 154}, --Ardenweald [1536] = {25, 97, 85}, --Maldraxxus [1698] = {25, 97, 85}, --Seat of the Primus [1525] = {48, 96, 153}, --Revendreth [1911] = {53, 80, 115}, --Torghast Entrance [1912] = {53, 80, 115}, --Runecrafter --Major City-- [84] = {129, 144, 155}, --Stormwind City [85] = {121, 52, 55}, --Orgrimmar [86] = {121, 31, 35}, --Orgrimmar - Cleft of Shadow [463] = {163, 99, 89}, --Echo Isles [87] = {102, 64, 58}, --Ironforge [27] = {151, 198, 213}, --Dun Morogh [469] = {151, 198, 213}, --New Tinkertown [88] = {115, 140, 113}, --Thunder Bluff [89] = {121, 31, 35}, --Darnassus R.I.P. [90] = { 42, 63, 79}, --Undercity [110] = {172, 58, 54}, --Silvermoon City [202] = {78, 78, 78}, --Gilneas City [217] = {78, 78, 78}, --Ruins of Gilneas [627] = {102, 58, 64}, --Dalaran Broken Isles [111] = {88, 108, 91}, --Shattrath City -- TBC -- [107] = {181, 151, 93}, --Nagrand Outland [109] = {96, 48, 108}, --Netherstorm [102] = {61, 77, 162}, --Zangarmash [105] = {123, 104, 80}, --Blade's Edge Mountains -- MOP -- [378] = {120, 107, 81}, --The Wandering Isle [371] = { 95, 132, 78}, --The Jade Forrest [379] = { 90, 119, 156}, --Kun-Lai Summit -- LEG -- [641] = { 70, 128, 116}, --Val'sharah -- BFA -- [81] = { 98, 84, 77}, --Silithus [1473]= {168, 136, 90}, --Chamber of Heart [1163]= { 89, 140, 123}, --Dazar'alor - The Great Seal [1164]= { 89, 140, 123}, --Dazar'alor - Hall of Chroniclers [1165]= { 89, 140, 123}, --Dazar'alor [862] = { 89, 140, 123}, --Zuldazar [864] = {187, 161, 134}, --Vol'dun [863] = {113, 173, 183}, --Nazmir [895] = { 89, 140, 123}, --Tiragarde Sound [1161]= { 89, 140, 123}, --Boralus [942] = {127, 164, 114}, --Stormsong [896] = {156, 165, 153}, --Drustvar [1462] = {16, 156, 192}, --Mechagon [1355] = {41, 74, 127}, --Nazjatar [249] = {180,149, 121}, --Uldum Normal [1527] = {180,149, 121}, --Uldum Assault [390] = {150, 117, 94}, --Eternal Blossoms Normal [1530] = {150, 117, 94}, --Eternal Blossoms Assault --{105, 71, 156} ["NZ"] = {105, 71, 156}, --During Assault: N'Zoth Purple Skybox [1580] = {105, 71, 156}, --Ny'alotha - Vision of Destiny [1581] = {105, 71, 156}, --Ny'alotha - Annex of Prophecy [1582] = {105, 71, 156}, --Ny'alotha - Ny'alotha [1590] = {105, 71, 156}, --Ny'alotha - The Hive [1591] = {105, 71, 156}, --Ny'alotha - Terrace of Desolation [1592] = {105, 71, 156}, --Ny'alotha - The Ritual Chamber [1593] = {105, 71, 156}, --Ny'alotha - Twilight Landing [1594] = {105, 71, 156}, --Ny'alotha - Maw of Gor'ma [1595] = {105, 71, 156}, --Ny'alotha - Warren of Decay [1596] = {105, 71, 156}, --Ny'alotha - Chamber of Rebirth [1597] = {105, 71, 156}, --Ny'alotha - Locus of Infinite Truths --Allied Race Starting Zone-- [124] = {87, 56, 132}, --DK [1186] = {117, 26, 22}, --Dark Iron [971] = {65, 57, 124}, --Void Elf --Class Hall [625] = { 42, 63, 79}, --Dalaran, Broken Isles Halls of Shadow [626] = { 42, 63, 79}, --Hall of Shadow [715] = {149, 180, 146}, --Emerald Dreamway [747] = { 70, 128, 116}, --The Dreamgrove --Frequently Visited [198] = {78, 78, 78}, --Hyjal }; NarciThemeUtil = {}; NarciThemeUtil.colorIndex = 0; function NarciThemeUtil:GetColorTable() local R, G, B = unpack(mapColors[self.colorIndex]); return {R/255, G/255, B/255} end function NarciThemeUtil:GetColor() local R, G, B = unpack(mapColors[self.colorIndex]); return R/255, G/255, B/255 end function NarciThemeUtil:GetColorIndex() return self.colorIndex end function NarciThemeUtil:SetColorIndex(index) if index and mapColors[index] then self.colorIndex = index else self.colorIndex = 0; end return self:GetColorTable() end ---------------------------------------------------------------------- local function NarciAPI_ConvertHexColorToRGB(hexColor, includeHex) local r = tonumber(strsub(hexColor, 1, 2), 16) / 255; local g = tonumber(strsub(hexColor, 3, 4), 16) / 255; local b = tonumber(strsub(hexColor, 5, 6), 16) / 255; if includeHex then return {r, g, b, hexColor}; else return {r, g, b}; end end local function RGB2HSV(r, g, b) local Cmax = max(r, g, b); local Cmin = min(r, g, b); local dif = Cmax - Cmin; local Hue = 0; local Brightness = floor(100*(Cmax / 255) + 0.5)/100; local Stauration = 0; if Cmax ~= 0 then Stauration = floor(100*(dif / Cmax)+0.5)/100; end; if dif ~= 0 then if r == Cmax and g >= b then Hue = (g - b) / dif + 0; elseif r == Cmax and g < b then Hue = (g - b) / dif + 6; elseif g == Cmax then Hue = (b - r) / dif + 2; elseif b == Cmax then Hue = (r - g) / dif + 4; end end return floor(60*Hue + 0.5), Stauration, Brightness end local function RGBRatio2HSV(r, g, b) return RGB2HSV(255 * r, 255 * g, 255 * b) end local function HSV2RGB(h, s, v) local Cmax = 255 * v; local Cmin = Cmax * (1 - s); local i = floor(h / 60); local dif = h % 60; local Cmid = (Cmax - Cmin) * dif / 60; local r, g, b; if i == 0 or i == 6 then r, g, b = Cmax, Cmin + Cmid, Cmin; elseif i == 1 then r, g, b = Cmax - Cmid, Cmax, Cmin; elseif i == 2 then r, g, b = Cmin, Cmax, Cmin + Cmid; elseif i == 3 then r, g, b = Cmin, Cmax - Cmid, Cmax; elseif i == 4 then r, g, b = Cmin + Cmid, Cmin, Cmax; else r, g, b = Cmax, Cmin, Cmax - Cmid; end r, g, b = floor(r + 0.5)/255, floor(g + 0.5)/255, floor(b + 0.5)/255; return r, g, b end NarciAPI.ConvertHexColorToRGB = NarciAPI_ConvertHexColorToRGB; NarciAPI.RGB2HSV = RGB2HSV; NarciAPI.RGBRatio2HSV = RGBRatio2HSV; NarciAPI.HSV2RGB = HSV2RGB; Narci_FontColor = { ["Brown"] = {0.85098, 0.80392, 0.70588, "|cffd9cdb4"}, ["DarkGrey"] = {0.42, 0.42, 0.42, "|cff6b6b6b"}, ["LightGrey"] = {0.72, 0.72, 0.72, "|cffb8b8b8"}, ["White"] = {0.88, 0.88, 0.88, "|cffe0e0e0"}, ["Good"] = {0.4862, 0.7725, 0.4627, "|cff7cc576"}, ["Bad"] = {1, 0.3137, 0.3137, 0.3137, "|cffff5050"}, ["Corrupt"] = {0.584, 0.428, 0.82, "|cff946dd1"}, }; local CustomQualityColors= { [0] = "9d9d9d", --Poor [1] = "ffffff", --Common [2] = "1eff00", --Uncommon [3] = "699eff", --Rare 0070dd 699eff [4] = "b953ff", --Epic a335ee [5] = "ff8000", --Legend [6] = "e6cc80", --Artifact [7] = "00ccff", --Heirloom [8] = "00ccff", [9] = "ffffff", }; for index, hex in pairs(CustomQualityColors) do CustomQualityColors[index] = NarciAPI_ConvertHexColorToRGB(hex, true); end local function GetCustomQualityColor(itemQuality) if (not itemQuality) or (not CustomQualityColors[itemQuality]) then itemQuality = 1; end return CustomQualityColors[itemQuality][1], CustomQualityColors[itemQuality][2], CustomQualityColors[itemQuality][3]; end NarciAPI.GetItemQualityColor = GetCustomQualityColor; local function GetCustomQualityColorByItemID(itemID) local itemQuality = C_Item.GetItemQualityByID(itemID); return GetCustomQualityColor(itemQuality); end NarciAPI.GetItemQualityColorByItemID = GetCustomQualityColorByItemID; local function GetCustomQualityHexColor(itemQuality) if (not itemQuality) or (not CustomQualityColors[itemQuality]) then itemQuality = 1; end return CustomQualityColors[itemQuality][4] end NarciAPI.GetItemQualityHexColor = GetCustomQualityHexColor; NarciAPI.GetItemQualityColorTable = function() local newTable = {}; for k, v in pairs(CustomQualityColors) do newTable[k] = v; end return newTable; end -------------------- ------Item API------ -------------------- local function GetItemEnchantID(itemLink) if itemLink then local _, _, _, linkType, linkID, enchantID = strsplit(":|H", itemLink); return tonumber(enchantID) or 0; else return 0 end end NarciAPI.GetItemEnchantID = GetItemEnchantID; local PrimaryStatsList = { [LE_UNIT_STAT_STRENGTH] = NARCI_STAT_STRENGTH, [LE_UNIT_STAT_AGILITY] = NARCI_STAT_AGILITY, [LE_UNIT_STAT_INTELLECT] = NARCI_STAT_INTELLECT, }; local function NarciAPI_GetPrimaryStats() --Return name and value local currentSpec = GetSpecialization() or 1; local _, _, _, _, _, primaryStat = GetSpecializationInfo(currentSpec); primaryStat = primaryStat or 1; local value = UnitStat("player", primaryStat); local name = PrimaryStatsList[primaryStat]; return name, value; end NarciAPI.GetPrimaryStats = NarciAPI_GetPrimaryStats; local GemInfo = Narci.GemData; local EnchantInfo = Narci.EnchantData; local DoesItemExist = C_Item.DoesItemExist; local GetCurrentItemLevel = C_Item.GetCurrentItemLevel; local GetItemLink = C_Item.GetItemLink local GetItemStats = C_Item.GetItemStats; local GetItemGem = C_Item.GetItemGem; function NarciAPI_GetItemStats(itemLocation) local statsTable = {}; statsTable.gems = 0; if not itemLocation or not DoesItemExist(itemLocation) then statsTable.prim = 0; statsTable.stamina = 0; statsTable.crit = 0; statsTable.haste = 0; statsTable.mastery = 0; statsTable.versatility = 0; statsTable.corruption = 0; statsTable.GemIcon = ""; statsTable.GemPos = ""; statsTable.EnchantPos = ""; statsTable.EnchantSpellID = nil; statsTable.ilvl = 0; return statsTable; end local ItemLevel = GetCurrentItemLevel(itemLocation) local itemLink = GetItemLink(itemLocation) local stats = GetItemStats(itemLink) or {}; local prim = stats["ITEM_MOD_AGILITY_SHORT"] or stats["ITEM_MOD_STRENGTH_SHORT"] or stats["ITEM_MOD_INTELLECT_SHORT"] or 0; local stamina = stats["ITEM_MOD_STAMINA_SHORT"] or 0; local crit = stats["ITEM_MOD_CRIT_RATING_SHORT"] or 0; local haste = stats["ITEM_MOD_HASTE_RATING_SHORT"] or 0; local mastery = stats["ITEM_MOD_MASTERY_RATING_SHORT"] or 0; local versatility = stats["ITEM_MOD_VERSATILITY"] or 0; local corruption = stats["ITEM_MOD_CORRUPTION"] or 0; statsTable.prim = prim; statsTable.stamina = stamina; statsTable.crit = crit; statsTable.haste = haste; statsTable.mastery = mastery; statsTable.versatility = versatility; statsTable.corruption = corruption; statsTable.ilvl = ItemLevel; --Calculate bonus from Gems and Enchants-- local gemIndex = 1; --BFA 1 gem for each item. local gemName, gemLink = GetItemGem(itemLink, gemIndex); if gemLink then local gemID = GetItemInfoInstant(gemLink); local _, _, _, _, GemIcon, _, itemSubClassID = GetItemInfoInstant(gemLink); statsTable.GemIcon = GemIcon statsTable.gems = 1; if GemInfo[gemID] then local info = GemInfo[gemID] statsTable.GemPos = GemInfo[1]; if info[1] == "crit" then statsTable.crit = statsTable.crit + info[2]; elseif info[1] == "haste" then statsTable.haste = statsTable.haste + info[2]; elseif info[1] == "mastery" then statsTable.mastery = statsTable.mastery + info[2]; elseif info[1] == "versatility" then statsTable.versatility = statsTable.versatility + info[2]; elseif info[1] == "AGI" or info[1] == "STR" or info[1] == "INT" then statsTable.prim = statsTable.prim + info[2]; statsTable.GemPos = "prim"; end end end local enchantID = GetItemEnchantID(itemLink); if enchantID ~= 0 and EnchantInfo[enchantID] then local data = EnchantInfo[enchantID] statsTable.EnchantPos = data[1]; if data[1] == "crit" then statsTable.crit = statsTable.crit + data[2]; elseif data[1] == "haste" then statsTable.haste = statsTable.haste + data[2]; elseif data[1] == "mastery" then statsTable.mastery = statsTable.mastery + data[2]; elseif data[1] == "versatility" then statsTable.versatility = statsTable.versatility + data[2]; elseif data[1] == "AGI" or data[1] == "STR" or data[1] == "INT" then statsTable.prim = statsTable.prim + data[2]; statsTable.EnchantPos = "prim"; elseif data[1] == "stamina" then statsTable.stamina = statsTable.stamina + data[2]; statsTable.EnchantPos = "stamina"; end statsTable.EnchantSpellID = data[3]; end return statsTable; end function NarciAPI_GetItemStatsFromSlot(slotID) local itemLocation = ItemLocation:CreateFromEquipmentSlot(slotID); local itemLink = C_Item.GetItemLink(itemLocation) return GetItemStats(itemLink); end do local GetContainerNumSlots = (C_Container and C_Container.GetContainerNumSlots) or GetContainerNumSlots; local GetContainerItemID = (C_Container and C_Container.GetContainerItemID) or GetContainerItemID; local GetContainerItemLink = (C_Container and C_Container.GetContainerItemLink) or GetContainerItemLink; local GetInventoryItemID = GetInventoryItemID; local GetItemCount = C_Item.GetItemCount; local function GetItemBagPosition(itemID, findHighestItemLevel) if findHighestItemLevel then local topLevel = -1; local level; local GetDetailedItemLevelInfo = GetDetailedItemLevelInfo; local id1, id2; for bagID = 0, (NUM_BAG_SLOTS or 4) do for slotID = 1, GetContainerNumSlots(bagID) do if(GetContainerItemID(bagID, slotID) == itemID) then level = GetDetailedItemLevelInfo( GetContainerItemLink(bagID, slotID) ) or 0; if level > topLevel then id1, id2 = bagID, slotID; end end end end return id1, id2; else for bagID = 0, (NUM_BAG_SLOTS or 4) do for slotID = 1, GetContainerNumSlots(bagID) do if(GetContainerItemID(bagID, slotID) == itemID) then return bagID, slotID end end end end end NarciAPI.GetItemBagPosition = GetItemBagPosition; local function GetItemPositionByItemID(itemID) local count = GetItemCount(itemID); if count and count > 0 then local id; for slotID = 1, 19 do id = GetInventoryItemID("player", slotID); if id and id == itemID then return "inventory", slotID end end local bagID, slotID = GetItemBagPosition(itemID); if bagID then return "container", bagID, slotID end end end NarciAPI.GetItemPositionByItemID = GetItemPositionByItemID; end -------------------- ---Formating API---- -------------------- local function NarciAPI_FormatLargeNumbers(value) value = tonumber(value) or 0; local formatedNumber = "" if value >= 1000 and value < 1000000 then formatedNumber = strsub(value, 1, -4) .. "," .. strsub(value, -3); elseif value >= 1000000 and value < 1000000000 then formatedNumber = strsub(value, 1, -7) .. "," .. strsub(value, -6, -4) .. "," .. strsub(value, -3); else formatedNumber = tostring(value) end return formatedNumber end NarciAPI.FormatLargeNumbers = NarciAPI_FormatLargeNumbers; local RemoveTextBeforeColon; if TEXT_LOCALE == "zhCN" or TEXT_LOCALE == "zhTW" then function RemoveTextBeforeColon(text) if find(text, ": ") then return match(text, ": (.+)"); elseif find(text, ":") then return match(text, ":(.+)"); else return text end end else function RemoveTextBeforeColon(text) if find(text, ":") then text = match(text, ":%s*(.+)"); end if find(text, "- ") then text = match(text, "- (.+)"); end return text --return string.gsub(text, "^.+[:-]%s*", ""); --May not working on Russian? end end NarciAPI.RemoveTextBeforeColon = RemoveTextBeforeColon; -------------------- ---Fade Frame API--- -------------------- local function SetFade_finishedFunc(frame) if frame.fadeInfo.mode == "OUT" then frame:Hide(); elseif frame.fadeInfo.mode == "IN" then frame:Show(); end end function NarciAPI_FadeFrame(frame, time, mode) if mode == "IN" then UIFrameFadeIn(frame, time, frame:GetAlpha(), 1); elseif mode == "OUT" then if not frame:IsShown() then return; end UIFrameFadeOut(frame, time, frame:GetAlpha(), 0); elseif mode == "Forced_IN" then UIFrameFadeIn(frame, time, 0, 1); elseif mode == "Forced_OUT" then UIFrameFadeOut(frame, time, 1, 0); end if not frame.fadeInfo then return; end frame.fadeInfo.finishedArg1 = frame; frame.fadeInfo.finishedFunc = SetFade_finishedFunc end ------------------------------------------------------------------ -------------------- ---UI Element API--- -------------------- NarciUIMixin = {}; function NarciUIMixin:Highlight(state) if state then self.Border.Highlight:SetAlpha(1); self.Border.Normal:SetAlpha(0); else self.Border.Highlight:SetAlpha(0); self.Border.Normal:SetAlpha(1); end end function NarciUIMixin:SetColor(r, g, b) if self.Color then self.Color:SetColorTexture(r/255, g/255, b/255); end end local SCREEN_WIDTH, SCREEN_HEIGHT = GetPhysicalScreenSize(); --Assume players don't change screen resolution (triggers DISPLAY_SIZE_CHANGED) local function GetPixelForWidget(widget, pixelSize) local scale = widget:GetEffectiveScale(); if pixelSize then return pixelSize * (768/SCREEN_HEIGHT)/scale else return (768/SCREEN_HEIGHT)/scale end end NarciAPI.GetPixelForWidget = GetPixelForWidget; local function GetPixelByScale(scale, pixelSize) if pixelSize then return pixelSize * (768/SCREEN_HEIGHT)/scale else return (768/SCREEN_HEIGHT)/scale end end NarciAPI.GetPixelByScale = GetPixelByScale; local function GetTexturePixelSize(texture) local scale = texture:GetEffectiveScale(); local w, h = texture:GetSize(); local pixel = (768/SCREEN_HEIGHT)/scale; return w/pixel, h/pixel end NarciAPI.GetTexturePixelSize = GetTexturePixelSize; local function GetBestSizeForPixel(size, pixel) return floor(size/pixel + 0.5) * pixel end NarciAPI.GetBestSizeForPixel = GetBestSizeForPixel; local function GetObjectScreenSize(objectSize, scale) local _, screenHeight = GetPhysicalScreenSize(); if not scale then scale = UIParent:GetEffectiveScale(); end return objectSize / ((768/SCREEN_HEIGHT)/scale) end NarciAPI.GetObjectScreenSize = GetObjectScreenSize; function NarciAPI_OptimizeBorderThickness(self) if not self.HasOptimized then local point, relativeTo, relativePoint, xOfs, yOfs = self:GetPoint() local uiScale = self:GetEffectiveScale(); local rate = (768/SCREEN_HEIGHT)/uiScale; local borderWeight = 2.0; local weight = borderWeight * rate; local weight2 = weight * math.sqrt(2); self.Border:SetPoint("TOPLEFT", self, "TOPLEFT", weight, -weight) self.Border:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", -weight, weight) if self.ThumbBorder then self.ThumbBorder:SetPoint("TOPLEFT", self.VirtualThumb, -weight2, weight2) self.ThumbBorder:SetPoint("BOTTOMRIGHT", self.VirtualThumb,weight2, -weight2) end if self.Marks then for i=1, #self.Marks do self.Marks[i]:SetWidth(weight); end end self.HasOptimized = true; end end function NarciAPI_SliderWithSteps_OnLoad(self) self.oldValue = -1208; self.Marks = {}; local width = self:GetWidth(); local step = self:GetValueStep(); local sliderMin, sliderMax = self:GetMinMaxValues(); local range = sliderMax - sliderMin; local num_Gap = floor((range / step) + 0.5); if num_Gap == 0 then return; end; local tex; local markOffset = 5; width = width - 2*markOffset; local pixel = GetPixelForWidget(self); for i = 1, (num_Gap + 1) do tex = self:CreateTexture(nil, "BACKGROUND", nil, 1); tex:SetSize(2*pixel, 20*pixel); tex:SetColorTexture(0.25, 0.25, 0.25); tex:SetPoint("CENTER", self, "LEFT", markOffset + (i-1)*width/num_Gap, 0); tinsert(self.Marks, tex); end --self.VirtualThumb:SetTexture("Interface/AddOns/Narcissus/Art/BasicShapes/Diamond", nil, nil, "TRILINEAR"); end -----Smooth Scroll----- local function SmoothScrollContainer_OnUpdate(self, elapsed) local value = self.scrollBar:GetValue(); local step = max(abs(value - self.endValue)*(self.speedRatio)*(elapsed*60) , self.minOffset); --if the step (Δy) is too small, the fontstring will jitter. --Consider elapsed -- scroll duration should be constant regardless of FPS local remainedStep; if ( self.delta == 1 ) then --Up remainedStep = min(self.endValue - value, 0); if - remainedStep <= ( self.minOffset) then self:Hide(); self.scrollBar:SetValue(min(self.maxVal, self.endValue)); if self.onScrollFinishedFunc then self.onScrollFinishedFunc(); end else self.scrollBar:SetValue(max(0, value - step)); end else remainedStep = max(self.endValue - value, 0); if remainedStep <= ( self.minOffset) then self:Hide(); self.scrollBar:SetValue(min(self.maxVal, self.endValue)); if self.onScrollFinishedFunc then self.onScrollFinishedFunc(); end else self.scrollBar:SetValue(min(self.maxVal, value + step)); end end end local function NarciAPI_SmoothScroll_OnMouseWheel(self, delta, stepSize) if ( not self.scrollBar:IsVisible() ) then if self.parentScrollFunc then self.parentScrollFunc(delta); else return; end end local ScrollContainer = self.SmoothScrollContainer; stepSize = stepSize or self.stepSize or self.buttonHeight; ScrollContainer.stepSize = stepSize; ScrollContainer.maxVal = self.range; --self.scrollBar:SetValueStep(0.01); ScrollContainer.delta = delta; local current = self.scrollBar:GetValue(); if not((current <= 0.1 and delta > 0) or (current >= self.range - 0.1 and delta < 0 )) then ScrollContainer:Show() else return; end local deltaRatio = ScrollContainer.deltaRatio or 1; if IsShiftKeyDown() then deltaRatio = 2 * deltaRatio; end local endValue = floor( (100 * min(max(0, ScrollContainer.endValue - delta*deltaRatio*self.buttonHeight), self.range) + 0.5)/100 ); ScrollContainer.endValue = endValue; if self.positionFunc then local isTop = endValue <= 0.1; local isBottom = endValue >= self.range - 1; self.positionFunc(endValue, delta, self.scrollBar, isTop, isBottom); end end local function SetScrollRange(scrollFrame, range) range = max(range, 0); scrollFrame.scrollBar:SetMinMaxValues(0, range); scrollFrame.scrollBar:SetShown(range ~= 0); scrollFrame.range = range; end local SmoothScrollFrameMixin = {}; function SmoothScrollFrameMixin:GetEndPosition() return self.SmoothScrollContainer.endValue; end function SmoothScrollFrameMixin:SnapToEndPosition() local offset = self:GetEndPosition(); self.SmoothScrollContainer:Hide(); self.scrollBar:SetValue(offset); end function SmoothScrollFrameMixin:SnapToOffset(offset) if self.range and offset > self.range then offset = self.range; elseif offset < 0 then offset = 0; end self.SmoothScrollContainer:Hide(); self.scrollBar:SetValue(offset); self.SmoothScrollContainer.endValue = offset; end function NarciAPI_SmoothScroll_Initialization(scrollFrame, updatedList, updateFunc, deltaRatio, speedRatio, minOffset, positionFunc, onScrollFinishedFunc) if updateFunc then scrollFrame.update = updateFunc; end if positionFunc then scrollFrame.positionFunc = positionFunc; end if updatedList then scrollFrame.updatedList = updatedList; end local parentName = scrollFrame:GetName(); local frameName = parentName and (parentName .. "SmoothScrollContainer") or nil; local SmoothScrollContainer = CreateFrame("Frame", frameName, scrollFrame); SmoothScrollContainer:Hide(); --local scale = match(GetCVar( "gxWindowedResolution" ), "%d+x(%d+)" ); local uiScale = scrollFrame:GetEffectiveScale(); local pixel = (768/SCREEN_HEIGHT)/uiScale; local scrollBar = scrollFrame.scrollBar; scrollBar:SetValue(0); scrollBar:SetValueStep(0.001); SmoothScrollContainer.stepSize = 0; SmoothScrollContainer.delta = 0; SmoothScrollContainer.animationDuration = 0; SmoothScrollContainer.endValue = 0; SmoothScrollContainer.maxVal = 0; SmoothScrollContainer.deltaRatio = deltaRatio or 1; SmoothScrollContainer.speedRatio = speedRatio or 0.5; SmoothScrollContainer.minOffset = minOffset or pixel; SmoothScrollContainer.scrollBar = scrollFrame.scrollBar; SmoothScrollContainer:SetScript("OnUpdate", SmoothScrollContainer_OnUpdate); SmoothScrollContainer:SetScript("OnShow", function(self) self.endValue = self:GetParent().scrollBar:GetValue(); end); SmoothScrollContainer:SetScript("OnHide", function(self) self:Hide(); end); scrollFrame.SmoothScrollContainer = SmoothScrollContainer; scrollFrame:SetScript("OnMouseWheel", NarciAPI_SmoothScroll_OnMouseWheel); --a position-related function if onScrollFinishedFunc then SmoothScrollContainer.onScrollFinishedFunc = onScrollFinishedFunc; end scrollFrame.SetScrollRange = SetScrollRange; for k, v in pairs(SmoothScrollFrameMixin) do scrollFrame[k] = v; end end function NarciAPI_ApplySmoothScrollToScrollFrame(scrollFrame, deltaRatio, speedRatio, positionFunc, buttonHeight, range, parentScrollFunc, onScrollFinishedFunc) scrollFrame.buttonHeight = buttonHeight or floor(scrollFrame:GetHeight() + 0.5); scrollFrame.range = range or 0; scrollFrame.scrollBar:SetMinMaxValues(0, range or 0) scrollFrame.scrollBar:SetScript("OnValueChanged", function(self, value) scrollFrame:SetVerticalScroll(value); if self.onValueChangedFunc then self.onValueChangedFunc(value); end end) NarciAPI_SmoothScroll_Initialization(scrollFrame, nil, nil, deltaRatio, speedRatio, nil, positionFunc, onScrollFinishedFunc); scrollFrame.parentScrollFunc = parentScrollFunc; end function NarciAPI_ApplySmoothScrollToBlizzardUI(scrollFrame, deltaRatio, speedRatio, positionFunc) NarciAPI_SmoothScroll_Initialization(scrollFrame, nil, nil, deltaRatio, speedRatio, nil, positionFunc); end -----Create A List of Button---- --[[ function NarciAPI_BuildButtonList(self, buttonTemplate, buttonNameTable, initialOffsetX, initialOffsetY, initialPoint, initialRelative, offsetX, offsetY, point, relativePoint) local button, buttonHeight, buttons, numButtons; local parentName = self:GetName(); local buttonName = parentName and (parentName .. "Button") or nil; initialPoint = initialPoint or "TOPLEFT"; initialRelative = initialRelative or "TOPLEFT"; initialOffsetX = initialOffsetX or 0; initialOffsetY = initialOffsetY or 0; point = point or "TOPLEFT"; relativePoint = relativePoint or "BOTTOMLEFT"; offsetX = offsetX or 0; offsetY = offsetY or 0; if ( self.buttons ) then buttons = self.buttons; buttonHeight = buttons[1]:GetHeight(); else button = CreateFrame("BUTTON", buttonName and (buttonName .. 1) or nil, self, buttonTemplate); buttonHeight = button:GetHeight(); button:SetPoint(initialPoint, self, initialRelative, initialOffsetX, initialOffsetY); button:SetID(0); buttons = {} button.Name:SetText(buttonNameTable[1]) tinsert(buttons, button); end local numButtons = #buttonNameTable; for i = 2, numButtons do button = CreateFrame("BUTTON", buttonName and (buttonName .. i) or nil, self, buttonTemplate); button:SetPoint(point, buttons[i-1], relativePoint, offsetX, offsetY); button:SetID(i-1); button.Name:SetText(buttonNameTable[i]) tinsert(buttons, button); end self.buttons = buttons; end --]] -----Language Adaptor----- local function LanguageDetector(str) local len = string.len(str) local i = 1 while i <= len do local c = string.byte(str, i) local shift = 1 if (c > 0 and c <= 127)then shift = 1 elseif c == 195 then shift = 2 --Latin/Greek elseif (c >= 208 and c <=211) then shift = 2 return "RU" --RU included elseif (c >= 224 and c <= 227) then shift = 3 --JP return "JP" elseif (c >= 228 and c <= 233) then shift = 3 --CN return "CN" elseif (c >= 234 and c <= 237) then shift = 3 --KR return "KR" elseif (c >= 240 and c <= 244) then shift = 4 --Unknown invalid end i = i + shift end return "RM" end local function GetFirstLetterLanguage(str) local c = string.byte(str, 1); if (c > 0 and c <= 127)then return "RM" elseif c == 195 then return "RM" --Latin/Greek elseif (c >= 208 and c <=211) then return "RU" elseif (c >= 224 and c <= 227) then return "JP" elseif (c >= 228 and c <= 233) then return "CN" elseif (c >= 234 and c <= 237) then return "KR" elseif (c >= 240 and c <= 244) then return "CN" --Unknown invalid end end NarciAPI.LanguageDetector = LanguageDetector; --[[ function LDTest(string) local str = string local lenInByte = #str for i=1,lenInByte do local char = strsub(str, i,i) local curByte = string.byte(str, i) print(char.." "..curByte) end return "roman" end local Eng = "abcdefghijklmnopqrstuvwxyz" --abcdefghijklmnopqrstuvwxyz Z~90 z~122 1-1 local DE = "äöüß" --195 1-2 local CN = "乀氺" --228 229 230 233 HEX E4-E9 Hexadecimal UTF-8 CJK local KR = "제" --237 236 235 234 1-3 EB-ED local RU = "ѱӧ" --D0400-D04C0 208 209 210 211 1-2 local FR = "ÀÃÇÊÉÕàãçêõáéíóúà" --1-2 195 C3 -PR local JP = "ひらがな" --1-3 227 E3 Kana --LDTest("繁體繁体") --local language = LanguageDetector("繁體中文") --print("Str is: "..language) --]] local PlayerNameFont = { ["CN"] = "Interface\\AddOns\\Narcissus\\Font\\NotoSansCJKsc-Medium.otf", ["RM"] = "Interface\\AddOns\\Narcissus\\Font\\SemplicitaPro-Semibold.otf", ["RU"] = "Interface\\AddOns\\Narcissus\\Font\\NotoSans-Medium.ttf", ["KR"] = "Interface\\AddOns\\Narcissus\\Font\\NotoSansCJKsc-Medium.otf", ["JP"] = "Interface\\AddOns\\Narcissus\\Font\\NotoSansCJKsc-Medium.otf", } local EditBoxFont = { ["CN"] = {"Interface\\AddOns\\Narcissus\\Font\\NotoSansCJKsc-Medium.otf", 8}, ["RM"] = {"Interface\\AddOns\\Narcissus\\Font\\SourceSansPro-Semibold.ttf", 9}, ["RU"] = {"Interface\\AddOns\\Narcissus\\Font\\NotoSans-Medium.ttf", 8}, ["KR"] = {"Interface\\AddOns\\Narcissus\\Font\\NotoSansCJKsc-Medium.otf", 8}, ["JP"] = {"Interface\\AddOns\\Narcissus\\Font\\NotoSansCJKsc-Medium.otf", 8}, } local NormalFont12 = { ["CN"] = {"Interface\\AddOns\\Narcissus\\Font\\NotoSansCJKsc-Medium.otf", 11}, ["RM"] = {"Interface\\AddOns\\Narcissus\\Font\\SourceSansPro-Semibold.ttf", 12}, ["RU"] = {"Interface\\AddOns\\Narcissus\\Font\\NotoSans-Medium.ttf", 11}, ["KR"] = {"Interface\\AddOns\\Narcissus\\Font\\NotoSansCJKsc-Medium.otf", 11}, ["JP"] = {"Interface\\AddOns\\Narcissus\\Font\\NotoSansCJKsc-Medium.otf", 11}, } local ActorNameFont = { ["CN"] = {"Interface\\AddOns\\Narcissus\\Font\\NotoSansCJKsc-Medium.otf", 8}, ["RM"] = {"Interface\\AddOns\\Narcissus\\Font\\SourceSansPro-Semibold.ttf", 9}, ["RU"] = {"Interface\\AddOns\\Narcissus\\Font\\NotoSans-Medium.ttf", 8}, ["KR"] = {"Interface\\AddOns\\Narcissus\\Font\\NotoSansCJKsc-Medium.otf", 8}, ["JP"] = {"Interface\\AddOns\\Narcissus\\Font\\NotoSansCJKsc-Medium.otf", 8}, } local function SmartSetActorName(fontstring, text) --Automatically apply different font based on given text languange. Change text color after this step. if not fontstring then return; end; fontstring:SetText(text); local language = LanguageDetector(text); if language and ActorNameFont[language] then fontstring:SetFont(ActorNameFont[language][1] , ActorNameFont[language][2], ""); end end local function SmartFontType(self, fontTable) local str = self:GetText(); local language = LanguageDetector(str); --print(str.." Language is: "..Language); local height = self:GetHeight(); if language and fontTable[language] then self:SetFont(fontTable[language] , height, ""); end end local function SmartEditBoxFont(self, extraHeight) local str = self:GetText(); local language = LanguageDetector(str); if language and EditBoxFont[language] then local height = extraHeight or 0; self:SetFont(EditBoxFont[language][1] , EditBoxFont[language][2] + height, ""); end end local function NarciAPI_SmartFontType(fontString) SmartFontType(fontString, PlayerNameFont); end local function SmartSetName(fontString, str) local language = LanguageDetector(str); if language and NormalFont12[language] then fontString:SetFont(NormalFont12[language][1], NormalFont12[language][2], ""); end fontString:SetText(str); end NarciAPI.SmartSetActorName = SmartSetActorName; NarciAPI.SmartFontType = NarciAPI_SmartFontType; NarciAPI.SmartSetName = SmartSetName; function NarciAPI_SmartEditBoxType(self, isUserInput, extraHeight) SmartEditBoxFont(self, extraHeight); end --[[ function NarciAPI_EditBox_OnLanguageChanged(self, language) SmartEditBoxFont(self); end --]] -----Filter Shared Functions----- function NarciAPI_LetterboxAnimation(command) local frame = Narci_FullScreenMask; frame:StopAnimating(); if command == "IN" then frame:Show(); frame.BottomMask.animIn:Play(); frame.TopMask.animIn:Play(); elseif command == "OUT" then frame.BottomMask.animOut:Play(); frame.TopMask.animOut:Play(); else if NarcissusDB.LetterboxEffect then frame:Show(); frame.BottomMask.animIn:Play(); frame.TopMask.animIn:Play(); else frame:Hide(); end end end -----Format Normalization----- local function SplitTooltipByLineBreak(str) local str1, _, str2 = strsplit("\n", str); return str1 or "", str2 or ""; end NARCI_CRIT_TOOLTIP, NARCI_CRIT_TOOLTIP_FORMAT = SplitTooltipByLineBreak(CR_CRIT_TOOLTIP); _, NARCI_HASTE_TOOLTIP_FORMAT = SplitTooltipByLineBreak(STAT_HASTE_BASE_TOOLTIP); NARCI_VERSATILITY_TOOLTIP_FORMAT_1, NARCI_VERSATILITY_TOOLTIP_FORMAT_2 = SplitTooltipByLineBreak(CR_VERSATILITY_TOOLTIP); -----Delayed Tooltip----- local DelayedTP = CreateFrame("Frame"); DelayedTP:Hide(); DelayedTP:SetScript("OnShow", function(self) self.t = 0; --Total time after ShowDelayedTooltip gets called --self.ScanTime = 0; --Cursor scanning time --self.CursorX, self.CursorY = GetCursorPosition(); --Cursor position end) DelayedTP:SetScript("OnHide", function(self) self.t = 0; --self.ScanTime = 0; end) DelayedTP:SetScript("OnUpdate", function(self, elapsed) self.t = self.t + elapsed; --self.ScanTime = self.ScanTime + elapsed; if self.t >= 0.6 then if self.focus and self.focus == GetMouseFocus() then NarciGameTooltip:ClearAllPoints(); NarciGameTooltip:SetPoint(self.point, self.relativeTo, self.relativePoint, self.ofsx, self.ofsy); FadeFrame(NarciGameTooltip, 0.15, 1, 0); self.focus = nil; end self:Hide(); end end) function NarciAPI_ShowDelayedTooltip(point, relativeTo, relativePoint, ofsx, ofsy) local tp = DelayedTP; tp:Hide(); if point then tp.focus = GetMouseFocus(); tp.point, tp.relativeTo, tp.relativePoint, tp.ofsx, tp.ofsy = point, relativeTo, relativePoint, ofsx, ofsy; tp:Show(); else tp.focus = nil; FadeFrame(NarciGameTooltip, 0, 0); end end -----Run Delayed Function----- local DelayedFunc = CreateFrame("Frame"); DelayedFunc:Hide(); DelayedFunc.delay = 0; DelayedFunc.t = 0; DelayedFunc:SetScript("OnHide", function(self) self.focus = nil; self.t = 0; end) DelayedFunc:SetScript("OnUpdate", function(self, elapsed) self.t = self.t + elapsed; if self.t >= self.delay then if self.focus == GetMouseFocus() then self.func(); end self:Hide(); end end) function NarciAPI_RunDelayedFunction(frame, delay, func) DelayedFunc:Hide(); if func and type(func) == "function" then delay = delay or 0; DelayedFunc.focus = frame; DelayedFunc.delay = delay; DelayedFunc.func = func; DelayedFunc:Show(); end end -----Alert Frame----- NarciAlertFrameMixin = {}; function NarciAlertFrameMixin:AddShakeAnimation(frame) if frame.animError then return; end; local ag = frame:CreateAnimationGroup(); local a1 = ag:CreateAnimation("Translation"); a1:SetOrder(1); a1:SetOffset(4, 0); a1:SetDuration(0.05); local a2 = ag:CreateAnimation("Translation"); a2:SetOrder(2); a2:SetOffset(-8, 0); a2:SetDuration(0.1); local a3 = ag:CreateAnimation("Translation"); a3:SetOrder(3); a3:SetOffset(8, 0); a3:SetDuration(0.1); local a4 = ag:CreateAnimation("Translation"); a4:SetOrder(4); a4:SetOffset(-4, 0); a4:SetDuration(0.05); ag:SetScript("OnPlay", function() PlaySound(138528); --Mechagon_HK8_Lockon end); frame.animError = ag; end function NarciAlertFrameMixin:SetAnchor(frame, offsetY, AddErrorAnimation) if frame.RegisterErrorEvent then frame:RegisterErrorEvent(); After(0.5, function() frame:UnregisterErrorEvent(); end) else frame:RegisterEvent("UI_ERROR_MESSAGE"); After(0.5, function() frame:UnregisterEvent("UI_ERROR_MESSAGE"); end) end self:Hide(); self:ClearAllPoints(); self:SetScale(Narci_Character:GetEffectiveScale()) offsetY = offsetY or 0 self:SetPoint("BOTTOM", frame, "TOP", 0, offsetY); self:SetFrameLevel(50); self.anchor = frame; if AddErrorAnimation then self:AddShakeAnimation(frame); end end function NarciAlertFrameMixin:AddMessage(msg, UseErrorAnimation) self.Text:SetText(msg); self:UpdateFrameSize(); FadeFrame(self, 0.2, 1); local anchorFrame = self.anchor; if anchorFrame then if anchorFrame.animError and UseErrorAnimation then anchorFrame.animError:Play(); end anchorFrame:UnregisterEvent("UI_ERROR_MESSAGE"); end end function NarciAlertFrameMixin:UpdateFrameSize() local textWidth = self.Text:GetWrappedWidth(); local textHeight = self.Text:GetHeight(); self:SetSize(textWidth + 24, textHeight + 24); end ------------------------ --Filled Bar Animation-- ------------------------ --Corruption Bar --[[ local cos = math.cos; local pi = math.pi; local function inOutSine(t, b, c, d) return -c / 2 * (cos(pi * t / d) - 1) + b end local FluidAnim = CreateFrame("Frame"); FluidAnim:Hide(); FluidAnim.d = 0.5; FluidAnim.d1 = 0.25; FluidAnim.d2 = 0.5; local function FluidLevel(self, elapsed) self.t = self.t + elapsed; local height = inOutSine(self.t, self.startHeight, self.endHeight - self.startHeight, self.d); if self.t >= self.d then height = self.endHeight; self:Hide(); end self.Fluid:SetHeight(height); end local function FluidUp(self, elapsed) self.t = self.t + elapsed; local height; if self.t <= self.d1 then height = inOutSine(self.t, self.startHeight, 84 - self.startHeight, self.d1); elseif self.t < self.d3 then if not self.colorChanged then self.colorChanged = true; self.Fluid:SetColorTexture(self.r, self.g, self.b); end height = inOutSine(self.t - self.d1, 0.01, self.endHeight, self.d2); else height = self.endHeight; self:Hide(); end self.Fluid:SetHeight(height); end local function FluidDown(self, elapsed) self.t = self.t + elapsed; local height; if self.t <= self.d1 then height = inOutSine(self.t, self.startHeight, 0.01 - self.startHeight, self.d1); elseif self.t < self.d3 then if not self.colorChanged then self.colorChanged = true; self.Fluid:SetColorTexture(self.r, self.g, self.b); end height = inOutSine(self.t - self.d1, 84, self.endHeight - 84, self.d2); else height = self.endHeight; self:Hide(); end self.Fluid:SetHeight(height); end FluidAnim:SetScript("OnShow", function(self) self.t = 0; self.colorChanged = false; end); function NarciAPI_SmoothFluid(bar, newHeight, newLevel, r, g, b) local FluidAnim = FluidAnim; FluidAnim:Hide(); FluidAnim.endHeight = newHeight; FluidAnim.Fluid = bar; FluidAnim.r, FluidAnim.g, FluidAnim.b = r, g, b; local oldLevel = FluidAnim.oldCorruptionLevel or newLevel; FluidAnim.oldCorruptionLevel = newLevel; local t1, t2; local h = FluidAnim.Fluid:GetHeight(); FluidAnim.startHeight = h; if newLevel == oldLevel then FluidAnim:SetScript("OnUpdate", FluidLevel); FluidAnim.d = max( abs(h - FluidAnim.endHeight) / 84 , 0.35); bar:SetColorTexture(r, g, b); elseif newLevel < oldLevel then FluidAnim:SetScript("OnUpdate", FluidDown); t1 = math.max(h / 84, 0); t2 = math.max((84 - FluidAnim.endHeight) / 84, 0.4); FluidAnim.d1 = t1 FluidAnim.d2 = t2 FluidAnim.d3 = t1 + t2; else FluidAnim:SetScript("OnUpdate", FluidUp); t1 = math.max((84 - h) / 84, 0); t2 = math.max(FluidAnim.endHeight / 84, 0.4); FluidAnim.d1 = t1 FluidAnim.d2 = t2 FluidAnim.d3 = t1 + t2; end After(0, function() FluidAnim:Show(); end) return t1 end local EyeballTexture = "Interface\\AddOns\\Narcissus\\ART\\Widgets\\CorruptionSystem\\Eyeball-Orange"; local CorruptionColor = "|cfff57f20"; local FluidColors = {0.847, 0.349, 0.145}; function NarciAPI_GetEyeballColor() return EyeballTexture, CorruptionColor, FluidColors[1], FluidColors[2], FluidColors[3]; end function NarciAPI_SetEyeballColor(index) if index == 4 then EyeballTexture = "Interface\\AddOns\\Narcissus\\ART\\Widgets\\CorruptionSystem\\Eyeball-Blue"; CorruptionColor = "|cff83c7e7"; FluidColors = {0.596, 0.73, 0.902}; elseif index == 2 then EyeballTexture = "Interface\\AddOns\\Narcissus\\ART\\Widgets\\CorruptionSystem\\Eyeball-Purple"; CorruptionColor = "|cfff019ff"; FluidColors = {0.87, 0.106, 0.949}; elseif index == 3 then EyeballTexture = "Interface\\AddOns\\Narcissus\\ART\\Widgets\\CorruptionSystem\\Eyeball-Green"; CorruptionColor = "|cff8cdacd"; FluidColors = {0.56, 0.855, 0.757}; else index = 1; EyeballTexture = "Interface\\AddOns\\Narcissus\\ART\\Widgets\\CorruptionSystem\\Eyeball-Orange"; CorruptionColor = "|cfff57f20"; FluidColors = {0.847, 0.349, 0.145}; end NarcissusDB.EyeColor = index; local Preview = Narci_EyeColorPreview; local ColorButtons = Preview:GetParent().ColorButtons; Preview:SetTexCoord(0.25*(index - 1), 0.25*index, 0, 1); for i = 1, #ColorButtons do --ColorButtons[i]Highlight(false); end --ColorButtons[index]:Highlight(true); Narci:SetItemLevel(); end --]] -------------------- --UI 3D Animation--- -------------------- Narci.AnimSequenceInfo = { ["Controller"] = { ["TotalFrames"] = 30, ["cX"] = 0.205078125, ["cY"] = 0.1171875, ["Column"] = 4, ["Row"] = 8, }, ["Heart"] = { ["TotalFrames"] = 28, ["cX"] = 0.25, ["cY"] = 0.140625, ["Column"] = 4, ["Row"] = 7, }, ["ActorPanel"] = { ["TotalFrames"] = 26, ["cX"] = 0.4296875, ["cY"] = 0.056640625, ["Column"] = 2, ["Row"] = 17, }, } function NarciAPI_PlayAnimationSequence(index, SequenceInfo, Texture) local Frames = SequenceInfo["TotalFrames"]; local cX, cY = SequenceInfo["cX"], SequenceInfo["cY"]; local col, row = SequenceInfo["Column"], SequenceInfo["Row"] if index > Frames or index < 1 then return false; end local n = math.modf((index -1)/ row) + 1; local m = index % row if m == 0 then m = row; end local left, right = (n-1)*cX, n*cX; local top, bottom = (m-1)*cY, m*cY; Texture:SetTexCoord(left, right, top, bottom); Texture:SetAlpha(1); Texture:Show(); return true; end -------------------- -----Play Voice----- -------------------- local ERROR_NOTARGET, ALERT_INCOMING; do local _, _, raceID = UnitRace("player"); local genderID = UnitSex("player") or 2; raceID = raceID or 1; genderID = genderID - 1; --(2→1) Male (3→2) Female if raceID == 25 or raceID == 26 then --Pandaren faction raceID = 24; elseif raceID == 52 or raceID == 70 then raceID = 52; end if raceID == 37 then --Mechagnome IGNORED_MOG_SLOT[8] = true; --feet IGNORED_MOG_SLOT[9] = true; --wrist IGNORED_MOG_SLOT[10] = true; --hands elseif raceID == 52 then --Dracthyr end local VOICE_BY_RACE = { --[raceID] = { [gender] = {Error_NoTarget, ALERT_INCOMING} } [1] = {[1] = {1906, 2669, }, [2] = {2030, 2681, }}, --1 Human [2] = {[1] = {2317, 2693, }, [2] = {2372, 2705, }}, --2 Orc [3] = {[1] = {1614, 2717, }, [2] = {1684, 2729, }}, --3 Dwarf [4] = {[1] = {56231, 56311, }, [2] = {56096, 56174, }}, --4 NE [5] = {[1] = {2085, 2765, }, [2] = {2205, 2777, }}, --5 UD [6] = {[1] = {2459, 2789, }, [2] = {2458, 2802, }}, --6 Tauren [7] = {[1] = {1741, 2827, }, [2] = {1796, 2839, }}, --7 Gnome [8] = {[1] = {1851, 2851, }, [2] = {1961, 2863, }}, --8 Troll [9] = {[1] = {19109, 19137, }, [2] = {19218, 19246}}, --9 Goblin [10] = {[1] = {9597, 9664, }, [2] = {9598, 9624, }}, --10 BloodElf [11] = {[1] = {9463, 9714, }, [2] = {9514, 9689, }}, --11 Goat [22] = {[1] = {18991, 19346, }, [2] = {18719, 19516, }}, --22 Worgen [24] = {[1] = {28846, 28924, }, [2] = {29899, 29812, }}, --24 Pandaren [27] = {[1] = {96356, 96383, }, [2] = {96288, 96315, }}, --27 Nightborne [28] = {[1] = {95931, 95844, }, [2] = {95510, 95543, }}, --28 Highmountain Tauren [29] = {[1] = {95636, 95665, }, [2] = {95806, 95857, }}, --29 Void Elf [30] = {[1] = {96220, 96247, }, [2] = {96152, 96179, }}, --30 Light-forged [31] = {[1] = {127289, 1273128, }, [2] = {126915, 126944, }}, --31 Zandalari [32] = {[1] = {127102, 127131, }, [2] = {127008, 127037, }}, --32 Kul'Tiran [34] = {[1] = {101933, 101962, }, [2] = {101859, 101888, }}, --36 Dark Iron Dwarf [35] = {[1] = {144073, 144111, }, [2] = {143981, 144019, }}, --35 Vulpera [36] = {[1] = {110370, 110399, }, [2] = {110295, 110324, }}, --36 Mag'har [37] = {[1] = {143863, 143892, }, [2] = {144223, 144275, }}, --37 Mechagnome!!!! [52] = {[1] = {212644, 212598, }, [2] = {212644, 212688, }}, --52 Dracthyr }; if VOICE_BY_RACE[raceID] then ERROR_NOTARGET = VOICE_BY_RACE[raceID][genderID][1]; ALERT_INCOMING = VOICE_BY_RACE[raceID][genderID][2]; end ERROR_NOTARGET = ERROR_NOTARGET or 2030; ALERT_INCOMING = ALERT_INCOMING or 2669; wipe(VOICE_BY_RACE); end function Narci:PlayVoice(name) if name == "ERROR" then PlaySound(ERROR_NOTARGET, "Dialog"); elseif name == "DANGER" then PlaySound(ALERT_INCOMING, "Dialog"); end end --Time --C_DateAndTime.GetCurrentCalendarTime local DEFAULT_ACTOR_INFO_ID = 1620; --438 Pre-DF local ActorIDByRace = { --local GenderID = UnitSex(unit); 2 Male 3 Female --[raceID] = {male actorID, female actorID, bustOffsetZ_M, bustOffsetZ_F}, [2] = {483, 483}, -- Orc bow [3] = {471, nil}, -- Dwarf [5] = {472, 487}, -- UD 0.9585 seems small [6] = {449, 484}, -- Tauren [7] = {450, 450}, -- Gnome [8] = {485, 486}, -- Troll 0.9414 too high? [9] = {476, 477}, -- Goblin [11] = {475, 501}, -- Goat [22] = {474, 500}, -- Worgen [24] = {473, 473}, -- Pandaren [28] = {490, 491}, -- Highmountain Tauren [30] = {488, 489}, -- Lightforged Draenei [31] = {492, 492}, -- Zandalari [32] = {494, 497}, -- Kul'Tiran [34] = {499, nil}, -- Dark Iron Dwarf [35] = {924, 923}, -- Vulpera [36] = {495, 498}, -- Mag'har [37] = {929, 931}, -- Mechagnome [52] = {1554, 1554}, -- Dracthyr [70] = {1554, 1554}, -- Dracthyr }; local ActorIDByModelFileID = { --/dump DressUpFrame.ModelScene:GetPlayerActor():GetModelFileID() [4207724] = 1653, --Dracthyr 1554 [4395382] = 1654, --Visage M Dracthyr-alt 1583 [4220488] = 1654, --Visage F [878772] = 1623, --Dwarf M --[950080] = Dwarf F [1890765] = 1644, --darkirondwarf-male --[1890763] = DarkIron F [1721003] = 1640, --kultiran-male [1886724] = 1642, --kultiran-female [2622502] = 1649, --mechagnome-male [2564806] = 1650, --mechagnome-female [1890761] = 1648, --vulpera-male [1890759] = 1647, --vulpera-female [1630218] = 1637, --highmountaintauren-male [1630402] = 1638, --highmountaintauren-female [900914] = 1622, --Gnome M [940356] = 1622, --Gnome F [119376] = 1628, --goblin-male [119369] = 1629, --goblin-female [1005887] = 1627, --draenei-male --[1022598] = draenei F [1620605] = 1635, --lightforgeddraenei-male [1593999] = 1636, --lightforgeddraenei-female [1630447] = 1639, --zandalaritroll M [1662187] = 1639, --zandalaritroll F [1022938] = 1632, --troll-male [1018060] = 1633, --troll-female [959310] = 1624, --scourge-male [997378] = 1634, --scourge-female [535052] = 1625, --pandaren M [589715] = 1625, --pandaren F [307454] = 1626, --worgen-male [307453] = 1645, --worgen-female [917116] = 1641, --magharorc-male hunched [1968587] = 1641, --magharorc-male straght [949470] = 1643, --magharorc-female --[1011653] = Human M --[1000764] = Human F --[1814471] = Nightborne M --[1810676] = Nightborne F --[1734034] = VE M --[1733758] = VE F --[1100087] = BE M --[1100258] = BE F --[974343] = NE M --[921844] = NE F }; local GetModelSceneActorInfoByID = C_ModelInfo.GetModelSceneActorInfoByID; local function GetActorInfoByFileID(fileID) --print("FileID: ", fileID) local infoID; if fileID and ActorIDByModelFileID[fileID] then infoID = ActorIDByModelFileID[fileID]; else infoID = DEFAULT_ACTOR_INFO_ID; end return GetModelSceneActorInfoByID(infoID); end addon.GetActorInfoByFileID = GetActorInfoByFileID; --Re-check this↑ table every major patch function Narci_FindActorIDBy(name) local id = 300; local info, tag; local find = string.find; while id < 2000 do id = id + 1; info = C_ModelInfo.GetModelSceneActorInfoByID(id); if info then tag = info.scriptTag; if tag and find(tag, name) then print(id, tag); --return end end end end local ZoomDistanceByRace = { --[raceID] = {male Zoom, female Zoom, bustOffsetZ_M, bustOffsetZ_F}, [1] = {2.4, 2}, -- Human [2] = {2.5, 2}, -- Orc bow [3] = {2.5, 2}, -- Dwarf [4] = {2.2, 2.1}, -- Night Elf [5] = {2.5, 2}, -- UD [6] = {3, 2.5}, -- Tauren [7] = {2.6, 2.8}, -- Gnome [8] = {2.5, 2}, -- Troll [9] = {2.9, 2.9}, -- Goblin [10] = {2, 2}, -- Blood Elf [11] = {2.4, 2}, -- Goat [22] = {2.8, 2}, -- Worgen [24] = {2.9, 2.4}, -- Pandaren [27] = {2, 2}, -- Nightborne --[29] = {2, 2}, -- Void Elf --[28] = {2, 2}, -- Highmountain Tauren --[30] = {2, 2}, -- Lightforged Draenei [31] = {2.2, 2}, -- Zandalari [32] = {2.4, 2.3}, -- Kul'Tiran --[34] = {2, 2}, -- Dark Iron Dwarf [35] = {2.6, 2.1}, -- Vulpera --[36] = {2, 2}, -- Mag'har --[37] = {2, 2}, -- Mechagnome } function NarciAPI_GetCameraZoomDistanceByUnit(unit) if not UnitExists(unit) or not UnitIsPlayer(unit) or not CanInspect(unit, false) then return; end local _, _, raceID = UnitRace(unit); local genderID = UnitSex(unit); if raceID == 25 or raceID == 26 then --Pandaren A|H raceID = 24; elseif raceID == 29 then raceID = 10; elseif raceID == 37 then raceID = 7; elseif raceID == 30 then raceID = 11; elseif raceID == 28 then raceID = 6; elseif raceID == 34 then raceID = 3; elseif raceID == 36 then raceID = 2; elseif raceID == 22 then if unit == "player" then local _, inAlternateForm = HasAlternateForm(); if not inAlternateForm then --Wolf raceID = 22; else raceID = 1; end end end if not (raceID and genderID) then return 2 elseif ZoomDistanceByRace[raceID] then return ZoomDistanceByRace[raceID][genderID - 1] or 2; else return 2 end end local PanningYOffsetByRace = { --[raceID] = { { male = {offsetY1 when frame maximiazed, offsetY2} }, {female = ...} } [0] = { --default {-290, -110}, }, [4] = { --NE {-317, -117}, {-282, -115.5}, }, [10] = { --BE {-282, -110}, {-290, -116}, } --/dump DressUpFrame.ModelScene:GetActiveCamera().panningYOffset } PanningYOffsetByRace[29] = PanningYOffsetByRace[10]; local function GetPanningYOffset(raceID, genderID) genderID = genderID -1; if PanningYOffsetByRace[raceID] and PanningYOffsetByRace[raceID][genderID] then return PanningYOffsetByRace[raceID][genderID] else return PanningYOffsetByRace[0][1] end end function NarciAPI_GetActorInfoByUnit(unit) if not UnitExists(unit) or not UnitIsPlayer(unit) or not CanInspect(unit, false) then return nil, PanningYOffsetByRace[0][1]; end local _, _, raceID = UnitRace(unit); local genderID = UnitSex(unit); if raceID == 25 or raceID == 26 then --Pandaren A|H raceID = 24 end local actorInfoID; if not (raceID and genderID) then actorInfoID = DEFAULT_ACTOR_INFO_ID; --438 elseif ActorIDByRace[raceID] then actorInfoID = ActorIDByRace[raceID][genderID - 1] or DEFAULT_ACTOR_INFO_ID; else actorInfoID = DEFAULT_ACTOR_INFO_ID; --438 end return GetModelSceneActorInfoByID(actorInfoID), GetPanningYOffset(raceID, genderID) end NarciModelSceneActorMixin = CreateFromMixins(ModelSceneActorMixin); function NarciModelSceneActorMixin:OnAnimFinished() if self.oneShot then --self:Hide(); if self.finalSequence then self:SetAnimation(0, 0, 0, self.finalSequence); else self:Hide(); end end if self.onfinishedCallback then self.onfinishedCallback(); end end function NarciAPI_SetupModelScene(modelScene, modelFileID, zoomDistance, view, actorIndex, UseTransit) local pi = math.pi; local model = modelScene; local actorTag; if not actorIndex then actorTag = "narciEffectActor"; else actorTag = "narciEffectActor"..actorIndex end local actor = model[actorTag]; if not actor then local actorID = 156; --effect C_ModelInfo.GetModelSceneActorInfoByID(156) local actorInfo = C_ModelInfo.GetModelSceneActorInfoByID(actorID); --actor = model:AcquireAndInitializeActor(actorInfo); actor = model:CreateActor(nil, "NarciModelSceneActorTemplate"); actor:SetYaw(pi); model[actorTag] = actor; local parentFrame = model:GetParent(); if parentFrame then model:SetFrameLevel(parentFrame:GetFrameLevel() + 1 or 20); else model:SetFrameLevel(20); end end --local cameraTag = "NarciUI"; local camera = model.narciCamera; if not camera then camera = CameraRegistry:CreateCameraByType("OrbitCamera"); if camera then model.narciCamera = camera; model:AddCamera(camera); local modelSceneCameraInfo = C_ModelInfo.GetModelSceneCameraInfoByID(114); camera:ApplyFromModelSceneCameraInfo(modelSceneCameraInfo, 1, 1); --1 ~ CAMERA_TRANSITION_TYPE_IMMEDIATE / CAMERA_MODIFICATION_TYPE_DISCARD end end model:SetActiveCamera(camera); if modelFileID then actor:SetModelByFileID(modelFileID); end if zoomDistance then if UseTransit then --change camera.targetInterpolationAmount for smoothing time --:GetTargetInterpolationAmount() :SetTargetInterpolationAmount(value) camera:SetZoomDistance(1); camera:SnapAllInterpolatedValues(); After(0, function() camera:SetZoomDistance(zoomDistance); end); else camera:SetZoomDistance(zoomDistance); camera:SynchronizeCamera(); end end if view then local pitch, yaw; if type(view) == "string" then view = strupper(view); if view == "FRONT" then pitch = 0; yaw = pi; elseif view == "BACK" then pitch = 0; yaw = 0; elseif view == "TOP" then pitch = pi/2; yaw = pi; elseif view == "BOTTOM" then pitch = -pi/2; yaw = pi; elseif view == "LEFT" then pitch = 0; yaw = -pi/2; elseif view == "RIGHT" then pitch = 0; yaw = pi/2; else return; end elseif type(view) == "table" then pitch = view[1]; yaw = view[2]; if not (pitch and yaw) then return; end end actor:SetPitch(pitch); actor:SetYaw(yaw); end return actor, camera --[[ if rollDegree then actor:SetRoll(rad(-rollDegree)) --Clockwise end --]] end --[[ ScriptAnimatedEffectController:GetCurrentEffectID() /dump ScriptedAnimationEffectsUtil.GetEffectByID() 101 Center Rune Wind Cyan effect = {visual(fileID), visualScale, animationSpeed, ...} fileID, effectID: 3483475 --Black Swirl 52 984698 --Center Rune Wind Cyan 101 3655832 --Circle Rune Effect 73 3656114 --69 --]] ------------------------------------------------------------------------------ local function ReAnchorFrame(frame) --maintain frame top position when changing its height local oldCenterX = frame:GetCenter(); --local oldBottom = frame:GetBottom(); local oldTop = frame:GetTop(); local screenWidth = WorldFrame:GetWidth(); local screenHeight = WorldFrame:GetHeight(); local scale = frame:GetEffectiveScale(); if not scale or scale == 0 then scale = 1; end local width = frame:GetWidth()/2; frame:ClearAllPoints(); --frame:SetPoint("BOTTOMRIGHT", nil, "BOTTOMRIGHT", oldCenterX + width - screenWidth / scale , oldBottom); frame:SetPoint("TOPRIGHT", nil, "TOPRIGHT", oldCenterX + width - screenWidth / scale , oldTop - screenHeight/scale); end local function ParserButton_ShowTooltip(self) if self.itemLink then local frame = self:GetParent(); local tp = frame.tooltip; --GameTooltip_SetBackdropStyle(TP, GAME_TOOLTIP_BACKDROP_STYLE_CORRUPTED_ITEM); tp:SetOwner(self, "ANCHOR_NONE"); tp:SetPoint("TOP", frame.ItemString, "BOTTOM", 0, -14); tp:SetHyperlink(self.itemLink); tp:SetMinimumWidth(254 / 0.8); tp:Show(); frame:SetHeight( max (floor(tp:GetHeight() - 260), 0) + 400); ReAnchorFrame(frame); end end local function ParserButton_GetCursor(self) local infoType, itemID, itemLink = GetCursorInfo(); self.Highlight:Hide() if not (infoType and infoType == "item") then return end self.itemLink = itemLink; local itemName, _, itemQuality, itemLevel, _, _, _, _, itemEquipLoc, itemIcon = GetItemInfo(itemLink); local itemString = match(itemLink, "item:([%-?%d:]+)"); local enchantID = GetItemEnchantID(itemLink); local r, g, b = GetCustomQualityColor(itemQuality); --Show info self.ItemIcon:SetTexture(itemIcon); local frame = self:GetParent(); frame.ItemName:SetText(itemName); frame.ItemName:SetTextColor(r, g, b); frame.ItemString:SetText(itemString); frame.Pointer:Hide(); ParserButton_ShowTooltip(self); ClearCursor(); end --[[ function Narci_ItemParser_OnLoad(self) self:SetUserPlaced(false) self:ClearAllPoints(); self:SetPoint("CENTER", UIParent, "CENTER", 0, 0); self:RegisterForDrag("LeftButton"); self:SetScript("OnShow", ReAnchorFrame); self.ItemButton:SetScript("OnReceiveDrag", ParserButton_GetCursor); self.ItemButton:SetScript("OnClick", ParserButton_GetCursor); self.ItemButton:SetScript("OnEnter", ParserButton_ShowTooltip); local locale = TEXT_LOCALE; local version, build, date, tocversion = GetBuildInfo(); self.ClientInfo:SetText(locale.." "..version.."."..build.." "..NARCI_VERSION_INFO); local tooltip = CreateFrame("GameTooltip", "Narci_ItemParserTooltip", self, "GameTooltipTemplate"); tooltip:Hide(); self.tooltip = tooltip; local scale = 0.8; local tooltipScale = 0.8; self:SetScale(0.8); tooltip:SetScale(tooltipScale); end --]] ---------------------------- -----Item Import/Export----- ---------------------------- local WOWHEAD_ENCODING = "0zMcmVokRsaqbdrfwihuGINALpTjnyxtgevElBCDFHJKOPQSUWXYZ123456789"; --version: 9 WH.calc.hash.getEncoding(9) local WOWHEAD_DELIMITER = 8; --WH.calc.hash.getDelimiter(9) local COMPRESSION_INDICATOR = 7; --WH.calc.hash.getZeroDelimiterCompressionIndicator(9) :7 + single letter local WOWHEAD_MAXCODING_INDEX = 58 --WH.calc.hash.getMaxEncodingIndex(a); //9 ~ 58 local WOWHEAD_CUSTOMIZATION = "0zJ89b"; local EncodeValue = {} for i = 0, #WOWHEAD_ENCODING do EncodeValue[i] = strsub(WOWHEAD_ENCODING, i+1, i+1); end local EquipmentOrderToCharacterSlot = { [1] = 1, [2] = 3, [3] = 15, [4] = 5, [5] = 4, [6] = 19, [7] = 9, [8] = 10, [9] = 6, [10]= 7, [11]= 8, [12]= 16, [13]= 17, }; local CharacterSlotToEquipmentOrder = {} for k, v in pairs(EquipmentOrderToCharacterSlot) do CharacterSlotToEquipmentOrder[v] = tostring(k); v = tostring(v); end local function EncodeLongValue(number) local m = WOWHEAD_MAXCODING_INDEX; if number <= m then return EncodeValue[number]; end local floor = floor; local shortValues = {number}; local v = 0; while(shortValues[1] > m) do v = floor(shortValues[1] / m); tinsert(shortValues, shortValues[1] - m * v); shortValues[1] = v; end local str = EncodeValue[ shortValues[1] ]; for i = #shortValues, 2, -1 do if shortValues[2] ~= "0" then str = str .. EncodeValue[ shortValues[i] ] else str = str .. "7" end end return str end local function EncodeItemlist(itemlist, unitInfo) if not itemlist or type(itemlist) ~= "table" or itemlist == {} then return ""; end --itemlist = {[slot] = {itemID, bonusID}} local raceID, genderID, classID; if unitInfo then raceID, genderID, classID = unitInfo.raceID, unitInfo.genderID, unitInfo.classID; else local _; local unit = "player"; _, _, raceID = UnitRace(unit); genderID = UnitSex(unit); _, _, classID = UnitClass(unit); end if not (raceID and genderID and classID) then local _; local unit = "player"; _, _, raceID = UnitRace(unit); _, _, classID = UnitClass(unit); genderID = UnitSex(unit) or 2; raceID = raceID or 1; classID = classID or 1; end genderID = genderID - 2; --Male 2 → 0 Female 3 → 1 local wowheadLink = "https://www.wowhead.com/dressing-room#s".. EncodeValue[raceID] .. EncodeValue[genderID] .. EncodeValue[classID] .. WOWHEAD_CUSTOMIZATION.. WOWHEAD_DELIMITER; local segment, slot, item; local blanks = 0; for i = 1, #EquipmentOrderToCharacterSlot do --item = { itemID, bonusID } slot = EquipmentOrderToCharacterSlot[i] item = itemlist[slot]; if item and item[1] then segment = EncodeLongValue(item[1]) if #segment < 3 then segment = "7".. CharacterSlotToEquipmentOrder[slot] .. segment end item[2] = item[2] or 0; --bonusID if slot == 16 and item[2] > 0 and item[2] < 99 then local offHand = itemlist[17]; if offHand and offHand[1] then segment = segment .. WOWHEAD_DELIMITER .. "0" .. WOWHEAD_DELIMITER .. EncodeLongValue(offHand[1]) .. WOWHEAD_DELIMITER .. "7c" .. EncodeValue[ item[2] ]; else segment = segment .. WOWHEAD_DELIMITER .. "7V" .. EncodeValue[ item[2] ]; end wowheadLink = wowheadLink .. segment; break; else segment = segment .. WOWHEAD_DELIMITER .. EncodeLongValue(item[2]) .. WOWHEAD_DELIMITER; end wowheadLink = wowheadLink .. segment; else blanks = blanks + 1; wowheadLink = wowheadLink .. "7M" end end return wowheadLink end NarciAPI.EncodeItemlist = EncodeItemlist; -------------------- ----Model Widget---- -------------------- local function NarciAPI_InitializeModelLight(model) --Model: DressUpModel/Cinematic Model/... --Not ModelScene --model:SetLight(true, false, - 0.44699833180028 , 0.72403680806459 , -0.52532198881773, 0.8, 172/255, 172/255, 172/255, 1, 0.8, 0.8, 0.8); TransitionAPI.SetModelLight(model, true, false, - 0.44699833180028 , 0.72403680806459 , -0.52532198881773, 0.8, 172/255, 172/255, 172/255, 1, 0.8, 0.8, 0.8); end NarciAPI.InitializeModelLight = NarciAPI_InitializeModelLight; ---------------------------- ----UI Animation Generic---- ---------------------------- function NarciAPI_CreateAnimationFrame(duration, frameName) local frame = CreateFrame("Frame", frameName); frame:Hide(); frame.total = 0; frame.duration = duration; frame:SetScript("OnHide", function(self) self.total = 0; end); return frame; end function NarciAPI_CreateFadingFrame(parentObject) local animFade = NarciAPI_CreateAnimationFrame(0.2); animFade.timeFactor = 1; parentObject.animFade = animFade; animFade:SetScript("OnUpdate", function(frame, elapsed) frame.t = frame.t + elapsed; if frame.t > 0 then local alpha = frame.fromAlpha; alpha = alpha + frame.timeFactor * elapsed; frame.fromAlpha = alpha; if alpha >= 1 then alpha = 1; frame:Hide(); elseif alpha <= 0 then alpha = 0; frame:Hide(); end parentObject:SetAlpha(alpha); end end); function parentObject:FadeOut(duration, delay) delay = delay or 0; animFade.t = -delay; duration = duration or 0.15; if duration == 0 then animFade:Hide(); parentObject:SetAlpha(0); return end local alpha = parentObject:GetAlpha(); animFade.fromAlpha = alpha; animFade.timeFactor = -1/duration; if alpha == 0 then animFade:Hide(); else animFade:Show(); end end function parentObject:FadeIn(duration, delay) delay = delay or 0; animFade.t = -delay; duration = duration or 0.2; if duration == 0 then animFade:Hide(); parentObject:SetAlpha(1); return end local alpha = parentObject:GetAlpha(); animFade.fromAlpha = alpha; animFade.timeFactor = 1/duration; parentObject:Show(); if alpha ~= 1 then animFade:Show(); end end return animFade end ------------------------------------------- local DelayedFadeIn = NarciAPI_CreateAnimationFrame(1); DelayedFadeIn:SetScript("OnUpdate", function(self, elapsed) self.total = self.total + elapsed; if self.total >= self.duration then self:Hide(); if self.anchor == GetMouseFocus() then FadeFrame(self.object, 0.25, 1); end end end); NarciHotkeyNotificationMixin = {}; function NarciHotkeyNotificationMixin:SetKey(hotkey, mouseButton, description, alwaysShown, enableListener) local ICON_HEIGHT = 20; self.alwaysShown = alwaysShown; self.enableListener = enableListener; self.Label:SetText(description); if description then self.GradientM:Show(); self.GradientR:Show(); else self.GradientM:Hide(); self.GradientR:Hide(); end if alwaysShown then self:SetAlpha(1); else self:SetAlpha(0); end local width = self.Label:GetWidth(); if hotkey then self.KeyIcon:SetTexture("Interface/AddOns/Narcissus/Art/Keyboard/Key", nil, nil, "TRILINEAR"); self.KeyIcon:Show(); if string.lower(hotkey) == "alt" then hotkey = NARCI_MODIFIER_ALT; end self.KeyLabel:SetText(hotkey); self.KeyLabel:SetShadowColor(0, 0, 0); self.KeyLabel:SetShadowOffset(0, 1.4); local texWidth; if string.len(hotkey) > 5 then texWidth = 146; self.KeyIcon:SetTexCoord(0, texWidth/256, 0.25, 0.5); self.isLongButton = true else texWidth = 118; self.isLongButton = nil; self.KeyIcon:SetTexCoord(0, texWidth/256, 0, 0.25); end self.keyTexCoord = texWidth/256; self.KeyIcon:SetSize(texWidth/64*ICON_HEIGHT, ICON_HEIGHT); width = width + texWidth/64*ICON_HEIGHT; end if mouseButton then self.key = mouseButton; self.MouseIcon:SetTexture("Interface/AddOns/Narcissus/Art/Keyboard/Mouse", nil, nil, "TRILINEAR"); self.MouseIcon:Show(); self.MouseIcon:SetSize(ICON_HEIGHT, ICON_HEIGHT); if mouseButton == "LeftButton" then self.MouseIcon:SetTexCoord(0, 0.25, 0, 1); elseif mouseButton == "RightButton" then self.MouseIcon:SetTexCoord(0.25, 0.5, 0, 1); elseif mouseButton == "MiddleButton" then self.MouseIcon:SetTexCoord(0.5, 0.75, 0, 1); elseif mouseButton == "MouseWheel" then self.MouseIcon:SetTexCoord(0.75, 1, 0, 1); end if hotkey then self.KeyIcon:ClearAllPoints(); self.KeyIcon:SetPoint("RIGHT", self.MouseIcon, "LEFT", 0, 0); width = width + ICON_HEIGHT; end end self:SetWidth(width); end function NarciHotkeyNotificationMixin:ShowTooltip() DelayedFadeIn:Hide(); DelayedFadeIn.anchor = GetMouseFocus(); DelayedFadeIn.object = self; DelayedFadeIn:Show(); end function NarciHotkeyNotificationMixin:FadeIn() DelayedFadeIn:Hide(); FadeFrame(self, 0.25, 1); end function NarciHotkeyNotificationMixin:FadeOut() DelayedFadeIn:Hide(); FadeFrame(self, 0.25, 0); end function NarciHotkeyNotificationMixin:JustHide() DelayedFadeIn:Hide(); FadeFrame(self, 0, 0); end function NarciHotkeyNotificationMixin:OnShow() if self.enableListener then self:RegisterEvent("GLOBAL_MOUSE_UP"); end end function NarciHotkeyNotificationMixin:OnHide() if not self.alwaysShown then DelayedFadeIn:Hide(); self:Hide(); self:SetAlpha(0); end if self.enableListener then self:UnregisterEvent("GLOBAL_MOUSE_UP"); end end function NarciHotkeyNotificationMixin:OnEvent(event, key) if key == self.key then self:UnregisterEvent("GLOBAL_MOUSE_UP"); self:FadeOut(); end end function NarciHotkeyNotificationMixin:SetHighlight(state) if self.keyTexCoord then local texCoordY; if self.isLongButton then texCoordY = 0.25; else texCoordY = 0; end if state then texCoordY = texCoordY + 0.5; self.KeyIcon:SetTexCoord(0, self.keyTexCoord, texCoordY + 0.25, texCoordY); self.KeyLabel:SetPoint("CENTER", 0, -1); self.KeyLabel:SetTextColor(0.72, 0.72, 0.72); else self.KeyIcon:SetTexCoord(0, self.keyTexCoord, texCoordY, texCoordY + 0.25); self.KeyLabel:SetPoint("CENTER", 0, 0); self.KeyLabel:SetTextColor(0.6, 0.6, 0.6); end end end ----------------------------------------------------------- NarciDarkRoundButtonMixin = {}; function NarciDarkRoundButtonMixin:OnLoad() self.Background:SetTexture("Interface\\AddOns\\Narcissus\\Art\\Buttons\\Button-Round", nil, nil, "TRILINEAR"); end function NarciDarkRoundButtonMixin:SetLabelText(label) self.Label:SetText(""); self.Label:SetText(label); local textWidth = self.Label:GetWidth(); self.effectiveWidth = floor( self:GetWidth() + textWidth + 2 + 2); end function NarciDarkRoundButtonMixin:Initialize(groupIndex, label, tooltip, onClickFunc) self:SetLabelText(label); if tooltip then self.tooltip = tooltip; end if groupIndex then self.groupIndex = groupIndex; local parent = self:GetParent(); if not parent.buttonGroups then parent.buttonGroups = {}; end if not parent.buttonGroups[groupIndex] then parent.buttonGroups[groupIndex] = {}; end tinsert(parent.buttonGroups[groupIndex], self); end if onClickFunc then self.onClickFunc = onClickFunc; end end function NarciDarkRoundButtonMixin:GetEffectiveWidth() return self.effectiveWidth or self:GetWidth() end function NarciDarkRoundButtonMixin:GetGroupEffectiveWidth() if self.groupIndex then local buttons = self:GetParent().buttonGroups[self.groupIndex]; local width = 0; local maxWidth = 0; for i = 1, #buttons do width = buttons[i]:GetEffectiveWidth(); if width > maxWidth then maxWidth = width; end end return maxWidth else return self:GetEffectiveWidth(); end end function NarciDarkRoundButtonMixin:Select() self.SelectedIcon:Show(); self.isSelected = true; end function NarciDarkRoundButtonMixin:Deselect() self.SelectedIcon:Hide(); self.isSelected = nil; end function NarciDarkRoundButtonMixin:UpdateVisual() if self.groupIndex then local buttons = self:GetParent().buttonGroups[self.groupIndex]; for i = 1, #buttons do buttons[i]:Deselect() end end self:Select(); end function NarciDarkRoundButtonMixin:OnClick() self:UpdateVisual(); if self.onClickFunc then self.onClickFunc(); end end function NarciDarkRoundButtonMixin:OnMouseDown() self.PushedHighlight:Show(); end function NarciDarkRoundButtonMixin:OnMouseUp() self.PushedHighlight:Hide(); end function NarciDarkRoundButtonMixin:UpdateGroupHitBox() if self.groupIndex then local maxWidth = self:GetGroupEffectiveWidth(); local buttons = self:GetParent().buttonGroups[self.groupIndex]; for i = 1, #buttons do buttons[i]:SetHitRectInsets(0, buttons[i]:GetWidth() - maxWidth, 0, 0); end return maxWidth end end NarciDarkSquareButtonMixin = {}; function NarciDarkSquareButtonMixin:OnLoad() self.Background:SetTexture("Interface\\AddOns\\Narcissus\\Art\\Buttons\\Button-RoundedSquare", nil, nil, "TRILINEAR"); end function NarciDarkSquareButtonMixin:Initialize(groupIndex, icon, texCoord, tooltip, onClickFunc) if tooltip then self.tooltip = tooltip; end if icon then self.Icon:SetTexture(icon, nil, nil, "TRILINEAR"); if texCoord then self.Icon:SetTexCoord( unpack(texCoord) ); end end if groupIndex then self.groupIndex = groupIndex; local parent = self:GetParent(); if not parent.buttonGroups then parent.buttonGroups = {}; end if not parent.buttonGroups[groupIndex] then parent.buttonGroups[groupIndex] = {}; end tinsert(parent.buttonGroups[groupIndex], self); end if onClickFunc then self.onClickFunc = onClickFunc; end end function NarciDarkSquareButtonMixin:OnClick() self:UpdateVisual(); if self.onClickFunc then self.onClickFunc(); end end function NarciDarkSquareButtonMixin:UpdateVisual() if self.groupIndex then local button; local buttons = self:GetParent().buttonGroups[self.groupIndex]; for i = 1, #buttons do button = buttons[i]; if self ~= button then button:Deselect(); end end end self:Select(); end function NarciDarkSquareButtonMixin:OnEnter() self.Icon:SetAlpha(1); end function NarciDarkSquareButtonMixin:OnLeave() if not self.isSelected then self.Icon:SetAlpha(0.5); end end function NarciDarkSquareButtonMixin:OnMouseDown() self.PushedHighlight:Show(); end function NarciDarkSquareButtonMixin:OnMouseUp() self.PushedHighlight:Hide(); end function NarciDarkSquareButtonMixin:Select() self.Background:SetTexCoord(0.25, 0.5, 0, 1); self.Icon:SetAlpha(1); self.isSelected = true; end function NarciDarkSquareButtonMixin:Deselect() self.Background:SetTexCoord(0, 0.25, 0, 1); self.Icon:SetAlpha(0.5); self.isSelected = nil; end ----------------------------------------------------------- --Clipboard NarciClipboardMixin = {}; function NarciClipboardMixin:OnLoad() self.Tooltip:SetText(Narci.L["Copied"]); end function NarciClipboardMixin:SetText(text) self.EditBox:SetText(text); end function NarciClipboardMixin:SetFocus() self.EditBox:SetFocus(); end function NarciClipboardMixin:ClearFocus() self.EditBox:ClearFocus(); end function NarciClipboardMixin:ShowClipboard() self:Show(); self.EditBox:Show(); self:StopAnimating(); self.Tooltip:SetAlpha(0); end function NarciClipboardMixin:HasFocus() return self.EditBox.hasFocus; end function NarciClipboardMixin:ReAnchorTooltipToObject(object) if object then self.Tooltip:ClearAllPoints(); self.Tooltip:SetPoint("CENTER", object, "CENTER", 0, 0); end end NarciNonEditableEditBoxMixin = {}; function NarciNonEditableEditBoxMixin:OnLoad() end function NarciNonEditableEditBoxMixin:OnEditFocusGained() self.hasFocus = true; self:SelectText(); end function NarciNonEditableEditBoxMixin:OnEditFocusLost() self.hasFocus = nil; self:Quit(); end function NarciNonEditableEditBoxMixin:SelectText() self:SetCursorPosition(self.defaultCursorPosition or 0); self:HighlightText(); end function NarciNonEditableEditBoxMixin:OnHide() self:StopAnimating(); end function NarciNonEditableEditBoxMixin:Quit() self:ClearFocus(); if self.onQuitFunc then self.onQuitFunc(); end end function NarciNonEditableEditBoxMixin:OnTextChanged(isUserInput) if isUserInput then self:Quit(); end end function NarciNonEditableEditBoxMixin:OnKeyDown(key, down) local keys = CreateKeyChordStringUsingMetaKeyState(key); if keys == "CTRL-C" or key == "COMMAND-C" then self.hasCopied = true; After(0, function() self:GetParent().Tooltip.good:Play(); self:Hide(); end); end end ----------------------------------------------------------- NarciLanguageUtil = {}; NarciLanguageUtil.wowheadLinkPrefix = { ["default"] = "www", ["deDE"] = "de", ["esES"] = "es", ["esMX"] = "es", ["frFR"] = "fr", ["itIT"] = "it", ["ptBR"] = "pt", ["ruRU"] = "ru", ["koKR"] = "ko", ["zhCN"] = "cn", ["zhTW"] = "cn", }; NarciLanguageUtil.wowheadLinkPrefix.primary = NarciLanguageUtil.wowheadLinkPrefix[TEXT_LOCALE] or "www"; function NarciLanguageUtil:GetWowheadLink(specificLanguage) local prefix; if specificLanguage then prefix = self.wowheadLinkPrefix[ tostring(specificLanguage) ] or "www"; else prefix = self.wowheadLinkPrefix.primary; end return ( "https://".. prefix .. ".wowhead.com/"); end local function GetAllSelectedTalentIDsAndIcons(ignorePlayerLevel) local talentInfo = {} local maxTiers; if ignorePlayerLevel then maxTiers = 7; else maxTiers = GetMaxTalentTier(); --based on the character's level end local talentGroup = GetActiveSpecGroup(); local _, _, classID = UnitClass("player"); talentInfo.classID = classID; if not talentGroup then talentInfo.talentGroup = false; return; else talentInfo.talentGroup = talentGroup; end local column, tierUnlockLevel, talentID, iconTexture, selected; for tier = 1, maxTiers do _, column, tierUnlockLevel = GetTalentTierInfo(tier, talentGroup); if column then talentID, _, iconTexture, selected = GetTalentInfo(tier, column, talentGroup); talentInfo[tier] = {talentID, iconTexture, tierUnlockLevel}; else talentInfo[tier] = {false, 134400}; --Question Mark Icon end end return talentInfo end NarciAPI.GetAllSelectedTalentIDsAndIcons = GetAllSelectedTalentIDsAndIcons; local function CreateColor(r, g, b) return RoundToDigit(r/255, 4), RoundToDigit(g/255, 4), RoundToDigit(b/255, 4); end NarciAPI.CreateColor = CreateColor; local timeStartNarcissus; local function UpdateSessionTime() local t = time(); if timeStartNarcissus then local session = t - timeStartNarcissus; local timeSpent = NarciStatisticsDB.TimeSpentInNarcissus; if not timeSpent or type(timeSpent) ~= "number" then timeSpent = 0; end NarciStatisticsDB.TimeSpentInNarcissus = timeSpent + session; timeStartNarcissus = nil; else timeStartNarcissus = t; end end NarciAPI.UpdateSessionTime = UpdateSessionTime; local function UpdateScreenshotsCounter() if Narci.isActive then local numTaken = NarciStatisticsDB.ScreenshotsTakenInNarcissus; if not numTaken or type(numTaken) ~= "number" then numTaken = 0; end NarciStatisticsDB.ScreenshotsTakenInNarcissus = numTaken + 1; end end NarciAPI.UpdateScreenshotsCounter = UpdateScreenshotsCounter; local function GetClassColorByClassID(classID) local classInfo = classID and C_CreatureInfo.GetClassInfo(classID); if classInfo then return C_ClassColor.GetClassColor(classInfo.classFile); end end local function WrapNameWithClassColor(name, classID, specID, showIcon, offsetY) local classInfo = C_CreatureInfo.GetClassInfo(classID); if classInfo then local color = GetClassColorByClassID(classID); if color then if specID and showIcon then local str = color:WrapTextInColorCode(name); local _, _, _, icon, role = GetSpecializationInfoByID(specID); if icon then offsetY = offsetY or 0; str = "|T"..icon..":12:12:-1:"..offsetY..":64:64:4:60:4:60|t" ..str; end return str else return color:WrapTextInColorCode(name); end else return name end else return name end end NarciAPI.GetClassColorByClassID = GetClassColorByClassID; NarciAPI.WrapNameWithClassColor = WrapNameWithClassColor; local function GetOutfitSlashCommand() local playerActor = DressUpFrame.ModelScene:GetPlayerActor(); local itemTransmogInfoList = playerActor and playerActor:GetItemTransmogInfoList(); local slashCommand = TransmogUtil.CreateOutfitSlashCommand(itemTransmogInfoList); return slashCommand end NarciAPI.GetOutfitSlashCommand = GetOutfitSlashCommand; NarciAPI.GetScreenPixelSize = function() return 768 / SCREEN_HEIGHT end local UtilityModel, ValidDisplayIDs; local function DoesCreatureDisplayIDExist(id) if not id then return end; if not UtilityModel then ValidDisplayIDs = {}; UtilityModel = CreateFrame("CinematicModel", nil, UIParent); UtilityModel:SetKeepModelOnHide(true); UtilityModel:SetSize(2, 2); UtilityModel:SetPoint("TOP", UIParent, "BOTTOM", 0, -3); UtilityModel:SetScript("OnModelLoaded", function(self) local displayID = self:GetDisplayInfo(); if displayID and displayID ~= 0 then ValidDisplayIDs[displayID] = true; self.displayID = nil; end self:ClearModel(); end); UtilityModel:Hide(); end if ValidDisplayIDs[id] ~= nil then return ValidDisplayIDs[id]; else UtilityModel:ClearModel(); UtilityModel:SetDisplayInfo(id); end end NarciAPI.DoesCreatureDisplayIDExist = DoesCreatureDisplayIDExist; local function PixelPerfectDriver_Update(self) local scale = self:GetParent():GetEffectiveScale(); if scale == self.scale then return else self.scale = scale; end local p = 768 / SCREEN_HEIGHT / scale; for i, tex in ipairs(self.textures) do if tex.w then tex:SetWidth(p * tex.w); end if tex.h then tex:SetHeight(p * tex.h); end end end local function AddPixelPerfectTexture(frame, texture, pixelWidth, pixelHeight) if not frame.pixelDriver then frame.pixelDriver= CreateFrame("Frame", nil, frame); frame.pixelDriver.textures = {}; frame.pixelDriver:SetScript("OnShow", PixelPerfectDriver_Update); end texture.w = pixelWidth; texture.h = pixelHeight; --[[ for i, obj in ipairs(frame.pixelDriver.textures) do if obj == texture then return end end --]] tinsert(frame.pixelDriver.textures, texture); end NarciAPI.AddPixelPerfectTexture = AddPixelPerfectTexture; local function SetFramePointPixelPerfect(frame, point, relativeTo, relativePoint, offsetX, offsetY) if relativeTo then local pixel = GetPixelForWidget(frame); frame:ClearAllPoints(); local right0 = relativeTo:GetRight(); local right1 = floor( (right0 + offsetX) * pixel + 0.5) / pixel; local top0 = relativeTo:GetTop(); local top1 = floor( (top0 + offsetY) * pixel) / pixel; frame:SetPoint(point, relativeTo, relativePoint, right1 - right0, top1 - top0); end end NarciAPI.SetFramePointPixelPerfect = SetFramePointPixelPerfect; local function IsPlayerAtMaxLevel() local playerLevel = UnitLevel("player") or 0; local maxPlayerLevel; if GetMaxLevelForLatestExpansion then maxPlayerLevel = GetMaxLevelForLatestExpansion(); else local expansionLevel = GetExpansionLevel() or 0; maxPlayerLevel = GetMaxLevelForExpansionLevel(expansionLevel); end return playerLevel >= maxPlayerLevel; end NarciAPI.IsPlayerAtMaxLevel = IsPlayerAtMaxLevel; do local IsInteractingWithNpcOfType = C_PlayerInteractionManager.IsInteractingWithNpcOfType; local TYPE_GOSSIP = Enum.PlayerInteractionType and Enum.PlayerInteractionType.Gossip or 3; local TYPE_QUEST_GIVER = Enum.PlayerInteractionType and Enum.PlayerInteractionType.QuestGiver or 4; local GetQuestID = GetQuestID; local INTERACT_RECENELY = false; local function IsInteractingWithDialogNPC() if INTERACT_RECENELY then return true end; local currentQuestID = GetQuestID(); return IsInteractingWithNpcOfType(TYPE_GOSSIP) or IsInteractingWithNpcOfType(TYPE_QUEST_GIVER) or (currentQuestID ~= nil and currentQuestID ~= 0) end addon.IsInteractingWithDialogNPC = IsInteractingWithDialogNPC; local DialogEventHandler; local function DialogEventHandler_Check() if C_AddOns.IsAddOnLoaded("DialogueUI") then local events = { "GOSSIP_SHOW", "QUEST_DETAIL", "QUEST_PROGRESS", "QUEST_COMPLETE", "QUEST_GREETING", }; DialogEventHandler = CreateFrame("Frame"); for _, event in ipairs(events) do DialogEventHandler:RegisterEvent(event) end local function OnUpdate(self, elapsed) self.t = self.t + elapsed; if self.t >= 1 then self:SetScript("OnUpdate", nil); self.t = nil; INTERACT_RECENELY = false; end end DialogEventHandler:SetScript("OnEvent", function(self, event, ...) self.t = 0; INTERACT_RECENELY = true; self:SetScript("OnUpdate", OnUpdate); end); end end addon.AddLoadingCompleteCallback(DialogEventHandler_Check); end do local BindHelper; local BindEvents = { "ACTION_WILL_BIND_ITEM", "EQUIP_BIND_REFUNDABLE_CONFIRM", "EQUIP_BIND_TRADEABLE_CONFIRM", "EQUIP_BIND_CONFIRM", "END_BOUND_TRADEABLE", }; local function HideStaticPopup() if StaticPopup1 then StaticPopup1:Hide(); end end local function ConfirmBinding() if not BindHelper then BindHelper = CreateFrame("Frame"); BindHelper:Hide(); BindHelper:SetScript("OnEvent", function(self, event, ...) if event == "ACTION_WILL_BIND_ITEM" then if self.pending then self.pending = nil; C_Item.ActionBindsItem(); end elseif event == "EQUIP_BIND_REFUNDABLE_CONFIRM" or event == "EQUIP_BIND_TRADEABLE_CONFIRM" or event == "EQUIP_BIND_CONFIRM" then if self.pending then self.pending = nil; local slot = ... EquipPendingItem(slot); end elseif event == "END_BOUND_TRADEABLE" then if self.pending then self.pending = nil; local reason = ... C_Item.EndBoundTradeable(reason); end end HideStaticPopup(); for _, v in ipairs(BindEvents) do self:UnregisterEvent(v); end end); BindHelper:SetScript("OnUpdate", function(self, elapsed) self.t = self.t + elapsed; if self.t > 0.5 then self.t = nil; self.pending = nil; self:Hide(); for _, v in ipairs(BindEvents) do self:UnregisterEvent(v); end end end); end for _, v in ipairs(BindEvents) do BindHelper:RegisterEvent(v); end BindHelper.t = 0; BindHelper.pending = true; BindHelper:Show(); end addon.ConfirmBinding = ConfirmBinding; end local function DoesItemExistByID(itemID) itemID = GetItemInfoInstant(itemID) return itemID ~= nil end addon.DoesItemExistByID = DoesItemExistByID; local function CopyTable(tbl) --Blizzard TableUtil.lua if not tbl then return; end; local copy = {}; for k, v in pairs(tbl) do if type(v) == "table" then copy[k] = CopyTable(v); else copy[k] = v; end end return copy; end addon.CopyTable = CopyTable; do local SOUND_FILE_ID = 567520; local MuteSoundFile = MuteSoundFile; local UnmuteSoundFile = UnmuteSoundFile; local function MuteTargetLostSound(state) if state then MuteSoundFile(SOUND_FILE_ID); else UnmuteSoundFile(SOUND_FILE_ID); end end NarciAPI.MuteTargetLostSound = MuteTargetLostSound; end --[[ /script DEFAULT_CHAT_FRAME:AddMessage("\124Hitem:narcissus:0:\124h[Test Link]\124h\124r"); function TestFX(modelFileID, zoomDistance, view) NarciAPI_SetupModelScene(TestScene, modelFileID, zoomDistance, view); end --]] --/run TestFX(3152608, nil, ) 122972 --/run TestFX(1011653, 8, " --/run TestFX(3004122, 8, "LEFT") --Eyeball --Shake Frame SoulbindViewerMixin:Shake() Bliizard Blizzard_SoulbindsViewer.lua --Debug --[[ local DebugUtil = NarciAPI_CreateAnimationFrame(1, "NarciDebug"); DebugUtil:SetScript("OnUpdate", function(self, elapsed) self.total = self.total + elapsed; if self.total >= self.duration then self:Hide(); self.isAdding = nil; print("AVG: ".. self.sum / self.numNumber); end end); function DebugUtil:RestartTimer() self.total = 0; self:Show(); end function DebugUtil:CalculateAverage(number) if not number then return end; if self.isAdding then self.sum = self.sum + number; self.numNumber = self.numNumber + 1; else self.sum = number; self.numNumber = 1; self.isAdding = true; self:RestartTimer(); end end --]]