Blizzard_AzeriteEssenceUI/Blizzard_AzeriteEssenceUI.lua

Blizzard_AzeriteEssenceUI/Blizzard_AzeriteEssenceUI.lua (9.0.2.36753; unchanged since 9.0.1.36230)
  1. UIPanelWindows["AzeriteEssenceUI"] = { area = "left", pushable = 1 };
  2. AzeriteEssenceUIMixin = CreateFromMixins(CallbackRegistryMixin);
  3. AzeriteEssenceUIMixin:GenerateCallbackEvents(
  4. {
  5. "OnShow",
  6. "OnHide",
  7. });
  8. local ESSENCE_BUTTON_HEIGHT = 41;
  9. local ESSENCE_HEADER_HEIGHT = 21;
  10. local ESSENCE_BUTTON_OFFSET = 1;
  11. local ESSENCE_LIST_PADDING = 3;
  12. local LOCKED_FONT_COLOR = CreateColor(0.5, 0.447, 0.4);
  13. local LOCKED_ICON_COLOR = CreateColor(.898, .804, .722);
  14. local UNLOCK_LEVEL_TEXT_COLOR = CreateColor(0.051, 0.251, 0.373);
  15. local CONNECTED_LINE_COLOR = CreateColor(.055, .796, .804);
  16. local DISCONNECTED_LINE_COLOR = CreateColor(.055, .796, .804);
  17. local LOCKED_LINE_COLOR = CreateColor(.486, .486, .486);
  18. local HEART_MODEL_SCENE_INFO = StaticModelInfo.CreateModelSceneEntry(256, 1962885); -- Offhand_1H_HeartofAzeroth_D_01.m2
  19. local LEARN_MODEL_SCENE_INFO = StaticModelInfo.CreateModelSceneEntry(259, 2101299); -- 8FX_Azerite_AbsorbCurrency_Small_ImpactBase.m2
  20. local LEARN_MODEL_SCENE_ACTOR_SETTINGS = {
  21. ["effect"] = { startDelay = 0.79, duration = 0.769, speed = 1 },
  22. };
  23. local UNLOCK_SLOT_MODEL_SCENE_INFO = StaticModelInfo.CreateModelSceneEntry(269, 1983548); -- 8FX_Azerite_Generic_NovaHigh_Base.m2
  24. local UNLOCK_STAMINA_MODEL_SCENE_INFO = StaticModelInfo.CreateModelSceneEntry(270, 1983548, 2924332); -- 8FX_Azerite_Generic_NovaHigh_Base.m2, CFX_Azerite_TimeLostTopaz_Major_Rank4_Cast.m2
  25. local UNLOCK_MODEL_SCENE_ACTOR_SETTINGS = {
  26. ["effect"] = { startDelay = 0, duration = 0.4, speed = 1 },
  27. ["effect2"] = { startDelay = 0.4, duration = 0.6, speed = 1 },
  28. };
  29. local REVEAL_SLOT_MODEL_SCENE_INFO = StaticModelInfo.CreateModelSceneEntry(286, 1983548, 2924332); -- 8FX_Azerite_Generic_NovaHigh_Base.m2, CFX_Azerite_TimeLostTopaz_Major_Rank4_Cast.m2
  30. local REVEAL_MODEL_SCENE_ACTOR_SETTINGS = {
  31. ["effect"] = { startDelay = 0, duration = 0.4, speed = 1 },
  32. ["effect2"] = { startDelay = 0.4, duration = 1.2, speed = 1 },
  33. };
  34. local MAJOR_BLUE_GEM_MODEL_SCENE_INFO = StaticModelInfo.CreateModelSceneEntry(287, 165995); -- BlueGlow_High.m2
  35. local MAJOR_PURPLE_GEM_MODEL_SCENE_INFO = StaticModelInfo.CreateModelSceneEntry(288, 166008); -- PurpleGlow_High.m2
  36. local MINOR_PURPLE_GEM_MODEL_SCENE_INFO = StaticModelInfo.CreateModelSceneEntry(289, 166008); -- PurpleGlow_High.m2
  37. local RANKED_MILESTONE_MODEL_SCENE_INFO = StaticModelInfo.CreateModelSceneEntry(316, 1983524); -- 8FX_Azerite_Generic_PrecastHand.m2
  38. local LEARN_SHAKE_DELAY = 0.869;
  39. local LEARN_SHAKE = { { x = 0, y = -20}, { x = 0, y = 20}, { x = 0, y = -20}, { x = 0, y = 20}, { x = -9, y = -8}, { x = 8, y = 8}, { x = -3, y = -8}, { x = 9, y = 8}, { x = -3, y = -1}, { x = 2, y = 2}, { x = -2, y = -3}, { x = -1, y = -1}, { x = 4, y = 2}, { x = 3, y = 4}, { x = -3, y = 4}, { x = 4, y = -4}, { x = -4, y = 2}, { x = -2, y = 1}, { x = -3, y = -1}, { x = 2, y = 2}, { x = -2, y = -3}, { x = -1, y = -1}, { x = 4, y = 2}, { x = 3, y = 4}, { x = -3, y = 4}, { x = 4, y = -4}, { x = -4, y = 2}, { x = -2, y = 1}, { x = -3, y = -1}, { x = 2, y = 2}, { x = -2, y = -3}, { x = -1, y = -1}, { x = 4, y = 2}, { x = 3, y = 4}, { x = -3, y = 4}, { x = 4, y = -4}, { x = -4, y = 2}, { x = -2, y = 1}, { x = -3, y = -1}, { x = 2, y = 2}, { x = -2, y = -3}, { x = -1, y = -1}, { x = 4, y = 2}, { x = 3, y = 4}, { x = -3, y = 4}, { x = 4, y = -4}, { x = -4, y = 2}, { x = -2, y = 1}, { x = -3, y = -1}, { x = 2, y = 2}, { x = -2, y = -3}, { x = -1, y = -1}, { x = 4, y = 2}, { x = 3, y = 4}, { x = -3, y = 4}, { x = 4, y = -4}, { x = -4, y = 2}, { x = -2, y = 1}, { x = -3, y = -1}, { x = 2, y = 2}, { x = -2, y = -3}, { x = -1, y = -1}, { x = 4, y = 2}, { x = 3, y = 4}, { x = -3, y = 4}, { x = 4, y = -4}, { x = -4, y = 2}, { x = -2, y = 1}, };
  40. local LEARN_SHAKE_DURATION = 0.20;
  41. local LEARN_SHAKE_FREQUENCY = 0.001;
  42. local REVEAL_SHAKE_DELAY = 0.869;
  43. local REVEAL_SHAKE = { { x = 0, y = -44}, { x = 0, y = 44}, { x = 0, y = -44}, { x = 0, y = 44}, { x = -9, y = -32}, { x = 8, y = 32}, { x = -3, y = -32}, { x = 9, y = 32}, { x = -11, y = -32}, { x = 1, y = 32}, { x = -13, y = -32}, { x = 7, y = 32}, { x = -3, y = -1}, { x = 2, y = 2}, { x = -2, y = -3}, { x = -1, y = -1}, { x = 4, y = 2}, { x = 3, y = 4}, { x = -3, y = 4}, { x = 4, y = -4}, { x = -4, y = 2}, { x = -2, y = 1}, { x = -3, y = -1}, { x = 2, y = 2}, { x = -2, y = -3}, { x = -1, y = -1}, { x = 4, y = 2}, { x = 3, y = 4}, { x = -3, y = 4}, { x = 4, y = -4}, { x = -4, y = 2}, { x = -2, y = 1}, { x = -3, y = -1}, { x = 2, y = 2}, { x = -2, y = -3}, { x = -1, y = -1}, { x = 4, y = 2}, { x = 3, y = 4}, { x = -3, y = 4}, { x = 4, y = -4}, { x = -4, y = 2}, { x = -2, y = 1}, { x = -3, y = -1}, { x = 2, y = 2}, { x = -2, y = -3}, { x = -1, y = -1}, { x = 4, y = 2}, { x = 3, y = 4}, { x = -3, y = 4}, { x = 4, y = -4}, { x = -4, y = 2}, { x = -2, y = 1}, { x = -3, y = -1}, { x = 2, y = 2}, { x = -2, y = -3}, { x = -1, y = -1}, { x = 4, y = 2}, { x = 3, y = 4}, { x = -3, y = 4}, { x = 4, y = -4}, { x = -4, y = 2}, { x = -2, y = 1}, { x = -3, y = -1}, { x = 2, y = 2}, { x = -2, y = -3}, { x = -1, y = -1}, { x = 4, y = 2}, { x = 3, y = 4}, { x = -3, y = 4}, { x = 4, y = -4}, { x = -4, y = 2}, { x = -2, y = 1}, };
  44. local REVEAL_SHAKE_DURATION = 0.40;
  45. local REVEAL_SHAKE_FREQUENCY = 0.001;
  46. local REVEAL_START_DELAY = 0.9;
  47. local REVEAL_DELAY_SECS_PER_DISTANCE = 0.0036;
  48. local REVEAL_LINE_DURATION_SECS_PER_DISTANCE = 0.0012;
  49. local REVEAL_SWIRL_SLOT_SCALE = 1;
  50. local REVEAL_SWIRL_STAMINA_SCALE = 0.5;
  51. local MAX_ESSENCE_RANK = 4;
  52. local AZERITE_ESSENCE_FRAME_EVENTS = {
  53. "UI_MODEL_SCENE_INFO_UPDATED",
  54. "AZERITE_ESSENCE_CHANGED",
  55. "AZERITE_ESSENCE_ACTIVATED",
  56. "AZERITE_ESSENCE_ACTIVATION_FAILED",
  57. "AZERITE_ESSENCE_UPDATE",
  58. "AZERITE_ESSENCE_FORGE_OPEN",
  59. "AZERITE_ESSENCE_FORGE_CLOSE",
  60. "AZERITE_ESSENCE_MILESTONE_UNLOCKED",
  61. "AZERITE_ITEM_POWER_LEVEL_CHANGED",
  62. "AZERITE_ITEM_ENABLED_STATE_CHANGED",
  63. };
  64. local MILESTONE_LOCATIONS = {
  65. [1] = { left = 238, top = -235 },
  66. [2] = { left = 101, top = -270 },
  67. [3] = { left = 155, top = -349 },
  68. [4] = { left = 247, top = -375 },
  69. [5] = { left = 336, top = -337 },
  70. [6] = { left = 377, top = -250 },
  71. [7] = { left = 356, top = -156 },
  72. [8] = { left = 278, top = -99 },
  73. [9] = { left = 179, top = -106 },
  74. [10] = { left = 111, top = -174 },
  75. [11] = { left = 103, top = -191 },
  76. };
  77. local LOCKED_RUNE_ATLASES = { "heartofazeroth-slot-minor-unlearned-bottomleft", "heartofazeroth-slot-minor-unlearned-topright", "heartofazeroth-slot-minor-unlearned-3" };
  78. function AzeriteEssenceUIMixin:OnLoad()
  79. CallbackRegistryMixin.OnLoad(self);
  80. self.TopTileStreaks:Hide();
  81. self:SetupModelScene();
  82. self:SetupMilestones();
  83. self:RefreshPowerLevel();
  84. self.OrbGlass.AlphaAnim:Play();
  85. self.ItemModelScene.AlphaAnim:Play();
  86. end
  87. function AzeriteEssenceUIMixin:SetupMilestones()
  88. self.Milestones = { };
  89. self.Lines = { };
  90. local previousMilestoneFrame;
  91. local lockedRuneCount = 0;
  92. local milestones = C_AzeriteEssence.GetMilestones();
  93. for i, milestoneInfo in ipairs(milestones) do
  94. local template;
  95. if milestoneInfo.slot == Enum.AzeriteEssence.MainSlot then
  96. template = "AzeriteMilestoneMajorSlotTemplate";
  97. elseif milestoneInfo.rank then
  98. template = "AzeriteMilestoneRankedTemplate";
  99. elseif milestoneInfo.slot then
  100. template = "AzeriteMilestoneMinorSlotTemplate";
  101. else
  102. template = "AzeriteMilestoneStaminaTemplate";
  103. end
  104. local milestoneFrame = CreateFrame("FRAME", nil, self, template);
  105. milestoneFrame:SetPoint("CENTER", self.OrbBackground, "TOPLEFT", MILESTONE_LOCATIONS[i].left, MILESTONE_LOCATIONS[i].top);
  106. milestoneFrame:SetFrameLevel(1500);
  107. milestoneFrame.milestoneID = milestoneInfo.ID;
  108. milestoneFrame.slot = milestoneInfo.slot;
  109. if milestoneFrame.LockedState then
  110. lockedRuneCount = lockedRuneCount + 1;
  111. local runeAtlas = LOCKED_RUNE_ATLASES[lockedRuneCount];
  112. if runeAtlas then
  113. milestoneFrame.LockedState.Rune:SetAtlas(runeAtlas);
  114. end
  115. end
  116. if previousMilestoneFrame then
  117. local lineContainer = CreateFrame("FRAME", nil, self, "AzeriteEssenceDependencyLineTemplate");
  118. lineContainer:SetConnectedColor(CONNECTED_LINE_COLOR);
  119. lineContainer:SetDisconnectedColor(DISCONNECTED_LINE_COLOR);
  120. lineContainer:SetThickness(6);
  121. lineContainer.Background:Hide();
  122. local fromCenter = CreateVector2D(previousMilestoneFrame:GetCenter());
  123. fromCenter:ScaleBy(previousMilestoneFrame:GetEffectiveScale());
  124. local toCenter = CreateVector2D(milestoneFrame:GetCenter());
  125. toCenter:ScaleBy(milestoneFrame:GetEffectiveScale());
  126. toCenter:Subtract(fromCenter);
  127. lineContainer:CalculateTiling(toCenter:GetLength());
  128. lineContainer:SetEndPoints(previousMilestoneFrame, milestoneFrame);
  129. lineContainer:SetScrollAnimationProgressOffset(0);
  130. milestoneFrame.linkLine = lineContainer;
  131. lineContainer.fromButton = previousMilestoneFrame;
  132. lineContainer.toButton = milestoneFrame;
  133. tinsert(self.Lines, lineContainer);
  134. end
  135. tinsert(self.Milestones, milestoneFrame);
  136. previousMilestoneFrame = milestoneFrame;
  137. end
  138. end
  139. function AzeriteEssenceUIMixin:OnEvent(event, ...)
  140. if event == "UI_MODEL_SCENE_INFO_UPDATED" then
  141. self:SetupModelScene();
  142. elseif event == "AZERITE_ESSENCE_CHANGED" then
  143. local essenceID, rank = ...;
  144. self:RefreshSlots();
  145. self.EssenceList:Update();
  146. self.EssenceList:OnEssenceChanged(essenceID);
  147. AzeriteEssenceLearnAnimFrame:PlayAnim();
  148. if rank < MAX_ESSENCE_RANK then
  149. PlaySound(SOUNDKIT.UI_82_HEARTOFAZEROTH_LEARNESSENCE_ANIM);
  150. else
  151. PlaySound(SOUNDKIT.UI_82_HEARTOFAZEROTH_LEARNESSENCE_ANIM_RANK4);
  152. end
  153. elseif event == "AZERITE_ESSENCE_ACTIVATED" or event == "AZERITE_ESSENCE_ACTIVATION_FAILED" or event == "AZERITE_ESSENCE_UPDATE" then
  154. self:ClearNewlyActivatedEssence();
  155. self:RefreshSlots();
  156. self.EssenceList:Update();
  157. elseif event == "AZERITE_ESSENCE_FORGE_OPEN" or event == "AZERITE_ESSENCE_FORGE_CLOSE" then
  158. self:RefreshMilestones();
  159. elseif event == "AZERITE_ESSENCE_MILESTONE_UNLOCKED" then
  160. self:RefreshMilestones();
  161. local milestoneID = ...;
  162. local milestoneFrame = self:GetMilestoneFrame(milestoneID);
  163. if milestoneFrame then
  164. milestoneFrame:OnUnlocked();
  165. end
  166. elseif event == "AZERITE_ITEM_POWER_LEVEL_CHANGED" then
  167. self:RefreshPowerLevel();
  168. self:RefreshMilestones();
  169. elseif event == "AZERITE_ITEM_ENABLED_STATE_CHANGED" then
  170. self:UpdateEnabledState();
  171. self:RefreshPowerLevel();
  172. self:RefreshMilestones();
  173. self:RefreshSlots();
  174. self.EssenceList:Update();
  175. self:UpdateEnabledAppearance();
  176. end
  177. end
  178. function AzeriteEssenceUIMixin:OnShow()
  179. -- portrait and title
  180. local itemLocation = C_AzeriteItem.FindActiveAzeriteItem();
  181. if itemLocation then
  182. local item = Item:CreateFromItemLocation(itemLocation);
  183. item:ContinueOnItemLoad(function()
  184. self:SetPortraitToAsset(item:GetItemIcon());
  185. self:SetTitle(item:GetItemName());
  186. end);
  187. end
  188. FrameUtil.RegisterFrameForEvents(self, AZERITE_ESSENCE_FRAME_EVENTS);
  189. self:RefreshPowerLevel();
  190. self:RefreshMilestones();
  191. self:UpdateEnabledAppearance();
  192. PlaySound(SOUNDKIT.UI_82_HEARTOFAZEROTH_WINDOW_OPEN);
  193. self:TriggerEvent(AzeriteEssenceUIMixin.Event.OnShow);
  194. end
  195. function AzeriteEssenceUIMixin:OnHide()
  196. if C_AzeriteEssence:IsAtForge() then
  197. C_AzeriteEssence:CloseForge();
  198. CloseAllBags(self);
  199. end
  200. if self.itemDataLoadedCancelFunc then
  201. self.itemDataLoadedCancelFunc();
  202. self.itemDataLoadedCancelFunc = nil;
  203. end
  204. if self.numRevealsPlaying then
  205. self:CancelReveal();
  206. end
  207. self.shouldPlayReveal = nil;
  208. FrameUtil.UnregisterFrameForEvents(self, AZERITE_ESSENCE_FRAME_EVENTS);
  209. self:ClearNewlyActivatedEssence();
  210. -- clean up anims
  211. self.ActivationGlow.Anim:Stop();
  212. self.ActivationGlow:SetAlpha(0);
  213. AzeriteEssenceLearnAnimFrame:StopAnim();
  214. PlaySound(SOUNDKIT.UI_82_HEARTOFAZEROTH_WINDOW_CLOSE);
  215. self:TriggerEvent(AzeriteEssenceUIMixin.Event.OnHide);
  216. end
  217. function AzeriteEssenceUIMixin:UpdateEnabledState()
  218. local azeriteItemLocation = C_AzeriteItem.FindActiveAzeriteItem();
  219. self.isAzeriteItemEnabled = azeriteItemLocation and C_AzeriteItem.IsAzeriteItemEnabled(azeriteItemLocation) or false;
  220. end
  221. function AzeriteEssenceUIMixin:IsAzeriteItemEnabled()
  222. if self.isAzeriteItemEnabled == nil then
  223. self:UpdateEnabledState();
  224. end
  225. return self.isAzeriteItemEnabled;
  226. end
  227. function AzeriteEssenceUIMixin:UpdateEnabledAppearance()
  228. local isEnabled = self:IsAzeriteItemEnabled();
  229. self.DisabledFrame:SetShown(not isEnabled);
  230. self.OrbRing:SetDesaturated(not isEnabled);
  231. self.ItemModelScene:SetDesaturation(isEnabled and 0 or 1);
  232. self.ItemModelScene:SetPaused(not isEnabled);
  233. for index, starFrame in ipairs(self.StarsAnimations) do
  234. starFrame.Anim:SetPlaying(isEnabled);
  235. end
  236. end
  237. function AzeriteEssenceUIMixin:OnMouseUp(mouseButton)
  238. if mouseButton == "LeftButton" or mouseButton == "RightButton" then
  239. C_AzeriteEssence.ClearPendingActivationEssence();
  240. end
  241. end
  242. function AzeriteEssenceUIMixin:TryShow()
  243. if C_AzeriteEssence.CanOpenUI() then
  244. ShowUIPanel(AzeriteEssenceUI);
  245. return true;
  246. end
  247. return false;
  248. end
  249. function AzeriteEssenceUIMixin:ShouldOpenBagsOnShow()
  250. return C_AzeriteEssence.GetNumUnlockedEssences() > 0;
  251. end
  252. function AzeriteEssenceUIMixin:OnEssenceActivated(essenceID, slotFrame)
  253. self:SetNewlyActivatedEssence(essenceID, slotFrame.milestoneID);
  254. self.ActivationGlow.Anim:Stop();
  255. self.ActivationGlow.Anim:Play();
  256. if self:ShouldPlayReveal() then
  257. self.revealInProgress = true;
  258. PlaySound(SOUNDKIT.UI_82_HEARTOFAZEROTH_SLOTFIRSTESSENCE);
  259. slotFrame:PlayRevealEffect();
  260. ScriptAnimationUtil.ShakeFrame(self:GetParent(), REVEAL_SHAKE, REVEAL_SHAKE_DURATION, REVEAL_SHAKE_FREQUENCY);
  261. C_Timer.After(REVEAL_START_DELAY,
  262. function()
  263. self:PlayReveal();
  264. end
  265. );
  266. else
  267. local soundID = SOUNDKIT.UI_82_HEARTOFAZEROTH_SLOTESSENCE;
  268. if slotFrame:IsMajorSlot() then
  269. local essenceInfo = C_AzeriteEssence.GetEssenceInfo(essenceID);
  270. if essenceInfo.rank == MAX_ESSENCE_RANK then
  271. soundID = SOUNDKIT.UI_82_HEARTOFAZEROTH_SLOTMAJORESSENCE_RANK4;
  272. end
  273. end
  274. PlaySound(soundID);
  275. end
  276. self:RefreshSlots();
  277. C_AzeriteEssence.ClearPendingActivationEssence();
  278. end
  279. function AzeriteEssenceUIMixin:RefreshPowerLevel()
  280. local azeriteItemLocation = C_AzeriteItem.FindActiveAzeriteItem();
  281. if azeriteItemLocation then
  282. local level = C_AzeriteItem.GetPowerLevel(azeriteItemLocation);
  283. self.PowerLevelBadgeFrame.Label:SetText(level);
  284. self.PowerLevelBadgeFrame:Show();
  285. self.powerLevel = level;
  286. else
  287. self.PowerLevelBadgeFrame:Hide();
  288. self.powerLevel = 0;
  289. end
  290. end
  291. function AzeriteEssenceUIMixin:MeetsPowerLevel(level)
  292. return level <= self.powerLevel;
  293. end
  294. function AzeriteEssenceUIMixin:OnEnterPowerLevelBadgeFrame()
  295. local itemLocation = C_AzeriteItem.FindActiveAzeriteItem();
  296. if itemLocation then
  297. local item = Item:CreateFromItemLocation(itemLocation);
  298. self.itemDataLoadedCancelFunc = item:ContinueWithCancelOnItemLoad(function()
  299. GameTooltip:SetOwner(self.PowerLevelBadgeFrame, "ANCHOR_RIGHT", -7, -6);
  300. GameTooltip_SetTitle(GameTooltip, item:GetItemName(), item:GetItemQualityColor().color);
  301. GameTooltip_AddColoredLine(GameTooltip, string.format(HEART_OF_AZEROTH_LEVEL, self.powerLevel), WHITE_FONT_COLOR);
  302. GameTooltip:Show();
  303. end);
  304. end
  305. end
  306. function AzeriteEssenceUIMixin:OnLeavePowerLevelBadgeFrame()
  307. GameTooltip:Hide();
  308. if self.itemDataLoadedCancelFunc then
  309. self.itemDataLoadedCancelFunc();
  310. self.itemDataLoadedCancelFunc = nil;
  311. end
  312. end
  313. function AzeriteEssenceUIMixin:RefreshMilestones()
  314. for i, milestoneFrame in ipairs(self.Milestones) do
  315. -- Main slot is always present
  316. if self:ShouldPlayReveal() and (not milestoneFrame.slot or not milestoneFrame:IsMajorSlot()) then
  317. milestoneFrame:Hide();
  318. else
  319. milestoneFrame:Show();
  320. milestoneFrame:Refresh();
  321. end
  322. end
  323. for i, lineContainer in ipairs(self.Lines) do
  324. if self:ShouldPlayReveal() then
  325. lineContainer:Hide();
  326. else
  327. lineContainer:Show();
  328. lineContainer:Refresh();
  329. end
  330. end
  331. end
  332. function AzeriteEssenceUIMixin:RefreshSlots()
  333. for i, slotButton in ipairs(self.Slots) do
  334. slotButton:Refresh();
  335. end
  336. end
  337. function AzeriteEssenceUIMixin:GetSlotFrame(slot)
  338. for _, slotFrame in ipairs(self.Slots) do
  339. if slotFrame.slot == slot then
  340. return slotFrame;
  341. end
  342. end
  343. return nil;
  344. end
  345. function AzeriteEssenceUIMixin:GetMilestoneFrame(milestoneID)
  346. for _, milestoneFrame in ipairs(self.Milestones) do
  347. if milestoneFrame.milestoneID == milestoneID then
  348. return milestoneFrame;
  349. end
  350. end
  351. return nil;
  352. end
  353. function AzeriteEssenceUIMixin:SetupModelScene()
  354. local forceUpdate = true;
  355. StaticModelInfo.SetupModelScene(self.ItemModelScene, HEART_MODEL_SCENE_INFO, forceUpdate);
  356. end
  357. function AzeriteEssenceUIMixin:SetNewlyActivatedEssence(essenceID, milestoneID)
  358. self.newlyActivatedEssenceID = essenceID;
  359. self.newlyActivatedEssenceMilestoneID = milestoneID;
  360. end
  361. function AzeriteEssenceUIMixin:GetNewlyActivatedEssence()
  362. return self.newlyActivatedEssenceID, self.newlyActivatedEssenceMilestoneID;
  363. end
  364. function AzeriteEssenceUIMixin:HasNewlyActivatedEssence()
  365. return self.newlyActivatedEssenceID ~= nil;
  366. end
  367. function AzeriteEssenceUIMixin:ClearNewlyActivatedEssence()
  368. self.newlyActivatedEssenceID = nil;
  369. self.newlyActivatedEssenceMilestoneID = nil;
  370. end
  371. function AzeriteEssenceUIMixin:GetSlotEssences()
  372. local slotEssences = { };
  373. for i, slotFrame in ipairs(self.Slots) do
  374. local essenceID = self:GetEffectiveEssence(slotFrame.milestoneID);
  375. if essenceID then
  376. slotEssences[essenceID] = slotFrame.slot;
  377. end
  378. end
  379. return slotEssences;
  380. end
  381. function AzeriteEssenceUIMixin:GetEffectiveEssence(milestoneID)
  382. if not milestoneID then
  383. return nil;
  384. end
  385. local newlyActivatedEssenceID, newlyActivatedEssenceMilestoneID = self:GetNewlyActivatedEssence();
  386. if milestoneID == newlyActivatedEssenceMilestoneID then
  387. return newlyActivatedEssenceID;
  388. end
  389. local essenceID = C_AzeriteEssence.GetMilestoneEssence(milestoneID);
  390. if essenceID == newlyActivatedEssenceID then
  391. return nil;
  392. else
  393. return essenceID;
  394. end
  395. end
  396. function AzeriteEssenceUIMixin:ShouldPlayReveal()
  397. if self.shouldPlayReveal == nil then
  398. self.shouldPlayReveal = C_AzeriteEssence:HasNeverActivatedAnyEssences();
  399. end
  400. return self.shouldPlayReveal;
  401. end
  402. function AzeriteEssenceUIMixin:IsRevealInProgress()
  403. return not not self.revealInProgress;
  404. end
  405. function AzeriteEssenceUIMixin:PlayReveal()
  406. if not self.revealSwirlPool then
  407. self.numRevealsPlaying = 0;
  408. self.revealSwirlPool = CreateFramePool("FRAME", self, "PowerSwirlAnimationTemplate");
  409. local previousFrame;
  410. local totalDistance = 0;
  411. for i, milestoneFrame in ipairs(self.Milestones) do
  412. if previousFrame then
  413. local delay = totalDistance * REVEAL_DELAY_SECS_PER_DISTANCE;
  414. local distance = RegionUtil.CalculateDistanceBetween(previousFrame, milestoneFrame);
  415. milestoneFrame:BeginReveal(delay);
  416. self:ApplyRevealSwirl(milestoneFrame, delay);
  417. milestoneFrame.linkLine:BeginReveal(delay, distance);
  418. self.numRevealsPlaying = self.numRevealsPlaying + 1;
  419. totalDistance = totalDistance + distance;
  420. end
  421. previousFrame = milestoneFrame;
  422. end
  423. PlaySound(SOUNDKIT.UI_82_HEARTOFAZEROTH_NODESREVEAL);
  424. end
  425. end
  426. function AzeriteEssenceUIMixin:ApplyRevealSwirl(milestoneFrame, delay)
  427. local swirlFrame = self.revealSwirlPool:Acquire();
  428. swirlFrame:SetAllPoints(milestoneFrame);
  429. swirlFrame:SetFrameLevel(milestoneFrame:GetFrameLevel() + 1);
  430. swirlFrame:SetScale(milestoneFrame.slot and REVEAL_SWIRL_SLOT_SCALE or REVEAL_SWIRL_STAMINA_SCALE);
  431. swirlFrame.timer = C_Timer.NewTimer(delay,
  432. function()
  433. swirlFrame:Show();
  434. swirlFrame.SelectedAnim:Play();
  435. end
  436. );
  437. end
  438. function AzeriteEssenceUIMixin:CancelReveal()
  439. for i, milestoneFrame in ipairs(self.Milestones) do
  440. milestoneFrame:CancelReveal();
  441. end
  442. for i, lineContainer in ipairs(self.Lines) do
  443. lineContainer:CancelReveal();
  444. end
  445. for swirlFrame in self.revealSwirlPool:EnumerateActive() do
  446. if swirlFrame.timer then
  447. swirlFrame.timer:Cancel();
  448. end
  449. swirlFrame.SelectedAnim:Stop();
  450. end
  451. self.revealSwirlPool:ReleaseAll();
  452. self.numRevealsPlaying = nil;
  453. self.revealInProgress = false;
  454. end
  455. function AzeriteEssenceUIMixin:OnSwirlAnimationFinished()
  456. self.numRevealsPlaying = self.numRevealsPlaying - 1;
  457. if self.numRevealsPlaying == 0 then
  458. self.numRevealsPlaying = nil;
  459. self.revealSwirlPool:ReleaseAll();
  460. self.shouldPlayReveal = false;
  461. self.revealInProgress = false;
  462. self:RefreshMilestones();
  463. end
  464. end
  465. AzeriteEssenceDependencyLineMixin = CreateFromMixins(PowerDependencyLineMixin);
  466. function AzeriteEssenceDependencyLineMixin:SetDisconnected()
  467. self.FillScroll1:SetVertexColor(self.disconnectedColor:GetRGB());
  468. self.FillScroll2:SetVertexColor(self.disconnectedColor:GetRGB());
  469. PowerDependencyLineMixin.SetDisconnected(self);
  470. end
  471. function AzeriteEssenceDependencyLineMixin:Refresh()
  472. local isAzeriteItemEnabled = self:GetParent():IsAzeriteItemEnabled();
  473. if self.toButton.unlocked and isAzeriteItemEnabled then
  474. self:SetState(PowerDependencyLineMixin.LINE_STATE_CONNECTED);
  475. self:SetAlpha(0.2);
  476. else
  477. if self.fromButton.unlocked and self.toButton.canUnlock and isAzeriteItemEnabled then
  478. self:SetDisconnectedColor(DISCONNECTED_LINE_COLOR);
  479. self:SetState(PowerDependencyLineMixin.LINE_STATE_DISCONNECTED);
  480. self:SetAlpha(0.08);
  481. else
  482. self:SetDisconnectedColor(LOCKED_LINE_COLOR);
  483. self:SetState(PowerDependencyLineMixin.LINE_STATE_DISCONNECTED);
  484. self:SetAlpha(0.08);
  485. end
  486. end
  487. end
  488. function AzeriteEssenceDependencyLineMixin:BeginReveal(delay, distance)
  489. self:Show();
  490. self:SetState(PowerDependencyLineMixin.LINE_STATE_CONNECTED);
  491. PowerDependencyLineMixin.BeginReveal(self, delay, distance * REVEAL_LINE_DURATION_SECS_PER_DISTANCE);
  492. end
  493. function AzeriteEssenceDependencyLineMixin:CancelReveal()
  494. self.RevealAnim:Stop();
  495. end
  496. function AzeriteEssenceDependencyLineMixin:OnRevealFinished()
  497. self:Refresh();
  498. end
  499. AzeriteEssenceListMixin = { };
  500. function AzeriteEssenceListMixin:OnLoad()
  501. self.ScrollBar.doNotHide = true;
  502. self.update = function() self:Refresh(); end
  503. self.dynamic = function(...) return self:CalculateScrollOffset(...); end
  504. HybridScrollFrame_CreateButtons(self, "AzeriteEssenceButtonTemplate", 4, -ESSENCE_LIST_PADDING, "TOPLEFT", "TOPLEFT", 0, -ESSENCE_BUTTON_OFFSET, "TOP", "BOTTOM");
  505. self.HeaderButton:SetParent(self.ScrollChild);
  506. self:RegisterEvent("VARIABLES_LOADED");
  507. self.collapsed = GetCVarBool("otherRolesAzeriteEssencesHidden");
  508. self:CheckAndSetUpLearnEffect();
  509. end
  510. function AzeriteEssenceListMixin:OnShow()
  511. self:Update();
  512. self:RegisterEvent("UI_MODEL_SCENE_INFO_UPDATED");
  513. self:RegisterEvent("PENDING_AZERITE_ESSENCE_CHANGED");
  514. end
  515. function AzeriteEssenceListMixin:OnHide()
  516. self:CleanUpLearnEssence();
  517. self:UnregisterEvent("UI_MODEL_SCENE_INFO_UPDATED");
  518. self:UnregisterEvent("PENDING_AZERITE_ESSENCE_CHANGED");
  519. C_AzeriteEssence.ClearPendingActivationEssence();
  520. end
  521. function AzeriteEssenceListMixin:OnEvent(event)
  522. if event == "UI_MODEL_SCENE_INFO_UPDATED" then
  523. self.LearnEssenceModelScene.effect = nil;
  524. elseif event == "PENDING_AZERITE_ESSENCE_CHANGED" then
  525. self:Refresh();
  526. elseif event == "VARIABLES_LOADED" then
  527. self.collapsed = GetCVarBool("otherRolesAzeriteEssencesHidden");
  528. self:Refresh();
  529. end
  530. end
  531. function AzeriteEssenceListMixin:Update()
  532. self:CacheAndSortEssences();
  533. self:Refresh();
  534. end
  535. function AzeriteEssenceListMixin:SetPendingEssence(essenceID)
  536. local essenceInfo = C_AzeriteEssence.GetEssenceInfo(essenceID);
  537. if essenceInfo and essenceInfo.unlocked and essenceInfo.valid then
  538. C_AzeriteEssence.SetPendingActivationEssence(essenceID);
  539. PlaySound(SOUNDKIT.UI_82_HEARTOFAZEROTH_SELECTESSENCE);
  540. end
  541. end
  542. local function SortComparison(entry1, entry2)
  543. if ( entry1.valid ~= entry2.valid ) then
  544. return entry1.valid;
  545. end
  546. if ( entry1.unlocked ~= entry2.unlocked ) then
  547. return entry1.unlocked;
  548. end
  549. if ( entry1.rank ~= entry2.rank ) then
  550. return entry1.rank > entry2.rank;
  551. end
  552. return strcmputf8i(entry1.name, entry2.name) < 0;
  553. end
  554. function AzeriteEssenceListMixin:CacheAndSortEssences()
  555. self.essences = C_AzeriteEssence.GetEssences();
  556. if not self.essences then
  557. return;
  558. end
  559. table.sort(self.essences, SortComparison);
  560. self.headerIndex = nil;
  561. for i, essenceInfo in ipairs(self.essences) do
  562. if not essenceInfo.valid then
  563. self.headerIndex = i;
  564. local headerInfo = { name = "Header", isHeader = true };
  565. tinsert(self.essences, i, headerInfo);
  566. break;
  567. end
  568. end
  569. end
  570. function AzeriteEssenceListMixin:CheckAndSetUpLearnEffect()
  571. local scene = self.LearnEssenceModelScene;
  572. if not scene.effect then
  573. local forceUpdate = true;
  574. local stopAnim = true;
  575. scene.effect = StaticModelInfo.SetupModelScene(scene, LEARN_MODEL_SCENE_INFO, forceUpdate, stopAnim);
  576. end
  577. end
  578. function AzeriteEssenceListMixin:GetNumViewableEssences()
  579. if not self:ShouldShowInvalidEssences() and self.headerIndex then
  580. return self.headerIndex;
  581. else
  582. return #self:GetCachedEssences();
  583. end
  584. end
  585. function AzeriteEssenceListMixin:ToggleHeader()
  586. self.collapsed = not self.collapsed;
  587. SetCVar("otherRolesAzeriteEssencesHidden", self.collapsed);
  588. self:Refresh();
  589. end
  590. function AzeriteEssenceListMixin:ForceOpenHeader()
  591. self.collapsed = false;
  592. end
  593. function AzeriteEssenceListMixin:ShouldShowInvalidEssences()
  594. return not self.collapsed;
  595. end
  596. function AzeriteEssenceListMixin:HasHeader()
  597. return self.headerIndex ~= nil;
  598. end
  599. function AzeriteEssenceListMixin:GetHeaderIndex()
  600. return self.headerIndex;
  601. end
  602. function AzeriteEssenceListMixin:GetCachedEssences()
  603. return self.essences or {};
  604. end
  605. function AzeriteEssenceListMixin:OnEssenceChanged(essenceID)
  606. if self.learnEssenceButton then
  607. return;
  608. end
  609. -- locate the appropriate button
  610. local essences = self:GetCachedEssences();
  611. local headerIndex = self:GetHeaderIndex();
  612. for index, essenceInfo in ipairs(essences) do
  613. if essenceInfo.ID == essenceID then
  614. -- open the header if closed and the essence is invalid
  615. if headerIndex and index > headerIndex and not self:ShouldShowInvalidEssences() then
  616. self:ForceOpenHeader();
  617. end
  618. -- scroll to the essence
  619. local getHeightFunc = function(index)
  620. if index == headerIndex then
  621. return ESSENCE_HEADER_HEIGHT + ESSENCE_BUTTON_OFFSET;
  622. else
  623. return ESSENCE_BUTTON_HEIGHT + ESSENCE_BUTTON_OFFSET;
  624. end
  625. end
  626. HybridScrollFrame_ScrollToIndex(self, index, getHeightFunc);
  627. -- find the button
  628. for i, button in ipairs(self.buttons) do
  629. if button.essenceID == essenceID then
  630. self.learnEssenceButton = button;
  631. break;
  632. end
  633. end
  634. break;
  635. end
  636. end
  637. if self.learnEssenceButton then
  638. -- disable the scrollbar
  639. ScrollBar_Disable(self.scrollBar);
  640. -- play glow
  641. self.learnEssenceButton.Glow.Anim:Play();
  642. self.learnEssenceButton.Glow2.Anim:Play();
  643. self.learnEssenceButton.Glow3.Anim:Play();
  644. -- scene
  645. local scene = self.LearnEssenceModelScene;
  646. scene:SetPoint("CENTER", self.learnEssenceButton);
  647. self:CheckAndSetUpLearnEffect();
  648. scene:ShowAndAnimateActors(LEARN_MODEL_SCENE_ACTOR_SETTINGS);
  649. C_Timer.After(2.969, function() self:CleanUpLearnEssence(); end);
  650. end
  651. end
  652. function AzeriteEssenceListMixin:CleanUpLearnEssence()
  653. if not self.learnEssenceButton then
  654. return;
  655. end
  656. self.learnEssenceButton.Glow.Anim:Stop();
  657. self.learnEssenceButton.Glow2.Anim:Stop();
  658. self.learnEssenceButton.Glow3.Anim:Stop();
  659. self.learnEssenceButton.Glow:SetAlpha(0);
  660. self.learnEssenceButton.Glow2:SetAlpha(0);
  661. self.learnEssenceButton.Glow3:SetAlpha(0);
  662. self.learnEssenceButton = nil;
  663. self.LearnEssenceModelScene:Hide();
  664. self:Refresh();
  665. end
  666. function AzeriteEssenceListMixin:CalculateScrollOffset(offset)
  667. local usedHeight = 0;
  668. local essences = self:GetCachedEssences();
  669. for i = 1, self:GetNumViewableEssences() do
  670. local essence = essences[i];
  671. local height;
  672. if essence.isHeader then
  673. height = ESSENCE_HEADER_HEIGHT + ESSENCE_BUTTON_OFFSET;
  674. else
  675. height = ESSENCE_BUTTON_HEIGHT + ESSENCE_BUTTON_OFFSET;
  676. end
  677. if ( usedHeight + height >= offset ) then
  678. return i - 1, offset - usedHeight;
  679. else
  680. usedHeight = usedHeight + height;
  681. end
  682. end
  683. return 0, 0;
  684. end
  685. function AzeriteEssenceListMixin:Refresh()
  686. local essences = self:GetCachedEssences();
  687. local numEssences = self:GetNumViewableEssences();
  688. local parent = self:GetParent();
  689. local isAzeriteItemEnabled = parent:IsAzeriteItemEnabled();
  690. local slotEssences = parent:GetSlotEssences();
  691. local pendingEssenceID = C_AzeriteEssence.GetPendingActivationEssence();
  692. local hasUnlockedEssence = false;
  693. self.HeaderButton:Hide();
  694. local offset = HybridScrollFrame_GetOffset(self);
  695. local totalHeight = numEssences * (ESSENCE_BUTTON_HEIGHT + ESSENCE_BUTTON_OFFSET) + ESSENCE_LIST_PADDING * 2;
  696. if self:HasHeader() then
  697. totalHeight = totalHeight + ESSENCE_HEADER_HEIGHT - ESSENCE_BUTTON_HEIGHT;
  698. end
  699. for i, button in ipairs(self.buttons) do
  700. local index = offset + i;
  701. if index <= numEssences then
  702. local essenceInfo = essences[index];
  703. if essenceInfo.isHeader then
  704. button:SetHeight(ESSENCE_HEADER_HEIGHT);
  705. button:Hide();
  706. self.HeaderButton:SetPoint("BOTTOM", button, 0, 0);
  707. self.HeaderButton:Show();
  708. if self:ShouldShowInvalidEssences() then
  709. self.HeaderButton.ExpandedIcon:Show();
  710. self.HeaderButton.CollapsedIcon:Hide();
  711. else
  712. self.HeaderButton.ExpandedIcon:Hide();
  713. self.HeaderButton.CollapsedIcon:Show();
  714. end
  715. else
  716. button:SetHeight(ESSENCE_BUTTON_HEIGHT);
  717. button.Icon:SetTexture(essenceInfo.icon);
  718. button.Name:SetText(essenceInfo.name);
  719. local activatedMarker;
  720. if essenceInfo.unlocked then
  721. local color = isAzeriteItemEnabled and ITEM_QUALITY_COLORS[essenceInfo.rank + 1] or LOCKED_FONT_COLOR; -- min shown quality is uncommon
  722. button.Name:SetTextColor(color.r, color.g, color.b);
  723. button.Icon:SetDesaturated(not essenceInfo.valid or not isAzeriteItemEnabled);
  724. button.Icon:SetVertexColor((isAzeriteItemEnabled and HIGHLIGHT_FONT_COLOR or LOCKED_ICON_COLOR):GetRGB());
  725. button.IconCover:SetShown(not isAzeriteItemEnabled);
  726. button.Background:SetAtlas("heartofazeroth-list-item");
  727. button.Background:SetDesaturated(not isAzeriteItemEnabled);
  728. local essenceSlot = slotEssences[essenceInfo.ID];
  729. if essenceSlot then
  730. if essenceSlot == Enum.AzeriteEssence.MainSlot then
  731. activatedMarker = button.ActivatedMarkerMain;
  732. else
  733. activatedMarker = button.ActivatedMarkerPassive;
  734. end
  735. end
  736. hasUnlockedEssence = true;
  737. else
  738. button.Name:SetTextColor(LOCKED_FONT_COLOR:GetRGB());
  739. button.Icon:SetDesaturated(true);
  740. button.Icon:SetVertexColor(LOCKED_FONT_COLOR:GetRGB());
  741. button.IconCover:Show();
  742. button.Background:SetAtlas("heartofazeroth-list-item-uncollected");
  743. button.Background:SetDesaturated(not isAzeriteItemEnabled);
  744. end
  745. button.PendingGlow:SetShown(essenceInfo.ID == pendingEssenceID and isAzeriteItemEnabled);
  746. button.essenceID = essenceInfo.ID;
  747. button.rank = essenceInfo.rank;
  748. button:Show();
  749. local desaturation = (not isAzeriteItemEnabled) and 1 or 0;
  750. for _, marker in ipairs(button.ActivatedMarkers) do
  751. marker:SetShown(marker == activatedMarker);
  752. marker:DesaturateHierarchy(desaturation);
  753. end
  754. end
  755. else
  756. button:Hide();
  757. end
  758. end
  759. HybridScrollFrame_Update(self, totalHeight, self:GetHeight());
  760. self:UpdateMouseOverTooltip();
  761. parent.RightInset.Background:SetDesaturated(not isAzeriteItemEnabled);
  762. if parent:ShouldPlayReveal() and not parent:IsRevealInProgress() then
  763. ScrollBar_Disable(self.scrollBar);
  764. if hasUnlockedEssence then
  765. local helpTipInfo = {
  766. text = AZERITE_ESSENCE_TUTORIAL_FIRST_ESSENCE,
  767. buttonStyle = HelpTip.ButtonStyle.Close,
  768. targetPoint = HelpTip.Point.TopEdgeCenter,
  769. offsetY = -12,
  770. };
  771. HelpTip:Show(self, helpTipInfo, self.buttons[1].Icon);
  772. else
  773. HelpTip:Hide(self, AZERITE_ESSENCE_TUTORIAL_FIRST_ESSENCE);
  774. end
  775. else
  776. HelpTip:Hide(self, AZERITE_ESSENCE_TUTORIAL_FIRST_ESSENCE);
  777. end
  778. end
  779. function AzeriteEssenceListMixin:UpdateMouseOverTooltip()
  780. for i, button in ipairs(self.buttons) do
  781. -- need to check shown for when mousing over button covered by header
  782. if button:IsMouseOver() and button:IsShown() then
  783. button:OnEnter();
  784. return;
  785. end
  786. end
  787. end
  788. AzeriteEssenceButtonMixin = { };
  789. function AzeriteEssenceButtonMixin:OnEnter()
  790. GameTooltip:SetOwner(self, "ANCHOR_RIGHT");
  791. GameTooltip:SetAzeriteEssence(self.essenceID, self.rank);
  792. GameTooltip:Show();
  793. end
  794. function AzeriteEssenceButtonMixin:OnClick(mouseButton)
  795. if mouseButton == "LeftButton" then
  796. local linkedToChat = false;
  797. if ( IsModifiedClick("CHATLINK") ) then
  798. linkedToChat = HandleModifiedItemClick(C_AzeriteEssence.GetEssenceHyperlink(self.essenceID, self.rank));
  799. end
  800. if ( not linkedToChat ) then
  801. self:GetParent():GetParent():SetPendingEssence(self.essenceID);
  802. end
  803. elseif mouseButton == "RightButton" then
  804. C_AzeriteEssence.ClearPendingActivationEssence();
  805. end
  806. end
  807. AzeriteMilestoneBaseMixin = { };
  808. function AzeriteMilestoneBaseMixin:OnLoad()
  809. if self.isDraggable then
  810. self:RegisterForDrag("LeftButton");
  811. end
  812. self.SwirlContainer:SetScale(self.swirlScale);
  813. end
  814. function AzeriteMilestoneBaseMixin:OnEvent(event, ...)
  815. if event == "UI_MODEL_SCENE_INFO_UPDATED" then
  816. self.EffectsModelScene.primaryEffect = nil;
  817. self.EffectsModelScene.secondaryEffect = nil;
  818. if self.slot or self.rank then
  819. local forceUpdate = true;
  820. self:UpdateModelScenes(forceUpdate);
  821. end
  822. end
  823. end
  824. function AzeriteMilestoneBaseMixin:OnShow()
  825. self:RegisterEvent("UI_MODEL_SCENE_INFO_UPDATED");
  826. end
  827. function AzeriteMilestoneBaseMixin:OnHide()
  828. self:UnregisterEvent("UI_MODEL_SCENE_INFO_UPDATED");
  829. end
  830. function AzeriteMilestoneBaseMixin:OnMouseUp(mouseButton)
  831. if mouseButton == "LeftButton" then
  832. if self.canUnlock and C_AzeriteEssence.IsAtForge() then
  833. C_AzeriteEssence.UnlockMilestone(self.milestoneID);
  834. end
  835. end
  836. end
  837. function AzeriteMilestoneBaseMixin:OnEnter()
  838. local spellID = C_AzeriteEssence.GetMilestoneSpell(self.milestoneID);
  839. if not spellID then
  840. return;
  841. end
  842. GameTooltip:SetOwner(self, "ANCHOR_RIGHT");
  843. local spell = Spell:CreateFromSpellID(spellID);
  844. spell:ContinueWithCancelOnSpellLoad(function()
  845. if GameTooltip:GetOwner() == self then
  846. local wrapText = true;
  847. GameTooltip_SetTitle(GameTooltip, spell:GetSpellName());
  848. GameTooltip_AddColoredLine(GameTooltip, spell:GetSpellDescription(), NORMAL_FONT_COLOR, wrapText);
  849. if not self.unlocked then
  850. self:AddStateToTooltip(AZERITE_ESSENCE_LOCKED_MILESTONE_LEVEL, AZERITE_ESSENCE_UNLOCK_MILESTONE);
  851. end
  852. GameTooltip:Show();
  853. end
  854. end);
  855. end
  856. function AzeriteMilestoneBaseMixin:OnLeave()
  857. if self.UnlockedState then
  858. self.UnlockedState.HighlightRing:Hide();
  859. if self.isDraggable then
  860. self.UnlockedState.DragHighlight:Hide();
  861. end
  862. end
  863. GameTooltip:Hide();
  864. end
  865. function AzeriteMilestoneBaseMixin:ShowStateFrame(stateFrame)
  866. if not self.StateFrames then
  867. return;
  868. end
  869. for i, frame in ipairs(self.StateFrames) do
  870. frame:SetShown(frame == stateFrame);
  871. end
  872. end
  873. function AzeriteMilestoneBaseMixin:CheckAndSetUpUnlockEffect()
  874. local scene = self.EffectsModelScene;
  875. if not scene.primaryEffect then
  876. local forceUpdate = true;
  877. local stopAnim = true;
  878. local sceneInfo = self.slot and UNLOCK_SLOT_MODEL_SCENE_INFO or UNLOCK_STAMINA_MODEL_SCENE_INFO;
  879. scene.primaryEffect, scene.secondaryEffect = StaticModelInfo.SetupModelScene(scene, sceneInfo, forceUpdate, stopAnim);
  880. end
  881. end
  882. function AzeriteMilestoneBaseMixin:OnUnlocked()
  883. self:CheckAndSetUpUnlockEffect();
  884. local scene = self.EffectsModelScene;
  885. if scene.primaryEffect then
  886. scene:ShowAndAnimateActors(UNLOCK_MODEL_SCENE_ACTOR_SETTINGS, function() scene:Hide(); end);
  887. C_Timer.After(.4,
  888. function()
  889. self.SwirlContainer:Show();
  890. self.SwirlContainer.SelectedAnim:Play();
  891. end
  892. );
  893. if GameTooltip:GetOwner() == self then
  894. self:OnEnter();
  895. end
  896. end
  897. if self.slot then
  898. PlaySound(SOUNDKIT.UI_82_HEARTOFAZEROTH_UNLOCKESSENCESLOT);
  899. else
  900. PlaySound(SOUNDKIT.UI_82_HEARTOFAZEROTH_UNLOCKSTAMINANODE);
  901. end
  902. end
  903. function AzeriteMilestoneBaseMixin:CheckAndSetUpRevealEffect()
  904. local scene = self.EffectsModelScene;
  905. if not scene.primaryEffect then
  906. scene:SetSize(2000, 2000);
  907. local forceUpdate = true;
  908. local stopAnim = true;
  909. scene.primaryEffect, scene.secondaryEffect = StaticModelInfo.SetupModelScene(scene, REVEAL_SLOT_MODEL_SCENE_INFO, forceUpdate, stopAnim);
  910. end
  911. end
  912. function AzeriteMilestoneBaseMixin:PlayRevealEffect()
  913. self:CheckAndSetUpRevealEffect();
  914. local scene = self.EffectsModelScene;
  915. if scene.primaryEffect then
  916. scene:ShowAndAnimateActors(REVEAL_MODEL_SCENE_ACTOR_SETTINGS, function() scene:Hide(); end);
  917. end
  918. end
  919. function AzeriteMilestoneBaseMixin:BeginReveal(delay)
  920. self:Show();
  921. self:Refresh();
  922. self.RevealAnim.Start:SetEndDelay(delay);
  923. self.RevealAnim:Play();
  924. end
  925. function AzeriteMilestoneBaseMixin:CancelReveal(delay)
  926. self.RevealAnim:Stop();
  927. self:SetAlpha(1);
  928. end
  929. function AzeriteMilestoneBaseMixin:ShouldShowUnlockState()
  930. if C_AzeriteEssence.IsAtForge() then
  931. return self.canUnlock;
  932. else
  933. return self:GetParent():MeetsPowerLevel(self.requiredLevel)
  934. end
  935. end
  936. function AzeriteMilestoneBaseMixin:UpdateMilestoneInfo()
  937. local milestoneInfo = C_AzeriteEssence.GetMilestoneInfo(self.milestoneID);
  938. self.unlocked = milestoneInfo.unlocked;
  939. self.canUnlock = milestoneInfo.canUnlock;
  940. self.requiredLevel = milestoneInfo.requiredLevel;
  941. self.rank = milestoneInfo.rank;
  942. end
  943. function AzeriteMilestoneBaseMixin:AddStateToTooltip(requiredLevelString, returnToForgeString)
  944. local wrapText = true;
  945. if C_AzeriteEssence.IsAtForge() then
  946. if self.canUnlock then
  947. GameTooltip_AddColoredLine(GameTooltip, AZERITE_CLICK_TO_SELECT, GREEN_FONT_COLOR, wrapText);
  948. elseif self:GetParent():MeetsPowerLevel(self.requiredLevel) then
  949. GameTooltip_AddColoredLine(GameTooltip, AZERITE_MILESTONE_NO_ACTIVE_LINKS, RED_FONT_COLOR, wrapText);
  950. else
  951. GameTooltip_AddColoredLine(GameTooltip, string.format(requiredLevelString, self.requiredLevel), DISABLED_FONT_COLOR, wrapText);
  952. end
  953. else
  954. if self:ShouldShowUnlockState() then
  955. GameTooltip_AddColoredLine(GameTooltip, returnToForgeString, RED_FONT_COLOR, wrapText);
  956. else
  957. GameTooltip_AddColoredLine(GameTooltip, string.format(requiredLevelString, self.requiredLevel), DISABLED_FONT_COLOR, wrapText);
  958. end
  959. end
  960. end
  961. function AzeriteMilestoneBaseMixin:IsMajorSlot()
  962. return self.isMajorSlot;
  963. end
  964. AzeriteMilestoneSlotMixin = CreateFromMixins(AzeriteMilestoneBaseMixin);
  965. function AzeriteMilestoneSlotMixin:OnLoad()
  966. self.UnlockedState.EmptyGlow.Anim:Play();
  967. AzeriteMilestoneBaseMixin.OnLoad(self);
  968. end
  969. function AzeriteMilestoneSlotMixin:OnDragStart()
  970. local spellID = C_AzeriteEssence.GetMilestoneSpell(self.milestoneID);
  971. if spellID then
  972. PickupSpell(spellID);
  973. end
  974. end
  975. function AzeriteMilestoneSlotMixin:UpdateModelScenes(forceUpdate)
  976. if not self.unlocked then
  977. return;
  978. end
  979. local isAzeriteItemEnabled = self:GetParent():IsAzeriteItemEnabled();
  980. if forceUpdate then
  981. self.UnlockedState.PurpleGemModelScene.forceUpdate = true;
  982. if self:IsMajorSlot() then
  983. self.UnlockedState.BlueGemModelScene.forceUpdate = true;
  984. end
  985. end
  986. if self:GetParent():GetEffectiveEssence(self.milestoneID) then
  987. local purpleGemModelSceneInfo = MINOR_PURPLE_GEM_MODEL_SCENE_INFO;
  988. if self:IsMajorSlot() then
  989. purpleGemModelSceneInfo = MAJOR_PURPLE_GEM_MODEL_SCENE_INFO;
  990. local scene = self.UnlockedState.BlueGemModelScene;
  991. scene:Show();
  992. scene.forceUpdate = not StaticModelInfo.SetupModelScene(scene, MAJOR_BLUE_GEM_MODEL_SCENE_INFO, scene.forceUpdate);
  993. scene:SetDesaturation(isAzeriteItemEnabled and 0 or 1);
  994. scene:SetPaused(not isAzeriteItemEnabled);
  995. end
  996. local scene = self.UnlockedState.PurpleGemModelScene;
  997. scene:Show();
  998. scene.forceUpdate = not StaticModelInfo.SetupModelScene(scene, purpleGemModelSceneInfo, scene.forceUpdate);
  999. scene:SetDesaturation(isAzeriteItemEnabled and 0 or 1);
  1000. scene:SetPaused(not isAzeriteItemEnabled);
  1001. else
  1002. if self:IsMajorSlot() then
  1003. self.UnlockedState.BlueGemModelScene:Hide();
  1004. end
  1005. self.UnlockedState.PurpleGemModelScene:Hide();
  1006. end
  1007. end
  1008. function AzeriteMilestoneSlotMixin:Refresh()
  1009. self:UpdateMilestoneInfo();
  1010. local isAzeriteItemEnabled = self:GetParent():IsAzeriteItemEnabled();
  1011. local desaturation = (not isAzeriteItemEnabled) and 1 or 0
  1012. if self.unlocked then
  1013. if self:IsMajorSlot() and self:GetParent():ShouldPlayReveal() then
  1014. self:CheckAndSetUpRevealEffect();
  1015. end
  1016. self:ShowStateFrame(self.UnlockedState);
  1017. local essenceID = self:GetParent():GetEffectiveEssence(self.milestoneID);
  1018. local icon;
  1019. if essenceID then
  1020. local essenceInfo = C_AzeriteEssence.GetEssenceInfo(essenceID);
  1021. icon = essenceInfo and essenceInfo.icon or nil;
  1022. end
  1023. local stateFrame = self.UnlockedState;
  1024. if icon then
  1025. stateFrame.Icon:SetTexture(icon);
  1026. stateFrame.Icon:Show();
  1027. stateFrame.EmptyIcon:Hide();
  1028. stateFrame.EmptyGlow:Hide();
  1029. else
  1030. stateFrame.Icon:Hide();
  1031. stateFrame.EmptyIcon:Show();
  1032. stateFrame.EmptyGlow:Show();
  1033. end
  1034. stateFrame:DesaturateHierarchy(desaturation);
  1035. else
  1036. if not self:IsMajorSlot() then
  1037. self:CheckAndSetUpUnlockEffect();
  1038. end
  1039. if self:ShouldShowUnlockState() then
  1040. self:ShowStateFrame(self.AvailableState);
  1041. self.AvailableState:DesaturateHierarchy(desaturation);
  1042. if C_AzeriteEssence.IsAtForge() then
  1043. self.AvailableState.GlowAnim:Stop();
  1044. if isAzeriteItemEnabled then
  1045. self.AvailableState.ForgeGlowAnim:Play();
  1046. end
  1047. else
  1048. self.AvailableState.ForgeGlowAnim:Stop();
  1049. if isAzeriteItemEnabled then
  1050. self.AvailableState.GlowAnim:Play();
  1051. end
  1052. end
  1053. else
  1054. self:ShowStateFrame(self.LockedState);
  1055. self.LockedState:DesaturateHierarchy(desaturation);
  1056. self.LockedState.UnlockLevelText:SetText(self.requiredLevel);
  1057. self.LockedState.UnlockLevelText:SetTextColor((isAzeriteItemEnabled and UNLOCK_LEVEL_TEXT_COLOR or LOCKED_FONT_COLOR):GetRGB())
  1058. end
  1059. end
  1060. self:UpdateModelScenes();
  1061. end
  1062. function AzeriteMilestoneSlotMixin:OnMouseUp(button)
  1063. if button == "LeftButton" then
  1064. if IsModifiedClick("CHATLINK") then
  1065. local essenceID = C_AzeriteEssence.GetMilestoneEssence(self.milestoneID);
  1066. if essenceID then
  1067. local essenceInfo = C_AzeriteEssence.GetEssenceInfo(essenceID);
  1068. if essenceInfo then
  1069. if HandleModifiedItemClick(C_AzeriteEssence.GetEssenceHyperlink(essenceInfo.ID, essenceInfo.rank)) then
  1070. return;
  1071. end
  1072. end
  1073. end
  1074. end
  1075. if C_AzeriteEssence.HasPendingActivationEssence() then
  1076. if self.unlocked then
  1077. if self:GetParent():HasNewlyActivatedEssence() then
  1078. UIErrorsFrame:AddMessage(ERR_CANT_DO_THAT_RIGHT_NOW, RED_FONT_COLOR:GetRGBA());
  1079. else
  1080. -- check for animation only, let it go either way for error messages
  1081. local pendingEssenceID = C_AzeriteEssence.GetPendingActivationEssence();
  1082. if C_AzeriteEssence.CanActivateEssence(pendingEssenceID, self.milestoneID) then
  1083. self:GetParent():OnEssenceActivated(pendingEssenceID, self);
  1084. if GameTooltip:GetOwner() == self then
  1085. GameTooltip:Hide();
  1086. end
  1087. end
  1088. C_AzeriteEssence.ActivateEssence(pendingEssenceID, self.milestoneID);
  1089. end
  1090. end
  1091. elseif self.canUnlock then
  1092. C_AzeriteEssence.UnlockMilestone(self.milestoneID);
  1093. end
  1094. end
  1095. end
  1096. function AzeriteMilestoneSlotMixin:OnEnter()
  1097. if self:IsMajorSlot() then
  1098. GameTooltip:SetOwner(self, "ANCHOR_RIGHT", -20, 0);
  1099. else
  1100. GameTooltip:SetOwner(self, "ANCHOR_RIGHT", -10, -5);
  1101. end
  1102. local essenceID = C_AzeriteEssence.GetMilestoneEssence(self.milestoneID);
  1103. if essenceID then
  1104. GameTooltip:SetAzeriteEssenceSlot(self.slot);
  1105. SharedTooltip_SetBackdropStyle(GameTooltip, GAME_TOOLTIP_BACKDROP_STYLE_AZERITE_ITEM);
  1106. if C_AzeriteEssence.HasPendingActivationEssence() then
  1107. local pendingEssenceID = C_AzeriteEssence.GetPendingActivationEssence();
  1108. if C_AzeriteEssence.CanActivateEssence(pendingEssenceID, self.milestoneID) then
  1109. self.UnlockedState.HighlightRing:Show();
  1110. end
  1111. elseif self.isDraggable then
  1112. local spellID = C_AzeriteEssence.GetMilestoneSpell(self.milestoneID);
  1113. if spellID then
  1114. self.UnlockedState.DragHighlight:Show();
  1115. end
  1116. end
  1117. else
  1118. local wrapText = true;
  1119. if not self.unlocked then
  1120. GameTooltip_SetTitle(GameTooltip, AZERITE_ESSENCE_PASSIVE_SLOT);
  1121. self:AddStateToTooltip(AZERITE_ESSENCE_LOCKED_SLOT_LEVEL, AZERITE_ESSENCE_UNLOCK_SLOT);
  1122. else
  1123. if self:IsMajorSlot() then
  1124. GameTooltip_SetTitle(GameTooltip, AZERITE_ESSENCE_EMPTY_MAIN_SLOT);
  1125. GameTooltip_AddColoredLine(GameTooltip, AZERITE_ESSENCE_EMPTY_MAIN_SLOT_DESC, NORMAL_FONT_COLOR, wrapText);
  1126. else
  1127. GameTooltip_SetTitle(GameTooltip, AZERITE_ESSENCE_EMPTY_PASSIVE_SLOT);
  1128. GameTooltip_AddColoredLine(GameTooltip, AZERITE_ESSENCE_EMPTY_PASSIVE_SLOT_DESC, NORMAL_FONT_COLOR, wrapText);
  1129. end
  1130. end
  1131. end
  1132. GameTooltip:Show();
  1133. end
  1134. AzeriteMilestoneStaminaMixin = CreateFromMixins(AzeriteMilestoneBaseMixin);
  1135. function AzeriteMilestoneStaminaMixin:Refresh()
  1136. self:UpdateMilestoneInfo();
  1137. local isAzeriteItemEnabled = self:GetParent():IsAzeriteItemEnabled();
  1138. if self.unlocked then
  1139. self.Icon:SetAtlas("heartofazeroth-node-on");
  1140. else
  1141. self.Icon:SetAtlas("heartofazeroth-node-off");
  1142. self:CheckAndSetUpUnlockEffect();
  1143. end
  1144. self.Icon:SetDesaturated(not isAzeriteItemEnabled);
  1145. if not self.unlocked and self:ShouldShowUnlockState() then
  1146. if C_AzeriteEssence.IsAtForge() then
  1147. self.GlowAnim:Stop();
  1148. if isAzeriteItemEnabled then
  1149. self.ForgeGlowAnim:Play();
  1150. end
  1151. else
  1152. self.ForgeGlowAnim:Stop();
  1153. if isAzeriteItemEnabled then
  1154. self.GlowAnim:Play();
  1155. end
  1156. end
  1157. else
  1158. self.GlowAnim:Stop();
  1159. self.ForgeGlowAnim:Stop();
  1160. end
  1161. end
  1162. AzeriteMilestoneRankedMixin = CreateFromMixins(AzeriteMilestoneBaseMixin);
  1163. function AzeriteMilestoneRankedMixin:Refresh()
  1164. self:UpdateMilestoneInfo();
  1165. if not self.unlocked and self.canUnlock then
  1166. self.needsUnlock = true;
  1167. end
  1168. if not self.unlocked and not self.canUnlock then
  1169. self:ShowStateFrame(self.LockedState);
  1170. self.LockedState.UnlockLevelText:SetText(self.requiredLevel);
  1171. else
  1172. self:ShowStateFrame(self.AvailableState);
  1173. self.AvailableState.RankText:SetText(self.rank);
  1174. local spellName, spellTexture = AzeriteEssenceUtil.GetMilestoneSpellInfo(self.milestoneID);
  1175. self.AvailableState.Icon:SetTexture(spellTexture);
  1176. if self:ShouldShowUnlockState() then
  1177. self:CheckAndSetUpUnlockEffect();
  1178. if C_AzeriteEssence.IsAtForge() then
  1179. self.AvailableState.GlowAnim:Stop();
  1180. self.AvailableState.ForgeGlowAnim:Play();
  1181. else
  1182. self.AvailableState.ForgeGlowAnim:Stop();
  1183. self.AvailableState.GlowAnim:Play();
  1184. end
  1185. else
  1186. self.AvailableState.GlowAnim:Stop();
  1187. self.AvailableState.ForgeGlowAnim:Stop();
  1188. end
  1189. end
  1190. self:UpdateModelScenes();
  1191. end
  1192. function AzeriteMilestoneRankedMixin:UpdateModelScenes(forceUpdate)
  1193. if not self.needsUnlock and self.unlocked then
  1194. local scene = self.EffectsModelScene;
  1195. if not scene.activeEffect or forceUpdate then
  1196. scene:SetFrameLevel(self.AvailableState:GetFrameLevel() - 1);
  1197. scene.activeEffect = StaticModelInfo.SetupModelScene(scene, RANKED_MILESTONE_MODEL_SCENE_INFO, forceUpdate);
  1198. end
  1199. end
  1200. end
  1201. function AzeriteMilestoneRankedMixin:CheckAndSetUpUnlockEffect()
  1202. local scene = self.EffectsModelScene;
  1203. if not scene.unlockEffect then
  1204. local forceUpdate = true;
  1205. local stopAnim = true;
  1206. self.EffectsModelScene:SetFrameLevel(self.AvailableState:GetFrameLevel() + 1);
  1207. scene.unlockEffect = StaticModelInfo.SetupModelScene(scene, REVEAL_SLOT_MODEL_SCENE_INFO, forceUpdate, stopAnim);
  1208. end
  1209. end
  1210. function AzeriteMilestoneRankedMixin:OnUnlocked()
  1211. self:CheckAndSetUpUnlockEffect();
  1212. local scene = self.EffectsModelScene;
  1213. if scene.unlockEffect then
  1214. local onUnlockDoneFunc = function()
  1215. self.needsUnlock = false;
  1216. self:Refresh();
  1217. end;
  1218. scene:ShowAndAnimateActors(REVEAL_MODEL_SCENE_ACTOR_SETTINGS, onUnlockDoneFunc);
  1219. ScriptAnimationUtil.ShakeFrame(self:GetParent():GetParent(), REVEAL_SHAKE, REVEAL_SHAKE_DURATION, REVEAL_SHAKE_FREQUENCY);
  1220. end
  1221. PlaySound(SOUNDKIT.UI_82_HEARTOFAZEROTH_UNLOCKSTAMINANODE);
  1222. end
  1223. AzeriteEssenceLearnAnimFrameMixin = { };
  1224. function AzeriteEssenceLearnAnimFrameMixin:OnLoad()
  1225. self:SetPoint("CENTER", AzeriteEssenceUI:GetSlotFrame(Enum.AzeriteEssence.MainSlot));
  1226. end
  1227. function AzeriteEssenceLearnAnimFrameMixin:PlayAnim()
  1228. if not AzeriteEssenceUI:IsShown() then
  1229. return;
  1230. end
  1231. self.Anim:Stop();
  1232. local runeIndex = random(1, 16);
  1233. local runeAtlas = "heartofazeroth-animation-rune"..runeIndex;
  1234. local useAtlasSize = true;
  1235. for i, texture in ipairs(self.Textures) do
  1236. texture:SetAlpha(0);
  1237. if texture.isRune then
  1238. texture:SetAtlas(runeAtlas, useAtlasSize);
  1239. end
  1240. end
  1241. self:Show();
  1242. self.Anim:Play();
  1243. C_Timer.After(LEARN_SHAKE_DELAY,
  1244. function()
  1245. ScriptAnimationUtil.ShakeFrame(self:GetParent(), LEARN_SHAKE, LEARN_SHAKE_DURATION, LEARN_SHAKE_FREQUENCY);
  1246. end
  1247. );
  1248. end
  1249. function AzeriteEssenceLearnAnimFrameMixin:StopAnim()
  1250. self:Hide();
  1251. end