Blizzard_AzeriteEssenceUI/Blizzard_AzeriteEssenceUI.lua

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