You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3581 lines
110 KiB

local max = math.max;
local GetText = GetText;
local GetTexture = GetTexture;
local NumLines = NumLines;
local _;
local _G = _G;
local After = C_Timer.After;
local GetItemInfo = GetItemInfo;
local UIFrameFadeIn = UIFrameFadeIn;
local UIFrameFadeOut = UIFrameFadeOut;
local PlaySound = PlaySound;
local unpack = unpack;
local _, _, _, tocversion = GetBuildInfo();
local strtrim = strtrim;
local match = string.match;
local gsub = string.gsub;
local sub = string.sub;
local strsplit = strsplit;
local min = math.min;
local max = math.max;
local abs = math.abs;
local floor = math.floor;
local TEXT_LOCALE = GetLocale();
NarciAPI = {};
NarciViewUtil = {};
local NarciGameTooltip = CreateFrame("GameTooltip", "NarciGameTooltip", UIParent, "GameTooltipTemplate");
------------------------
--Redirect API for 9.0--
------------------------
tocversion = tonumber(tocversion);
ExpansionTransitionBackdropTemplateMixin = {};
if BackdropTemplateMixin then
ExpansionTransitionBackdropTemplateMixin = CreateFromMixins(BackdropTemplateMixin);
end
--GetSlotVisualID
local NarciAPI_GetSlotVisualID;
if not TransmogLocationMixin then
function NarciAPI_GetSlotVisualID(slotID)
if slotID == 2 or (slotID > 10 and slotID < 15) then
return -1, -1;
end
local baseSourceID, baseVisualID, appliedSourceID, appliedVisualID, _, _, _, hideVisual = C_Transmog.GetSlotVisualInfo(slotID, 0);
if ( hideVisual ) then
return 0, 0;
elseif ( appliedSourceID == 0 ) then --NO_TRANSMOG_SOURCE_ID
return baseSourceID, baseVisualID;
else
return appliedSourceID, appliedVisualID;
end
end
else
function NarciAPI_GetSlotVisualID(slotID)
if slotID == 2 or (slotID > 10 and slotID < 15) then
return -1, -1;
end
local itemLocation = ItemLocation:CreateFromEquipmentSlot(slotID)
if not itemLocation or not C_Item.DoesItemExist(itemLocation) then
return -1, -1;
end
local transmogLocation = CreateFromMixins(TransmogLocationMixin);
local transmogType = 0;
local modification = 0;
transmogLocation:Set(slotID, transmogType, modification);
local baseSourceID, baseVisualID, appliedSourceID, appliedVisualID, appliedCategoryID, pendingSourceID, pendingVisualID, pendingCategoryID, hasPendingUndo, _, itemSubclass = C_Transmog.GetSlotVisualInfo(transmogLocation);
if ( appliedSourceID == 0 ) then --NO_TRANSMOG_SOURCE_ID
appliedSourceID = baseSourceID;
appliedVisualID = baseVisualID;
end
return appliedSourceID, appliedVisualID;
end
end
NarciAPI.GetSlotVisualID = NarciAPI_GetSlotVisualID;
--------------------
----API Datebase----
--------------------
local SlotIDtoName = {
--[slotID] = {InventorySlotName, Localized Name, invType, textureID} --GetInventorySlotInfo("SlotName")
[1] = {"HeadSlot", HEADSLOT, INVTYPE_HEAD},
[2] = {"NeckSlot", NECKSLOT, INVSLOT_NECK},
[3] = {"ShoulderSlot", SHOULDERSLOT, INVTYPE_SHOULDER},
[4] = {"ShirtSlot", SHIRTSLOT, INVTYPE_BODY},
[5] = {"ChestSlot", CHESTSLOT, INVTYPE_CHEST},
[6] = {"WaistSlot", WAISTSLOT, INVTYPE_WAIST},
[7] = {"LegsSlot", LEGSSLOT, INVTYPE_LEGS},
[8] = {"FeetSlot", FEETSLOT, INVTYPE_FEET},
[9] = {"WristSlot", WRISTSLOT, INVTYPE_WRIST},
[10]= {"HandsSlot", HANDSSLOT, INVTYPE_HAND},
[11]= {"Finger0Slot", FINGER0SLOT_UNIQUE, INVSLOT_FINGER1},
[12]= {"Finger1Slot", FINGER1SLOT_UNIQUE, INVSLOT_FINGER2},
[13]= {"Trinket0Slot", TRINKET0SLOT_UNIQUE, INVSLOT_TRINKET1},
[14]= {"Trinket1Slot", TRINKET1SLOT_UNIQUE, INVSLOT_TRINKET2},
[15]= {"BackSlot", BACKSLOT, INVTYPE_CLOAK},
[16]= {"MainHandSlot", MAINHANDSLOT, INVTYPE_WEAPONMAINHAND},
[17]= {"SecondaryHandSlot", SECONDARYHANDSLOT, INVTYPE_WEAPONOFFHAND},
[18]= {"AmmoSlot", RANGEDSLOT, INVSLOT_RANGED},
[19]= {"TabardSlot", TABARDSLOT, INVTYPE_TABARD},
}
for slotID, info in pairs(SlotIDtoName) do
_, info[4] = GetInventorySlotInfo(info[1]);
end
--[[
function NarciAPI_GetSlotLocalizedName(slotID)
return SlotIDtoName[slotID][2], SlotIDtoName[slotID][4]
end
--]]
Narci.SlotIDtoName = SlotIDtoName;
-----------------------------------------------------
local _, CommanderOfArgus = GetAchievementInfo(12078); --Argus Weapon Transmogs: Arsenal: Weapons of the Lightforged
CommanderOfArgus = CommanderOfArgus or "Commander of Argus";
CommanderOfArgus = "|cFFFFD100"..BATTLE_PET_SOURCE_6 .."|r "..CommanderOfArgus;
--local EnsorcelledEverwyrm = C_MountJournal.GetMountFromSpell(307932);
local _, _, PROMOTION_SHADOWLANDS = C_MountJournal.GetMountInfoExtraByID(1289); --EnsorcelledEverwyrm Promotion: Shadowlands Heroic Edition
local HERITAGE_ARMOR = Narci.L["Heritage Armor"];
local HeritageArmorItemIDs = {
165931, 165932, 165933, 165934, 165935, 165936, 165937, 16598, --Dwarf
161008, 161009, 161010, 161011, 161012, 161013, 161014, 161015, --Dark Iron
156668, 156669, 156670, 156671, 156672, 156673, 156674, 156684, --Highmountain
156699, 156700, 156701, 156702, 156703, 156704, 156705, 156706, --Lightforged
161050, 161051, 161052, 161054, 161055, 161056, 161057, 161058, --Mag'har Orc (Blackrock Recolor)
161059, 161060, 161061, 161062, 161063, 161064, 161065, 161066, --Mag'har Orc (Frostwolf Recolor)
160992, 160993, 160994, 160999, 161000, 161001, 161002, 161003, --Mag'har Orc (Warsong Recolor)
156690, 156691, 156692, 156693, 156694, 156695, 156696, 156697, 157758, 158917, --Void Elf
156675, 156676, 156677, 156678, 156679, 156680, 156681, 156685, --Nightborne
166348, 166349, 166351, 166352, 166353, 166354, 166355, 166356, 166357, --Blood Elf
164993, 164994, 164995, 164996, 164997, 164998, 164999, 165000, --Zandalari
165002, 165003, 165004, 165005, 165006, 165007, 165008, 165009, --Kul'tiran
168282, 168283, 168284, 168285, 168286, 168287, 168288, 168289, 168290, --Gnome
168291, 168292, 168293, 168294, 168295, 168296, 168297, 168298, 170063, --Tauren
173968, 173966, 173970, 173971, 173967, 173969, 174354, 174355, --Vulpera
173961, 173962, 173963, 173964, 173958, 173972, --Mechagnome
174000, 174001, 174002, 174003, 174004, 174005, 174006, 173999, 173998, --Worgen
--Reserved for test ↓
}
local SecretlItemIDs = {
[162690] = true, --Waist of Time
}
local SpecialItemList = {
[152332] = CommanderOfArgus, --Brilliant Daybreak Aegis
[152333] = CommanderOfArgus, --Lustrous Daybreak Aegis
[152334] = CommanderOfArgus, --Brilliant Eventide Aegis
[152335] = CommanderOfArgus, --Lustrous Eventide Aegis
[152336] = CommanderOfArgus, --Lustrous Daybreak Blade
[152337] = CommanderOfArgus, --Brilliant Daybreak Blade
[152338] = CommanderOfArgus, --Lustrous Eventide Blade
[152339] = CommanderOfArgus, --Brilliant Daybreak Blade
[152340] = CommanderOfArgus, --Lustrous Daybreak Greatsword
[152341] = CommanderOfArgus, --Lustrous Eventide Greatsword
[152342] = CommanderOfArgus, --Lustrous Daybreak Staff
[152343] = CommanderOfArgus, --Lustrous Eventide Staff
[172075] = PROMOTION_SHADOWLANDS, --The Eternal Traveler's
[172076] = PROMOTION_SHADOWLANDS,
[172077] = PROMOTION_SHADOWLANDS,
[172078] = PROMOTION_SHADOWLANDS,
[172079] = PROMOTION_SHADOWLANDS,
[172080] = PROMOTION_SHADOWLANDS,
[172081] = PROMOTION_SHADOWLANDS,
[172082] = PROMOTION_SHADOWLANDS,
[172083] = PROMOTION_SHADOWLANDS,
--[134110] = PROMOTION_SHADOWLANDS, --Test
}
local Ensemble_TheChosenDead_ItemIDs = {
142423, 142421, 142422, 142434, 142420, 142433, --Mail
142427, 142425, 142431, 142435, 142426, 142424, --Plate
142419, 142430, 142432, 142417, 142418, 142416, --Leather
142415, 142411, 142410, 142413, 142429, 142414, --Cloth
143355, 143345, 143334, 143354, 143346, 143347,
143356, 143339, 143349, 143342, 143344, 143335,
143353, 143368, 143340, 143337, 143348, 143341,
143343, 143367, 143336, 143352, 143366, 143351,
143360, 143358, 143350, 143361, 143364, 143359,
143338, 143369, 143365, 143363, 143362, 143357,
};
local function BuildSearchTable(table)
if type(table) ~="table" then
return;
end
local newTable = {};
for k, v in pairs(table) do
newTable[v] = true;
end
wipe(table);
return newTable;
end
local HeritageArmorList = BuildSearchTable(HeritageArmorItemIDs);
local Ensemble_TheChosenDead = BuildSearchTable(Ensemble_TheChosenDead_ItemIDs);
--[[
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
[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(sub(hexColor, 1, 2), 16) / 255;
local g = tonumber(sub(hexColor, 3, 4), 16) / 255;
local b = tonumber(sub(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 unpack(customQualityColors[itemQuality]);
end
NarciAPI.GetItemQualityColor = GetCustomQualityColor;
NarciAPI.GetItemQualityColorTable = function()
local newTable = {};
for k, v in pairs(customQualityColors) do
newTable[k] = v;
end
return newTable;
end
local BorderTexture = {
["Bright"] = {
[0] = "Interface/AddOns/Narcissus/Art/Border/HexagonBorder-Black",
[1] = "Interface/AddOns/Narcissus/Art/Border/HexagonBorder",
[2] = "Interface/AddOns/Narcissus/Art/Border/HexagonBorder-Uncommon",
[3] = "Interface/AddOns/Narcissus/Art/Border/HexagonBorder-Rare",
[4] = "Interface/AddOns/Narcissus/Art/Border/HexagonBorder-Epic", --Epic NZoth
[5] = "Interface/AddOns/Narcissus/Art/Border/HexagonBorder-Legendary",
[6] = "Interface/AddOns/Narcissus/Art/Border/HexagonBorder-Artifact",
[7] = "Interface/AddOns/Narcissus/Art/Border/HexagonBorder-Heirloom", --Void
[8] = "Interface/AddOns/Narcissus/Art/Border/HexagonBorder-Azerite",
[12] = "Interface/AddOns/Narcissus/Art/Border/HexagonBorder-Special",
["Heart"] = "Interface/AddOns/Narcissus/Art/Border/HexagonBorder-Heart", --Heart
["NZoth"] = "Interface/AddOns/Narcissus/Art/Border/HexagonBorder-NZoth",
["BlackDragon"] = "Interface/AddOns/Narcissus/Art/Border/HexagonBorder-BlackDragon", --8.3 Legendary Cloak
["Minimap"] = "Interface/AddOns/Narcissus/Art/Minimap/LOGO-Large",
},
["Dark"] = {
[0] = "Interface/AddOns/Narcissus/Art/Border-Thick/HexagonThickBorder-Black",
[1] = "Interface/AddOns/Narcissus/Art/Border-Thick/HexagonThickBorder-Black",
[2] = "Interface/AddOns/Narcissus/Art/Border-Thick/HexagonThickBorder-Uncommon",
[3] = "Interface/AddOns/Narcissus/Art/Border-Thick/HexagonThickBorder-Rare",
[4] = "Interface/AddOns/Narcissus/Art/Border-Thick/HexagonThickBorder-Epic", --Epic
[5] = "Interface/AddOns/Narcissus/Art/Border-Thick/HexagonThickBorder-Legendary",
[6] = "Interface/AddOns/Narcissus/Art/Border-Thick/HexagonThickBorder-Artifact",
[7] = "Interface/AddOns/Narcissus/Art/Border-Thick/HexagonThickBorder-Heirloom", --Void
[8] = "Interface/AddOns/Narcissus/Art/Border-Thick/HexagonThickBorder-Azerite",
[12] = "Interface/AddOns/Narcissus/Art/Border-Thick/HexagonThickBorder-Black",
["Heart"] = "Interface/AddOns/Narcissus/Art/Border-Thick/HexagonThickBorder-Heart", --Heart
["NZoth"] = "Interface/AddOns/Narcissus/Art/Border-Thick/HexagonThickBorder-NZoth",
["BlackDragon"] = "Interface/AddOns/Narcissus/Art/Border-Thick/HexagonThickBorder-BlackDragon", --8.3 Legendary Cloak
["Minimap"] = "Interface/AddOns/Narcissus/Art/Minimap/LOGO-Large", --only enable Thick minimap when AzeriteUI is loaded
},
}
local function NarciAPI_GetBorderTexture()
local index = NarcissusDB and NarcissusDB.BorderTheme
if not index then
return BorderTexture["Bright"], BorderTexture["Bright"]["Minimap"], "Bright"
else
return (BorderTexture[index] or BorderTexture["Bright"]), BorderTexture[index]["Minimap"], index
end
end
NarciAPI.GetBorderTexture = NarciAPI_GetBorderTexture;
local GemBorderTexture = {
[0] = "Interface/AddOns/Narcissus/Art/GemBorder/GemSlot", --Empty
[1] = "Interface/AddOns/Narcissus/Art/GemBorder/GemSlot-Unique", --Kraken's Eye
[2] = "Interface/AddOns/Narcissus/Art/GemBorder/GemSlot-Green",
[3] = "Interface/AddOns/Narcissus/Art/GemBorder/GemSlot-Unique", --Prismatic
[4] = "Interface/AddOns/Narcissus/Art/GemBorder/GemSlot-Unique", --Meta
[5] = "Interface/AddOns/Narcissus/Art/GemBorder/GemSlot-Orange", --Orange
[6] = "Interface/AddOns/Narcissus/Art/GemBorder/GemSlot-Purple",
[7] = "Interface/AddOns/Narcissus/Art/GemBorder/GemSlot-Yellow", --Yellow
[8] = "Interface/AddOns/Narcissus/Art/GemBorder/GemSlot-Blue", --Blue
[9] = "Interface/AddOns/Narcissus/Art/GemBorder/GemSlot-Yellow", --Empty
[10] = "Interface/AddOns/Narcissus/Art/GemBorder/GemSlot-Red", --Red
[11] = "Interface/AddOns/Narcissus/Art/GemBorder/GemSlot", --Artifact
}
--Some gems require you to assign colors manually
--itemID, itemType, itemSubType, itemEquipLoc, icon, itemClassID, itemSubClassID = GetItemInfoInstant(itemID or "itemString" or "itemName" or "itemLink")
local function GetGemBorderTexture(itemSubClassID, itemID)
local index = itemSubClassID or 0;
if itemID == 153714 or itemID == 173125 then
index = 10; --Red EXP bonus
elseif itemID == 153715 or itemID == 169220 or itemID == 173126 then
index = 2; --Movement Speed
elseif itemID == 168636 or itemID == 168637 or itemID == 168638 or
itemID == 153707 or itemID == 153708 or itemID == 153709 then
index = 1; --Primary
end
return GemBorderTexture[index], index
end
NarciAPI.GetGemBorderTexture = GetGemBorderTexture;
--------------------
------Item API------
--------------------
local function GetItemEnchantID(itemLink)
local _, _, _, linkType, linkID, enchantID = strsplit(":|H", itemLink);
return tonumber(enchantID) or 0;
end
NarciAPI.GetItemEnchantID = GetItemEnchantID;
local function IsHeritageArmor(itemID)
if not itemID then
return false;
end
if HeritageArmorList[itemID] then
return true;
else
return false;
end
end
local ITEMSOURCE_SECRETFINDING = Narci.L["Secret Finding"];
local function NarciAPI_IsItemSourceSpecial(itemID, modID)
if not itemID then
return false;
end
if IsHeritageArmor(itemID) then
return true, HERITAGE_ARMOR;
end
local itemSource = SpecialItemList[itemID];
if itemSource ~= nil then
--print("Is Special")
return true, itemSource;
end
if SecretlItemIDs[itemID] then
return true, ITEMSOURCE_SECRETFINDING;
end
if Ensemble_TheChosenDead[itemID] then
return true, "|cFFFFD100"..DUNGEON_FLOOR_HELHEIMRAID1.."|r";
end
return false;
end
NarciAPI.IsItemSourceSpecial = NarciAPI_IsItemSourceSpecial;
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_GemInfo;
local EnchantInfo = Narci_EnchantInfo;
local DoesItemExist = C_Item.DoesItemExist;
local GetCurrentItemLevel = C_Item.GetCurrentItemLevel;
local GetItemLink = C_Item.GetItemLink
local GetItemStats = GetItemStats;
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 = GetItemInfo(GemLink)
local _, _, _, _, GemIcon, _, itemSubClassID = GetItemInfoInstant(GemLink);
statsTable.GemIcon = GemIcon
statsTable.gems = 1;
if GemInfo[GemID] then
local GemInfo = GemInfo[GemID]
statsTable.GemPos = GemInfo[1];
if GemInfo[1] == "crit" then
statsTable.crit = statsTable.crit + GemInfo[2];
elseif GemInfo[1] == "haste" then
statsTable.haste = statsTable.haste + GemInfo[2];
elseif GemInfo[1] == "mastery" then
statsTable.mastery = statsTable.mastery + GemInfo[2];
elseif GemInfo[1] == "versatility" then
statsTable.versatility = statsTable.versatility + GemInfo[2];
elseif GemInfo[1] == "AGI" or GemInfo[1] == "STR" or GemInfo[1] == "INT" then
statsTable.prim = statsTable.prim + GemInfo[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";
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
--------------------
----Tooltip Scan----
--------------------
local TP = CreateFrame("GameTooltip", "NarciVirtualTooltip", nil, "GameTooltipTemplate");
TP:SetScript("OnLoad", GameTooltip_OnLoad);
TP:SetOwner(UIParent, 'ANCHOR_NONE');
local find = string.find;
local function NarciAPI_IsItemSocketable(itemLink, socketID)
if not itemLink then return; end
if not socketID then socketID = 1; end
local gemName, gemLink = GetItemGem(itemLink, socketID)
if gemName then
return gemName, gemLink;
end
--]]
local tex, texID;
for i = 1, 3 do
tex = _G["NarciVirtualTooltip".."Texture"..i]
tex = tex:SetTexture(nil);
end
TP:SetHyperlink(itemLink);
for i = 1, 3 do --max 10
tex = _G["NarciVirtualTooltip".."Texture"..i]
texID = tex and tex:GetTexture();
--print(texID)
if texID == 458977 then --no file name anymore 458977:Regular empty socket texture
return "Empty", nil;
end
end
--[[
for i = begin, num do
local str = _G["NarciVirtualTooltip".."TextLeft"..i]
if str and str:GetText() == SocketAction then
print("Has Socket")
return;
end
end
--]]
return nil, nil;
end
NarciAPI.IsItemSocketable = NarciAPI_IsItemSocketable;
local function NarciAPI_GetItemRank(itemLink, statName)
--Items that can get upgraded
if not itemLink then return; end
TP:SetHyperlink(itemLink);
local fontstring = _G["NarciVirtualTooltip".."TextLeft"..2];
fontstring = fontstring:GetText() or "";
fontstring = strtrim(fontstring, "|r");
local rank = match(fontstring, "%d+", -2) or "";
if statName then
local stats = GetItemStats(itemLink) or {};
return "|cff00ccff"..rank.."|r", stats[statName] or 0
else
return "|cff00ccff"..rank.."|r"
end
end
NarciAPI.GetItemRank = NarciAPI_GetItemRank;
local ITEM_ENCHANT_FORMAT = gsub(ENCHANTED_TOOLTIP_LINE, "%%s", "(.+)");
local function GetItemEnchantText(itemLink, colorized)
if not itemLink then return; end
TP:SetHyperlink(itemLink);
local num = TP:NumLines();
local str;
local enchantText;
local enchantFormat = ITEM_ENCHANT_FORMAT;
for i = 5, num do
str = _G["NarciVirtualTooltip".."TextLeft"..i];
if str then
str = str:GetText();
enchantText = match(str, enchantFormat);
if enchantText then
enchantText = strtrim(enchantText);
if enchantText ~= "" then
--print(enchantText)
if colorized then
enchantText = "|cff5fbd6b"..enchantText.."|r";
end
return enchantText
end
end
else
return
end
end
end
NarciAPI.GetItemEnchantText = GetItemEnchantText;
-----String API------
--[[
function NarciAPI_DeformatString(str, patterns)
if not str then return end
local patternType = type(patterns);
if patternType == "string" then
patterns = gsub(patterns, "%(", "%%%(");
patterns = gsub(patterns, "%)", "%%%)");
patterns = gsub(patterns, "%%d", "%%d+");
patterns = gsub(patterns, "%%s", "(.+)");
return match(str, patterns)
elseif patternType == "table" then
local pattern;
local result = {};
--print(str)
for i = 1, #patterns do
pattern = patterns[i];
pattern = gsub(pattern, "%(", "%%%(");
pattern = gsub(pattern, "%)", "%%%)");
pattern = gsub(pattern, "%%d", "%%d+");
pattern = gsub(pattern, "%%s", "(.+)");
result = { match(str, pattern) };
--print(pattern)
if #result ~= 0 then
return unpack(result);
end
end
return str;
end
end
--]]
local greyFont = "|cff959595";
local leftBrace = "%(";
local rightBrace = "%)";
if (TEXT_LOCALE == "zhCN") or (TEXT_LOCALE == "zhTW") then
leftBrace = "";
rightBrace = "";
end
local SOURCE_KNOWN = TRANSMOGRIFY_TOOLTIP_APPEARANCE_KNOWN;
local APPEARANCE_KNOWN = TRANSMOGRIFY_TOOLTIP_ITEM_UNKNOWN_APPEARANCE_KNOWN;
local APPEARANCE_UNKNOWN = TRANSMOGRIFY_TOOLTIP_APPEARANCE_UNKNOWN;
local function NarciAPI_IsAppearanceKnown(itemLink)
--Need to correspond with C_TransmogCollection.PlayerHasTransmog
if not itemLink then return; end
TP:SetHyperlink(itemLink);
local str;
local num = TP:NumLines();
for i = num, num - 2, -1 do
str = nil;
str = _G["NarciVirtualTooltip".."TextLeft"..i]
if not str then
return false;
else
str = str:GetText();
end
if str == SOURCE_KNOWN or str == APPEARANCE_KNOWN then
return true;
elseif str == APPEARANCE_UNKNOWN then
return false;
end
end
return false;
end
NarciAPI.IsAppearanceKnown = NarciAPI_IsAppearanceKnown;
local function trimComma(text)
return strtrim(text, "::");
end
local function formatString(text, removedText)
text = strtrim(text, removedText);
text = trimComma(text);
text = strtrim(text); --remove space
text = gsub(text, leftBrace, "\n\n"..greyFont)
text = gsub(text, rightBrace, "|r")
return text;
end
local onUse = ITEM_SPELL_TRIGGER_ONUSE;
local onEquip = ITEM_SPELL_TRIGGER_ONEQUIP;
local onProc = ITEM_SPELL_TRIGGER_ONPROC;
local minLevel = SOCKETING_ITEM_MIN_LEVEL_I;
local _onUse = trimComma(onUse)
local _onEquip = trimComma(onEquip)
local _onProc = trimComma(onProc)
local function NarciAPI_GetItemExtraEffect(itemLink)
if not itemLink then return; end
TP:SetHyperlink(itemLink);
local num = TP:NumLines();
local begin = max(num - 6, 0);
local output = "";
local category, str;
for i = begin, num, 1 do
str = nil;
str = _G["NarciVirtualTooltip".."TextLeft"..i]
if not str then
return;
else
str = str:GetText();
end
if find(str, onUse) then
str = formatString(str, _onUse);
if not category then category = _onUse; end
--return _onUse, str;
output = output..str.."\n\n"
elseif find(str, onEquip) then
str = formatString(str, _onEquip);
if not category then category = _onEquip; end
--return _onEquip, str;
output = output..str.."\n\n"
elseif find(str, onProc) then
str = formatString(str, _onProc);
if not category then category = _onProc; end
--return _onProc, str;
output = output..str.."\n\n"
end
end
return category, output;
end
NarciAPI.GetItemExtraEffect = NarciAPI_GetItemExtraEffect;
local SpecialGemData = {
--1 Movement Speed
--2 Health Regen
[173125] = 2, --Revitalizing Jewel Doublet
[173126] = 1, --Straddling Jewel Doublet
};
local function NarciAPI_GetGemBonus(itemID)
--itemID: Gem's Item ID or hyperlink
if not itemID then return; end
if type(itemID) == "number" then
TP:SetItemByID(itemID)
else
TP:SetHyperlink(itemID)
end
local num = TP:NumLines();
local bonusText;
local str;
local level = 0;
local bonusID = SpecialGemData[itemID];
if bonusID then
if bonusID == 1 then
bonusText = STAT_MOVEMENT_SPEED;
elseif bonusID == 2 then
bonusText = ITEM_MOD_HEALTH_REGENERATION_SHORT;
end
end
for i = 1, num do
str = _G["NarciVirtualTooltip".."TextLeft"..i]
if not str then
return;
else
str = str:GetText();
if not str then
return;
end
end
if not bonusText and strsub(str, 1, 1) == "+" then
bonusText = str;
end
if find(str, minLevel) then
level = formatString(str, minLevel);
end
if level and bonusText then return bonusText, tonumber(level); end
end
return bonusText, tonumber(level);
end
NarciAPI.GetGemBonus = NarciAPI_GetGemBonus;
--------------------
---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;
--------------------
---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 screenWidth, screenHeight = GetPhysicalScreenSize();
local UIParentWidth, UIParentHeight = UIParent:GetSize();
function NarciAPI_OptimizeBorderThickness(self)
if not self.HasOptimized then
local point, relativeTo, relativePoint, xOfs, yOfs = self:GetPoint()
local uiScale = self:GetEffectiveScale();
--local scale = match(GetCVar( "gxWindowedResolution" ), "%d+x(%d+)" );
--local rate = 768/scale/uiScale;
--local _, screenHeight = GetPhysicalScreenSize();
local rate = (768/screenHeight)/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
local OptimizeBorderThickness = NarciAPI_OptimizeBorderThickness;
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
for i=1, (num_Gap + 1) do
tex = self:CreateTexture(nil, "BACKGROUND", nil, 1);
tex:SetSize(2, 10);
tex:SetColorTexture(0.3, 0.3, 0.3, 1);
tex:SetPoint("LEFT", 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
--Internal Keybinding
NarciGenericKeyBindingButtonMixin = {};
local function ResetBindVisualAndScript(self)
self.Border:SetColorTexture(0, 0, 0);
self.Value:SetTextColor(1, 1, 1);
self.Value:SetShadowColor(0, 0, 0);
self.Value:SetShadowOffset(0.6, -0.6);
self:SetPropagateKeyboardInput(true)
self:SetScript("OnKeyDown", nil);
self.IsOn = false;
end
local function GenericKeyBindingButton_OnKeydown(self, key)
if key == "ESCAPE" or key == "SPACE" or key == "ENTER" then
self:ExitKeyBinding();
return
end
if self.actionName then
self:ExitKeyBinding(true);
NarcissusDB[self.actionName] = key;
end
end
function NarciGenericKeyBindingButtonMixin:ExitKeyBinding(success)
ResetBindVisualAndScript(self);
After(0, function()
self:GetBindingKey();
end)
if success then
self.Highlight:SetColorTexture(0.4862, 0.7725, 0.4627);
self.Description:SetText("|cff7cc576".. KEY_BOUND);
UIFrameFadeIn(self.Highlight, 0.2, 0, 1);
UIFrameFadeIn(self.Description, 0.2, 0, 1);
self.Timer:Stop();
self.Timer:SetScript("OnFinished", function()
UIFrameFadeOut(self.Highlight, 0.5, 1, 0);
UIFrameFadeOut(self.Description, 0.5, 1, 0);
end);
self.Timer:Play();
end
end
function NarciGenericKeyBindingButtonMixin:GetBindingKey()
OptimizeBorderThickness(self);
if self.actionName then
self.Value:SetText(NarcissusDB[self.actionName] or NOT_BOUND);
else
self.Value:SetText(NARCI_COLOR_RED_MILD.. "No Action");
end
end
function NarciGenericKeyBindingButtonMixin:ReleaseBindingKey()
ResetBindVisualAndScript(self);
if self.actionName then
self.Value:SetText(self.defaultKey or NOT_BOUND);
NarcissusDB[self.actionName] = self.defaultKey;
self.Highlight:SetColorTexture(0.9333, 0.1961, 0.1412);
self.Description:SetText(NARCI_COLOR_RED_MILD.."Hotkey reset");
UIFrameFadeIn(self.Description, 0.2, 0, 1);
UIFrameFadeIn(self.Highlight, 0.2, 0, 1);
self.Timer:Stop();
self.Timer:SetScript("OnFinished", function()
UIFrameFadeOut(self.Highlight, 0.5, 1, 0);
UIFrameFadeOut(self.Description, 0.5, 1, 0);
end);
self.Timer:Play();
end
end
function NarciGenericKeyBindingButtonMixin:OnClick(button)
self.IsOn = not self.IsOn;
if button == "LeftButton" then
if self.IsOn then
self.Border:SetColorTexture(0.9, 0.9, 0.9);
self.Value:SetTextColor(0, 0, 0);
self.Value:SetShadowColor(1, 1, 1);
self.Value:SetShadowOffset(0.6, -0.6);
self:SetPropagateKeyboardInput(false);
self:SetScript("OnKeyDown", GenericKeyBindingButton_OnKeydown);
end
else
self:ReleaseBindingKey();
end
end
function NarciGenericKeyBindingButtonMixin:OnHide()
self:StopAnimating();
end
-----Smooth Scroll-----
local function SmoothScrollContainer_OnUpdate(self, elapsed)
local delta = self.delta;
local scrollBar = self.scrollBar;
local value = scrollBar:GetValue();
local step = max(abs(value - self.endValue)*(self.speedRatio) , self.minOffset); --if the step (Δy) is too small, the fontstring will jitter.
local remainedStep;
if ( delta == 1 ) then
--Up
remainedStep = min(self.endValue - value, 0);
if - remainedStep <= ( self.minOffset) then
self:Hide();
scrollBar:SetValue(min(self.maxVal, self.endValue));
if self.onScrollFinishedFunc then
self.onScrollFinishedFunc();
end
else
scrollBar:SetValue(max(0, value - step));
end
else
remainedStep = max(self.endValue - value, 0);
if remainedStep <= ( self.minOffset) then
self:Hide();
scrollBar:SetValue(min(self.maxVal, self.endValue));
if self.onScrollFinishedFunc then
self.onScrollFinishedFunc();
end
else
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;
local 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
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/scale/uiScale;
--local _, screenHeight = GetPhysicalScreenSize();
local pixel = (768/screenHeight)/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);
scrollFrame.SmoothScrollContainer = SmoothScrollContainer;
scrollFrame:SetScript("OnMouseWheel", NarciAPI_SmoothScroll_OnMouseWheel); --a position-related function
if onScrollFinishedFunc then
SmoothScrollContainer.onScrollFinishedFunc = onScrollFinishedFunc;
end
scrollFrame.SetScrollRange = SetScrollRange;
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
--Linear Scroll For Gamepad Control
local LinearScrollUpdater = CreateFrame("Frame", "Narci_LinearScrollUpdater");
LinearScrollUpdater:Hide();
LinearScrollUpdater.value = 0;
LinearScrollUpdater.distancePerSecond = 0;
LinearScrollUpdater.multiplier = 1;
LinearScrollUpdater:SetScript("OnUpdate", function(self, elapsed)
local newValue = self.value + self.distancePerSecond * self.multiplier * elapsed;
self.slider:SetValue(newValue);
self.value = newValue;
if newValue >= self.maxValue or newValue <= 0 then
self:Hide();
end
if self.accelerate then
self.multiplier = self.multiplier + elapsed;
if self.multiplier > 3 then
self.multiplier = 3;
end
end
end);
function LinearScrollUpdater:Start(scrollFrame, distancePerSecond, accelerate)
self:Hide();
local scrollBar = scrollFrame.scrollBar;
if not scrollBar then
return
end
self.slider = scrollBar;
self.multiplier = 1;
self.accelerate = accelerate;
self.value = scrollBar:GetValue();
self.distancePerSecond = distancePerSecond;
self.minValue, self.maxValue = scrollBar:GetMinMaxValues();
self:Show();
return not(self.value == self.maxValue or self.value == self.minValue)
end
function LinearScrollUpdater:Stop()
self:Hide();
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(string)
local str = string
local len = strlen(str)
local i = 1
while i <= len do
local c = string.byte(str, i)
local shift = 1
--print(c)
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
local char = sub(str, i, i+shift-1)
i = i + shift
end
return "RM"
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 function SmartFontType(self, height, 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(self, height)
SmartFontType(self, height, PlayerNameFont);
end
NarciAPI.SmartFontType = NarciAPI_SmartFontType;
function NarciAPI_SmartEditBoxType(self, 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
Narci_PhotoModeToolbar.PhotoModeControllerAnimFrame.toAlpha = 0
frame:Show();
frame.BottomMask.animIn:Play();
frame.TopMask.animIn:Play();
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 timeDelay = 0.6;
local GetCursorPosition = GetCursorPosition;
local DelayedTP = CreateFrame("Frame");
DelayedTP:Hide();
DelayedTP:SetScript("OnShow", function(self)
self.t = 0; --Total time after ShowDelayedTooltip gets called
--self.ScanTime = 0; --Cursor scaning 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 >= timeDelay then
if self.focus and self.focus == GetMouseFocus() then
NarciGameTooltip:ClearAllPoints();
NarciGameTooltip:SetPoint(self.point, self.relativeTo, self.relativePoint, self.ofsx, self.ofsy);
UIFrameFadeIn(NarciGameTooltip, 0.12, 0, 1);
end
self:Hide();
end
end)
function NarciAPI_ShowDelayedTooltip(point, relativeTo, relativePoint, ofsx, ofsy)
local TP = DelayedTP;
TP.focus = GetMouseFocus();
TP.point, TP.relativeTo, TP.relativePoint, TP.ofsx, TP.ofsy = point, relativeTo, relativePoint, ofsx, ofsy;
TP:Hide();
TP:Show();
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-----
if BackdropTemplateMixin then
NarciAlertFrameMixin = CreateFromMixins(BackdropTemplateMixin);
else
NarciAlertFrameMixin = {};
end
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())
local y = offsetY or -12;
self:SetPoint("BOTTOM", frame, "TOP", 0, y);
self:SetFrameLevel(50)
self.anchor = frame;
if AddErrorAnimation then
self:AddShakeAnimation(frame);
end
end
function NarciAlertFrameMixin:AddMessage(msg, UseErrorAnimation)
self.Text:SetText(msg);
self:SetHeight(self.Background:GetHeight());
UIFrameFadeIn(self, 0.2, self:GetAlpha(), 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
------------------------
--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 Column, 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 _, _, 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;
end
local VOICE_BY_RACE = {
--[raceID] = { [gender] = {Error_NoTarget, } }
[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!!!!
}
local ERROR_NOTARGET, ALERT_INCOMING;
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;
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 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
}
--Re-check this↑ table every major patch
--[[
function Narci_GetActorByTag(raceName, gender)
raceName = string.lower(raceName);
local playerRaceActor;
if gender == 1 then
playerRaceActor = raceName.."-".."male";
else
playerRaceActor = raceName.."-".."female";
end
return DressUpFrame.ModelScene:GetActorByTag(playerRaceActor);
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 DEFAULT_ACTOR_INFO_ID = 438;
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
local GetModelSceneActorInfoByID = C_ModelInfo.GetModelSceneActorInfoByID;
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 and not CursorHasItem() then
local frame =self:GetParent();
local TP = frame.tooltip;
--GameTooltip_SetBackdropStyle(TP, GAME_TOOLTIP_BACKDROP_STYLE_CORRUPTED_ITEM);
TP:SetBackdrop(nil);
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
elseif not IsCorruptedItem(itemLink) then
local frame = self:GetParent();
frame.ItemName:SetText("Not a Corrupted Item");
local itemString = match(itemLink, "item:([%-?%d:]+)");
frame.ItemString:SetText(itemString);
After(2, function()
frame.ItemName:SetText("Drop a Corrupted Item Here");
end);
ClearCursor();
return
end
ClearCursor();
self.itemLink = itemLink;
local itemName, _, itemQuality, itemLevel, _, _, _, _, itemEquipLoc, itemIcon = GetItemInfo(itemLink);
local itemString = match(itemLink, "item:([%-?%d:]+)");
local supposedEffect, corruptionID = NarciAPI_GetCorruptedItemAffix(itemLink);
local hasGem = NarciAPI_IsItemSocketable(itemLink);
local enchantID = GetItemEnchantID(itemLink);
local corruption = GetItemStats(itemLink)["ITEM_MOD_CORRUPTION"] or 0;
local _, extraEffect = NarciAPI_GetItemExtraEffect(itemLink);
local r, g, b = GetItemQualityColor(itemQuality);
local HEX_PURPLE = "|c"..CORRUPTION_COLOR:GenerateHexColor();
if extraEffect then
extraEffect = HEX_PURPLE.. extraEffect
end
if supposedEffect then
supposedEffect = supposedEffect.." ".."|cff946dd1"..corruption.."|r";
else
supposedEffect = "|cff946dd1"..corruption.."|r";
end
if hasGem then
supposedEffect = supposedEffect.." ".."|cff40C7ebSocket|r"
end
local enchantName;
local colorizedString = itemString;
if enchantID and enchantID ~= 0 then
local GREEN = "|cff1eff00";
local info = Narci_EnchantInfo[enchantID];
if info then
enchantName = string.gsub(info[1], "%a", string.upper, 1).." "..info[2];
supposedEffect = supposedEffect.." "..GREEN..enchantName.."|r";
end
colorizedString = string.gsub(itemString, enchantID, GREEN..enchantID.."|r", 1);
end
if corruptionID then
colorizedString = string.gsub(colorizedString, corruptionID, HEX_PURPLE..corruptionID.."|r", 1);
end
--Show info
self.ItemIcon:SetTexture(itemIcon);
local frame = self:GetParent();
frame.ItemName:SetText(supposedEffect);
frame.ItemName:SetTextColor(r, g, b);
frame.ItemString:SetText(colorizedString);
frame.SupposedEffect:SetText(supposedEffect);
frame.ActualEffect:SetText(extraEffect);
frame.Pointer:Hide();
ParserButton_ShowTooltip(self)
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 TP = CreateFrame("GameTooltip", "Narci_ItemParserTooltip", self, "GameTooltipTemplate");
TP:Hide();
self.tooltip = TP;
local scale = 0.8;
local tooltipScale = 0.8;
self:SetScale(0.8);
TP: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);
end
NarciAPI.InitializeModelLight = NarciAPI_InitializeModelLight;
--------------------
--------Time--------
--------------------
function NarciAPI_FormatTime(seconds)
seconds = seconds or 0;
local hour = floor(seconds / 3600);
local minute = floor((seconds - 3600 * hour) / 60);
local second = mod(seconds, 60);
if hour > 0 then
return hour.."h "..minute.."m "..second.."s";
elseif minute > 0 then
return minute.."m "..second.."s";
else
return second.."s";
end
end
----------------------------
----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)
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);
function parentObject:FadeOut(duration)
duration = duration or 0.15;
local alpha = parentObject:GetAlpha();
animFade.fromAlpha = alpha;
animFade.timeFactor = -1/duration;
if alpha ~= 0 then
animFade:Show();
end
end
function parentObject:FadeIn(duration)
duration = duration or 0.2;
local alpha = parentObject:GetAlpha();
animFade.fromAlpha = alpha;
animFade.timeFactor = 1/duration;
parentObject:Show();
if alpha ~= 1 then
animFade:Show();
end
end
return animFade
end
----------------------------
-------Frame Template-------
----------------------------
NarciFrameMixin = CreateFromMixins(ExpansionTransitionBackdropTemplateMixin);
function NarciFrameMixin:ShowFrame(state)
self:SetShown(state);
if state then
self:SetAlpha(1);
else
self:SetAlpha(0);
end
end
function NarciFrameMixin:HideFrame()
self:ShowFrame(false);
end
function NarciFrameMixin:ToggleFrame()
if self:IsShown() then
self:ShowFrame(false);
else
self:ShowFrame(true);
end
end
function NarciFrameMixin:SetHeaderText(text, r, g, b)
self.Header:SetText(text);
self.Header:SetTextColor(r or 0.4, g or 0.4, b or 0.4);
end
function NarciFrameMixin:SetSizeAndAnchor(x, y, point, relativeTo, relativePoint, offsetX, offsetY)
if x then x = max(x, 40) end;
if y then y = max(y, 40) end;
if x then
if y then
self:SetSize(x, y);
else
self:SetWidth(x);
end
else
self:SetHeight(y);
end
self:ClearAllPoints();
self:SetPoint(point, relativeTo, relativePoint, offsetX, offsetY);
end
function NarciFrameMixin:SetRelativeFrameLevel(offset)
local parent = self:GetParent();
if parent then
local parentLevel = parent:GetFrameLevel() or 0;
self:SetFrameStrata(parent:GetFrameStrata());
self:SetFrameLevel( max(parentLevel + offset, 0) );
end
end
function NarciFrameMixin:HideWhenParentIsHidden(state)
if state then
self:SetScript("OnHide", function(self)
self:HideFrame();
end);
local parent = self:GetParent();
if parent and not parent:IsVisible() then
self:HideFrame();
end
else
self:SetScript("OnHide", nil);
end
end
-------------------------------------------
local FadeFrame = NarciAPI_FadeFrame;
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, "IN");
end
end
end);
NarciHotkeyNotificationMixin = {};
function NarciHotkeyNotificationMixin:SetKey(hotkey, mouseButton, text, alwaysShown)
local ICON_HEIGHT = 20;
self.alwaysShown = alwaysShown;
self.Label:SetText(text);
local width = self.Label:GetWidth();
if alwaysShown then
self:SetAlpha(1);
else
self:SetAlpha(0);
end
if hotkey then
self.KeyIcon:SetTexture("Interface/AddOns/Narcissus/Art/Keyboard/Key", nil, nil, "TRILINEAR");
self.KeyIcon:Show();
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.5, 1);
else
texWidth = 118;
self.KeyIcon:SetTexCoord(0, texWidth/256, 0, 0.5);
end
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);
self.enableListener = true;
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, "IN");
end
function NarciHotkeyNotificationMixin:FadeOut()
DelayedFadeIn:Hide();
FadeFrame(self, 0.25, "OUT");
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
NarciQuickFavoriteButtonMixin = {};
function NarciQuickFavoriteButtonMixin:SetIconSize(size)
self.iconSize = size;
self.Icon:SetSize(size, size);
self.Bling:SetSize(size, size);
self.Icon:SetTexCoord(0.5, 0.75, 0.25, 0.5);
self.favTooltip = Narci.L["Favorites Add"];
self.unfavTooltip = Narci.L["Favorites Remove"];
self.isFav = false;
end
function NarciQuickFavoriteButtonMixin:SetFavorite(isFavorite)
if isFavorite then
self.isFav = true;
self.Icon:SetTexCoord(0.75, 1, 0.25, 0.5);
self.Icon:SetAlpha(1);
else
self.isFav = false;
self.Icon:SetTexCoord(0.5, 0.75, 0.25, 0.5);
self.Icon:SetAlpha(0.4);
end
end
function NarciQuickFavoriteButtonMixin:PlayVisual()
self:StopAnimating();
if self.isFav then
self.Icon:SetTexCoord(0.75, 1, 0.25, 0.5);
self.parent.Star:Show();
self.Bling.animIn:Play();
else
self.Icon:SetTexCoord(0.5, 0.75, 0.25, 0.5);
self.parent.Star:Hide();
end
end
function NarciQuickFavoriteButtonMixin:OnEnter()
self.Icon:SetAlpha(1);
if self.isFav then
NarciTooltip:NewText(self.unfavTooltip, nil, nil, 1);
else
NarciTooltip:NewText(self.favTooltip, nil, nil, 1);
end
end
function NarciQuickFavoriteButtonMixin:OnLeave()
NarciTooltip:FadeOut();
if not self.isFav then
self.Icon:SetAlpha(0.6);
end
end
function NarciQuickFavoriteButtonMixin:OnHide()
self:StopAnimating();
end
function NarciQuickFavoriteButtonMixin:OnMouseDown()
self.Icon:SetSize(self.iconSize - 2, self.iconSize - 2);
NarciTooltip:FadeOut();
end
function NarciQuickFavoriteButtonMixin:OnMouseUp()
self.Icon:SetSize(self.iconSize, self.iconSize);
end
function NarciQuickFavoriteButtonMixin:OnDoubleClick()
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: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: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 = CreateKeyChordString(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
NarciChamferedFrameMixin = {};
function NarciChamferedFrameMixin:SetBackgroundColor(r, g, b, a)
if not self.backgroundAtlas then
self.backgroundAtlas = {
self.BackgroundTopLeft, self.BackgroundTop, self.BackgroundTopRight,
self.BackgroundMiddleLeft, self.BackgroundMiddle, self.BackgroundMiddleRight,
self.BackgroundBottomLeft, self.BackgroundBottom, self.BackgroundBottomRight,
};
end
a = a or 1;
for i = 1, #self.backgroundAtlas do
self.backgroundAtlas[i]:SetVertexColor(r, g, b);
self.backgroundAtlas[i]:SetAlpha(a);
end
end
function NarciChamferedFrameMixin:SetBorderColor(r, g, b, a)
if not self.borderAtlas then
self.borderAtlas = {
self.BorderTopLeft, self.BorderTop, self.BorderTopRight,
self.BorderMiddleLeft, self.BorderMiddle, self.BorderMiddleRight,
self.BorderBottomLeft, self.BorderBottom, self.BorderBottomRight,
};
end
a = a or 1;
for i = 1, #self.borderAtlas do
self.borderAtlas[i]:SetVertexColor(r, g, b);
self.borderAtlas[i]:SetAlpha(a);
end
end
function NarciChamferedFrameMixin:SetOffset(value)
--positive value expand the frame background
self.BackgroundTopLeft:SetPoint("TOPLEFT", self, "TOPLEFT", -value, value);
self.BorderTopLeft:SetPoint("TOPLEFT", self, "TOPLEFT", -value, value);
self.BackgroundTopRight:SetPoint("TOPRIGHT", self, "TOPRIGHT", value, value);
self.BorderTopRight:SetPoint("TOPRIGHT", self, "TOPRIGHT", value, value);
self.BackgroundBottomLeft:SetPoint("BOTTOMLEFT", self, "BOTTOMLEFT", -value, -value);
self.BorderBottomLeft:SetPoint("BOTTOMLEFT", self, "BOTTOMLEFT", -value, -value);
self.BackgroundBottomRight:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", value, -value);
self.BorderBottomRight:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", value, -value);
end
function NarciChamferedFrameMixin:Toggle()
self:SetShown(not self:IsShown());
end
function NarciChamferedFrameMixin:HideWhenParentIsHidden(state)
if state then
self:SetScript("OnHide", function()
self:Hide()
end);
else
self:SetScript("OnHide", nil);
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
-----------------------------------------------------------
NarciChainAnimationMixin = {};
function NarciChainAnimationMixin:Initialize(size, isLinked)
local offset = 4;
local unchainedOffset = 8;
local side = 24;
local tex = "Interface\\AddOns\\Narcissus\\Art\\Widgets\\LightSetup\\Chain";
self:SetScale(size / side);
self.isLinked = isLinked;
self.chains = {
self.UpTop, self.DownTop, self.UpBottom, self.DownBottom,
}
self.unchains = {
self.ChainShardUp, self.ChainShardDown, self.UpBroken, self.DownBroken
}
for i = 1, #self.chains do
self.chains[i]:SetTexture(tex, nil, nil, "TRILINEAR");
self.chains[i]:SetSize(side, side);
if i % 2 == 1 then
self.chains[i]:SetPoint("CENTER", self, "CENTER", offset, offset);
else
self.chains[i]:SetPoint("CENTER", self, "CENTER", -offset, -offset);
end
end
self.UpBroken:SetSize(side, side);
self.UpBroken:SetTexture(tex, nil, nil, "TRILINEAR");
self.UpBroken:SetPoint("CENTER", self, "CENTER", unchainedOffset, unchainedOffset);
self.DownBroken:SetSize(side, side);
self.DownBroken:SetTexture(tex, nil, nil, "TRILINEAR");
self.DownBroken:SetPoint("CENTER", self, "CENTER", -unchainedOffset, -unchainedOffset);
local tex2 = "Interface\\AddOns\\Narcissus\\Art\\Widgets\\LightSetup\\ChainShard";
self.ChainShardUp:SetSize(side/2, side/2);
self.ChainShardUp:SetTexture(tex2, nil, nil, "TRILINEAR");
self.ChainShardDown:SetSize(side/2, side/2);
self.ChainShardDown:SetTexture(tex2, nil, nil, "TRILINEAR");
local tex3 = "Interface\\AddOns\\Narcissus\\Art\\Widgets\\LightSetup\\ChainWave";
self.WaveExpand:SetSize(side/2, side/2);
self.WaveExpand:SetTexture(tex3, nil, nil, "TRILINEAR");
for i = 1, #self.chains do
self.chains[i]:SetShown(isLinked);
end
for i = 1, #self.unchains do
self.unchains[i]:SetShown(not isLinked);
end
end
function NarciChainAnimationMixin:Switch()
self:StopAnimating();
local isLinked = not self.isLinked;
self.isLinked = isLinked;
if isLinked then
for i = 1, #self.unchains do
self.unchains[i]:Hide();
end
for i = 1, #self.chains do
self.chains[i]:Show();
self.chains[i].Link:Play();
end
else
for i = 1, #self.unchains do
self.unchains[i]:Show();
self.unchains[i].Unlink:Play();
end
for i = 1, #self.chains do
self.chains[i]:Hide();
end
self.WaveExpand.Unlink:Play();
end
if not self.isPlayingSound then
self.isPlayingSound = true;
After(0.5, function() self.isPlayingSound = nil end);
if isLinked then
PlaySound(1188, "SFX", false);
else
PlaySound(112052, "SFX", false);
end
end
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;
--[[
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
local function TestItemLinkAffix(from, to)
local TP = TP;
local max = max;
local total = 0;
local s = from --6500;
local e = to --6600;
local output;
local itemLink;
local function GetExtraInfo()
itemLink = "\124cffa335ee\124Hitem:174954::::::::120::::2:1477:".. s ..":\124h[]\124h\124r";
TP:SetHyperlink(itemLink);
local num = TP:NumLines();
local begin = max(num - 3, 0);
local str;
for i = begin, num, 1 do
str = nil;
str = _G["NarciVirtualTooltip".."TextLeft"..i]
if not str then
break;
else
str = str:GetText();
end
if find(str, onEquip) then
print("|cFFFFD100"..s.."|r "..str);
break
end
end
s = s + 1;
total = total + 1;
if s < e and total < 1000 then
After(0, GetExtraInfo);
else
print("Search Complete")
end
end
print("Search from "..s.." to "..e);
for i = s, e do
--Cache
itemLink = "\124cffa335ee\124Hitem:174954::::::::120::::2:1477:".. i ..":\124h[]\124h\124r";
TP:SetHyperlink(itemLink);
end
After(1, GetExtraInfo);
end