diff --git a/.luacheckrc b/.luacheckrc
index fa46ab9..5bd56ca 100644
--- a/.luacheckrc
+++ b/.luacheckrc
@@ -318,6 +318,7 @@ stds.wow = {
"HandleModifiedItemClick",
"HasOverrideActionBar",
"HaveQuestData",
+ "HelpTip",
"HideUIPanel",
"hooksecurefunc",
"HouseEditorFrame",
@@ -1165,6 +1166,7 @@ stds.wow = {
"GetSuperTrackedMapPin",
"GetSuperTrackedQuestID",
"IsSuperTrackingAnything",
+ "SetSuperTrackedMapPin",
"SetSuperTrackedQuestID",
"SetSuperTrackedUserWaypoint",
},
@@ -1221,6 +1223,7 @@ stds.wow = {
"CanPurchaseRank",
"CommitConfig",
"ConfigHasStagedChanges",
+ "GetConfigIDBySystemID",
"GetConfigIDByTreeID",
"GetConfigInfo",
"GetDefinitionInfo",
@@ -1228,9 +1231,11 @@ stds.wow = {
"GetIncreasedTraitData",
"GetNodeCost",
"GetNodeInfo",
+ "GetTraitCurrencyInfo",
"GetTreeCurrencyInfo",
"GetTreeNodes",
"PurchaseRank",
+ "RefundRank",
"ResetTree",
"RollbackConfig",
"SetSelection",
@@ -1472,6 +1477,12 @@ stds.wow = {
},
},
+ SuperTrackingMapPinType = {
+ fields = {
+ "QuestOffer",
+ },
+ },
+
TooltipDataLineType = {
fields = {
"AzeriteEssencePower",
@@ -1775,6 +1786,7 @@ stds.wow = {
"FILTER",
"GAME_VERSION_LABEL",
"GARRISON_LANDING_PAGE_TITLE",
+ "GARRISON_MISSION_IN_PROGRESS_TOOLTIP",
"GARRISON_TYPE_8_0_LANDING_PAGE_TITLE",
"GARRISON_TYPE_9_0_LANDING_PAGE_TITLE",
"GOLD_AMOUNT_SYMBOL",
@@ -1822,6 +1834,7 @@ stds.wow = {
"NO_TRANSMOG_VISUAL_ID",
"OBJECTIVES_STOP_TRACKING",
"OBJECTIVES_VIEW_ACHIEVEMENT",
+ "OMNIUM_FOLIO_UNSPENT_POINTS",
"ORBIT_CAMERA_MOUSE_MODE_PITCH_ROTATION",
"ORDER_HALL_LANDING_PAGE_TITLE",
"PARAGON_REPUTATION_TOOLTIP_TEXT",
@@ -1844,6 +1857,7 @@ stds.wow = {
"RENOWN_REWARD_ACCOUNT_UNLOCK_LABEL",
"REPUTATION_TOOLTIP_ACCOUNT_WIDE_LABEL",
"RETRIEVING_DATA",
+ "RUNES_OF_POWER",
"SAVE",
"SEARCH",
"SECONDS_ABBR",
@@ -1857,7 +1871,9 @@ stds.wow = {
"SUMMON_RANDOM_PET",
"SWITCH",
"TALENT_BUTTON_TOOLTIP_NEXT_RANK",
+ "TALENT_BUTTON_TOOLTIP_PURCHASE_INSTRUCTIONS",
"TALENT_BUTTON_TOOLTIP_RANK_FORMAT",
+ "TALENT_BUTTON_TOOLTIP_REFUND_INSTRUCTIONS",
"TALENT_SPEC_ACTIVATE",
"TEMPSCENE",
"TIMEMANAGER_TOOLTIP_LOCALTIME",
diff --git a/API.lua b/API.lua
index 0e0a4a8..2037041 100644
--- a/API.lua
+++ b/API.lua
@@ -2918,6 +2918,9 @@ do -- Quest
end
end
end
+
+ function API.SuperTrackQuestMapPin(questID)
+ end
else
--Retail
function API.GetQuestProgressPercent(questID, asText)
@@ -3074,6 +3077,14 @@ do -- Quest
return tbl
end
+
+ function API.SuperTrackQuestMapPin(questID)
+ if C_QuestLog.IsOnQuest(questID) then
+ C_SuperTrack.SetSuperTrackedQuestID(questID);
+ else
+ C_SuperTrack.SetSuperTrackedMapPin(Enum.SuperTrackingMapPinType.QuestOffer, questID);
+ end
+ end
end
@@ -3169,6 +3180,7 @@ do -- Quest
for index, spellID in ipairs(spellRewards) do
info = C_QuestInfoSystem.GetQuestRewardSpellInfo(questID, spellID);
info.id = spellID;
+ info.isSpellReward = true;
tinsert(spells, info);
end
diff --git a/Art/ExpansionLandingPage/LandingButton.png b/Art/ExpansionLandingPage/LandingButton.png
index 9a36392..bcef05c 100644
Binary files a/Art/ExpansionLandingPage/LandingButton.png and b/Art/ExpansionLandingPage/LandingButton.png differ
diff --git a/Art/Frame/TraitSystem.png b/Art/Frame/TraitSystem.png
new file mode 100644
index 0000000..ee4aea3
Binary files /dev/null and b/Art/Frame/TraitSystem.png differ
diff --git a/Initialization.lua b/Initialization.lua
index 8b5fa9f..5da4363 100644
--- a/Initialization.lua
+++ b/Initialization.lua
@@ -1,5 +1,5 @@
-local VERSION_TEXT = "1.9.2 c";
-local VERSION_DATE = 1779500000;
+local VERSION_TEXT = "1.9.2 d";
+local VERSION_DATE = 1780400000;
local addonName, addon = ...
@@ -298,7 +298,7 @@ local DefaultValues = {
LootUI_UseCustomColor = false,
LootUI_GrowUpwards = false,
LootUI_WindowHide = false,
- LootUI_CombineItems = false,
+ LootUI_CombineItem = true,
LootUI_LowFrameStrata = false,
LootUI_HideTitle = false,
LootUI_ShowReputation = false,
diff --git a/Locales/enUS.lua b/Locales/enUS.lua
index 6700b62..f90800e 100755
--- a/Locales/enUS.lua
+++ b/Locales/enUS.lua
@@ -738,6 +738,7 @@ L["Delves Completion Reward Cap"] = "Completion Rewards";
L["Delves Completion Reward Cap Tooltip"] = "Once you reach this account-wide cap, completing a Bountiful Delve will no longer grant Delver's Journey or Companion EXP.\n\nRewards from Bountiful Coffers and Nemesis Caches will not be affected by this cap.\n\nThe cap will be raised by 28 per week.";
L["Near Completion Tooltip"] = "This entry is visible to you because you are about to reach the weekly cap.";
L["Inactive Currencies Tooltip"] = "These currencies are hidden because you have set them as Unused:";
+L["New Quest"] = "New Quest";
--ExpansionSummaryMinimapButton
diff --git a/Locales/zhCN.lua b/Locales/zhCN.lua
index b90d8c3..dddacc2 100755
--- a/Locales/zhCN.lua
+++ b/Locales/zhCN.lua
@@ -707,6 +707,7 @@ L["Delves Completion Reward Cap"] = "通关奖励";
L["Delves Completion Reward Cap Tooltip"] = "当你达到此战团上限后,完成丰裕地下堡将不再奖励旅程进度和伙伴经验。\n\n丰裕宝匣和宿敌宝箱不受此上限影响。\n\n每周上限为28次,可以累计。";
L["Near Completion Tooltip"] = "你可以看见此条目是因为你即将达到上限。";
L["Inactive Currencies Tooltip"] = "以下货币因为被你设为“未使用”而隐藏:";
+L["New Quest"] = "新任务";
--ExpansionSummaryMinimapButton
diff --git a/Modules/ControlCenter/Changelog/enUS.lua b/Modules/ControlCenter/Changelog/enUS.lua
index 37c7030..0bdb9b1 100644
--- a/Modules/ControlCenter/Changelog/enUS.lua
+++ b/Modules/ControlCenter/Changelog/enUS.lua
@@ -9,6 +9,46 @@ local changelogs = addon.ControlCenter.changelogs;
changelogs[10902] = {
+ {
+ type = "date",
+ versionText = "1.9.2 d",
+ timestamp = 1780400000,
+ },
+
+ {
+ type = "p",
+ bullet = true,
+ text = "Loot Window: Junk items are now combined and displayed as a single entry.",
+ },
+ {
+ type = "p",
+ bullet = 2,
+ text = "Note: This option is not new, but we changed its default state to Enabled.",
+ },
+
+ {
+ type = "p",
+ bullet = true,
+ text = "Expansion Summary, Great Vault: Fixed several issues.",
+ },
+ {
+ type = "p",
+ bullet = 2,
+ text = "Hovering the cursor over a Raid progress will no longer cause error.",
+ },
+ {
+ type = "p",
+ bullet = 2,
+ text = "World Activities should correctly show the increased item level after completing Tier 4 or Tier 5 Ritual Sites.",
+ },
+
+ {
+ type = "br",
+ },
+ {
+ type = "br",
+ },
+
{
type = "date",
versionText = "1.9.2 c",
diff --git a/Modules/ExpansionLandingPage/Basic.lua b/Modules/ExpansionLandingPage/Basic.lua
index 4595a91..2d9ef81 100644
--- a/Modules/ExpansionLandingPage/Basic.lua
+++ b/Modules/ExpansionLandingPage/Basic.lua
@@ -1846,6 +1846,17 @@ do --PlumberStrikethroughNumberMixin
end
+do --ExpansionFeature
+ function LandingPageUtil.HandleTraitTreeCurrencyChanged(treeID)
+ --Override
+ end
+
+ function LandingPageUtil.HasAnyPurchasableTrait()
+ return false;
+ end
+end
+
+
function LandingPageUtil.DisplayInactiveCurrencies(owner, currencyIDs)
if owner and currencyIDs then
local tooltip = GameTooltip;
diff --git a/Modules/ExpansionLandingPage/Currency.lua b/Modules/ExpansionLandingPage/Currency.lua
index aa45791..010d0e8 100644
--- a/Modules/ExpansionLandingPage/Currency.lua
+++ b/Modules/ExpansionLandingPage/Currency.lua
@@ -260,10 +260,9 @@ end
local CurrencyListMixin = {};
do
- function CurrencyListMixin:Refresh()
+ function CurrencyListMixin:OnLoad()
--Called once when frame is created
self:OnSizeChanged();
- self:FullUpdate();
end
function CurrencyListMixin:OnShow()
diff --git a/Modules/ExpansionLandingPage/ExpansionLandingPage.lua b/Modules/ExpansionLandingPage/ExpansionLandingPage.lua
index 69f2f8f..387cd02 100644
--- a/Modules/ExpansionLandingPage/ExpansionLandingPage.lua
+++ b/Modules/ExpansionLandingPage/ExpansionLandingPage.lua
@@ -277,11 +277,16 @@ do
if not self:IsUserPlaced() then
self:ResetPosition();
end
+
+ if LandingPageUtil.HasAnyPurchasableTrait() then
+ self:ShowTraitTab();
+ end
end
function PlumberExpansionLandingPageMixin:OnHide()
LandingPageUtil.PlayUISound("LandingPageClose");
LandingPageUtil.MainContextMenu:HideMenu();
+ LandingPageUtil.HandleTraitTreeCurrencyChanged(1186);
end
function PlumberExpansionLandingPageMixin:InitTabButtons()
@@ -381,10 +386,15 @@ do
local categories = {
{name = L["Great Vault"], frameGetter = LandingPageUtil.CreateGreatVaultFrame, validate = API.IsGreatVaultFeatureAvailable},
- {name = L["Item Upgrade"], frameGetter = LandingPageUtil.CreateItemUpgradeFrame},
{name = L["Resources"], frameGetter = LandingPageUtil.CreateCurrencyList},
};
+ if addon.IS_12_0_7 then
+ table.insert(categories, 2, {name = LandingPageUtil.GetTraitSystemName(), frameGetter = LandingPageUtil.CreateTraitFrame});
+ else
+ table.insert(categories, 2, {name = L["Item Upgrade"], frameGetter = LandingPageUtil.CreateItemUpgradeFrame});
+ end
+
local numCategories = #categories;
local offsetY = 16;
@@ -402,12 +412,15 @@ do
if v.frameGetter then
offsetY = offsetY + lineGap;
local frame, height, categoryOnEnterFunc = v.frameGetter(container);
+ frame.listCategoryButton = categoryButton;
frame:SetPoint("TOP", relativeTo, "TOP", 0, -offsetY);
offsetY = offsetY + height;
if k == numCategories then
frame:SetPoint("BOTTOM", relativeTo, "BOTTOM", 0, 16);
end
- frame:Refresh();
+ if frame.OnLoad then
+ frame:OnLoad();
+ end
if frame.OnShow and frame:IsVisible() then
frame:OnShow();
end
@@ -421,9 +434,6 @@ do
function PlumberExpansionLandingPageMixin:ShowLeftFrame(state)
self.LeftSection.DefaultFrame:SetShown(state);
end
- function LandingPageUtil.ShowLeftFrame(state)
- MainFrame:ShowLeftFrame(state);
- end
function PlumberExpansionLandingPageMixin:DimBackground(state)
if IS_MOP then
@@ -437,20 +447,11 @@ do
--local a = state and 0.5 or 1.0;
self.RightSection.NineSlice.Background:SetVertexColor(a, a, a);
end
- function LandingPageUtil.DimBackground(state)
- MainFrame:DimBackground(state);
- end
function PlumberExpansionLandingPageMixin:ToggleUI()
self:SetShown(not self:IsShown());
end
- function LandingPageUtil.ToggleUI()
- if MainFrame then
- MainFrame:ToggleUI();
- end
- end
-
function PlumberExpansionLandingPageMixin:ResetPosition()
self:ClearAllPoints();
if IS_MOP then
@@ -517,3 +518,39 @@ do
self:EnableMouse(false);
end
end
+
+
+do --12.0.7 Only
+ function PlumberExpansionLandingPageMixin:ShowTraitTab()
+ if not self:IsShown() then
+ self:Show();
+ end
+ local selectedTab = LandingPageUtil.GetSelectedTabKey();
+ if selectedTab ~= "faction" and selectedTab ~= "activity" then
+ LandingPageUtil.SelectTab("faction");
+ MainFrame:UpdateTabs();
+ end
+ LandingPageUtil.SelectExpansion(12);
+ end
+end
+
+
+do --Shared
+ function LandingPageUtil.ShowLeftFrame(state)
+ MainFrame:ShowLeftFrame(state);
+ end
+
+ function LandingPageUtil.DimBackground(state)
+ MainFrame:DimBackground(state);
+ end
+
+ function LandingPageUtil.ToggleUI()
+ if MainFrame then
+ MainFrame:ToggleUI();
+ end
+ end
+
+ function LandingPageUtil.GetUIFrameLevel()
+ return MainFrame.LeftSection.DefaultFrame:GetFrameLevel();
+ end
+end
diff --git a/Modules/ExpansionLandingPage/ExpansionLandingPage_Retail.xml b/Modules/ExpansionLandingPage/ExpansionLandingPage_Retail.xml
index 0a02412..e9a78e5 100644
--- a/Modules/ExpansionLandingPage/ExpansionLandingPage_Retail.xml
+++ b/Modules/ExpansionLandingPage/ExpansionLandingPage_Retail.xml
@@ -19,6 +19,7 @@
+
diff --git a/Modules/ExpansionLandingPage/FactionTab.lua b/Modules/ExpansionLandingPage/FactionTab.lua
index ffce6b2..1809cc9 100644
--- a/Modules/ExpansionLandingPage/FactionTab.lua
+++ b/Modules/ExpansionLandingPage/FactionTab.lua
@@ -1058,12 +1058,18 @@ do
local buttonHeight = 32;
local numLevels = #renownLevelsInfo;
local top, bottom;
+ local lastUnlockedIndex;
local firstLockedIndex;
if renownLevelsInfo then
for k, v in ipairs(renownLevelsInfo) do
rewards = C_MajorFactions.GetRenownRewardsForLevel(factionID, v.level)
table.sort(rewards, SortFunc_UIOrder);
+
+ if (not v.locked) and (not firstLockedIndex) then
+ lastUnlockedIndex = n;
+ end
+
for index, rewardInfo in ipairs(rewards) do
n = n + 1;
top = offsetY;
@@ -1125,8 +1131,7 @@ do
self.RenownItemScrollView:SetContent(content, retainPosition);
if scrollToFirstLockedReward then
- firstLockedIndex = firstLockedIndex or n;
- self.RenownItemScrollView:SnapToContent(firstLockedIndex);
+ self.RenownItemScrollView:SnapToContent(lastUnlockedIndex or n);
end
end
diff --git a/Modules/ExpansionLandingPage/FactionUtil.lua b/Modules/ExpansionLandingPage/FactionUtil.lua
index 02cae77..3591c13 100644
--- a/Modules/ExpansionLandingPage/FactionUtil.lua
+++ b/Modules/ExpansionLandingPage/FactionUtil.lua
@@ -16,42 +16,38 @@ local OverrideFactionInfo = {
---- MID ----
[2710] = { --Silvermoon Court
barColor = {206/255, 164/255, 56/255},
- rewardQuestID = 0,
+ rewardQuestID = 93811,
},
[2711] = { --Magisters
barColor = {155/255, 173/255, 204/255},
- rewardQuestID = 0,
},
[2712] = { --Blood Knights
barColor = {206/255, 159/255, 159/255},
- rewardQuestID = 0,
},
[2713] = { --Farstriders
barColor = {145/255, 181/255, 128/255},
- rewardQuestID = 0,
},
[2714] = { --Shades of the Row
barColor = {206/255, 164/255, 56/255},
- rewardQuestID = 0,
},
[2696] = { --Amani
barColor = {206/255, 162/255, 123/255},
- rewardQuestID = 0,
+ rewardQuestID = 93566,
},
[2704] = { --Hara'ti
barColor = {254/255, 132/255, 97/255},
- rewardQuestID = 0,
+ rewardQuestID = 89035,
},
[2699] = { --Singularity
barColor = {159/255, 169/255, 222/255},
- rewardQuestID = 0,
+ rewardQuestID = 89032,
},
[2764] = { --Prey S1
@@ -68,10 +64,12 @@ local OverrideFactionInfo = {
[2770] = { --Slayer's Duellum
barColor = {56/255, 184/255, 255/255},
+ rewardQuestID = 94492,
},
[2792] = { --Ritual Sites
barColor = {197/255, 142/255, 255/255},
+ rewardQuestID = 95391,
},
---- TWW ----
diff --git a/Modules/ExpansionLandingPage/GreatVault.lua b/Modules/ExpansionLandingPage/GreatVault.lua
index a991b64..960c572 100644
--- a/Modules/ExpansionLandingPage/GreatVault.lua
+++ b/Modules/ExpansionLandingPage/GreatVault.lua
@@ -45,11 +45,13 @@ do
self.Text:SetText("0/0");
end
+ local WORLD_MAX_LEVEL = addon.WeeklyRewardsConstant.GreatVaultWorldActivityMaxLevel;
+
local ActivityMaxLevels = {
-- [TEMPFIX] Hardcode Delves level because GetNextActivitiesIncrease return false
- [196] = 8,
- [197] = 8,
- [198] = 8,
+ [196] = WORLD_MAX_LEVEL,
+ [197] = WORLD_MAX_LEVEL,
+ [198] = WORLD_MAX_LEVEL,
};
function GreatVaultButtonMixin:IsRewardAtHighestTier()
@@ -305,6 +307,7 @@ function LandingPageUtil.CreateGreatVaultFrame(parent)
"HandlePreviewRaidRewardTooltip",
"HandlePreviewMythicRewardTooltip",
"HandlePreviewPvPRewardTooltip",
+ "HasMultipleRaidInstances",
};
local function ShowTooltip(self)
diff --git a/Modules/ExpansionLandingPage/MinimapButton.lua b/Modules/ExpansionLandingPage/MinimapButton.lua
index 554731e..a9a9282 100644
--- a/Modules/ExpansionLandingPage/MinimapButton.lua
+++ b/Modules/ExpansionLandingPage/MinimapButton.lua
@@ -744,6 +744,9 @@ do --Order Hall, RightClickMenu
OrderHallUtil.blizzardButtonHidden = true;
button:UnregisterAllEvents();
button:Hide();
+ C_Timer.After(0, function()
+ button:Hide();
+ end);
end
end
@@ -776,6 +779,103 @@ do --Order Hall, RightClickMenu
end
+do --Notification / Alert / Banner
+ function ButtonMixin:AlertFrame_Init()
+ local f = self.AlertFrame;
+
+ --API.DisableSharpening(f.BorderTop);
+ f.BorderTop:SetTexture(Def.TextureFile);
+ f.BorderTop:SetTexCoord(256/512, 512/512, 264/512, 312/512);
+ --API.DisableSharpening(f.BorderBottom);
+ f.BorderBottom:SetTexture(Def.TextureFile);
+ f.BorderBottom:SetTexCoord(256/512, 512/512, 264/512, 312/512);
+
+ local v = 0.02;
+ local a = 0.8;
+ local color1 = CreateColor(1, 1, 1, 0);
+ local color2 = CreateColor(1, 1, 1, 1);
+ f.GradientCenter:SetColorTexture(v, v, v, a);
+ f.GradientLeft:SetColorTexture(v, v, v, a);
+ f.GradientRight:SetColorTexture(v, v, v, a);
+ f.GradientLeft:SetGradient("HORIZONTAL", color1, color2);
+ f.GradientRight:SetGradient("HORIZONTAL", color2, color1);
+
+ local gradientLength = 24;
+ f.GradientLeft:SetWidth(gradientLength);
+ f.GradientRight:SetWidth(gradientLength);
+ end
+
+ function ButtonMixin:AlertFrame_ShowText(text)
+ local f = self.AlertFrame;
+ f.Text:SetText(text);
+ local textWidth = math.ceil(f.Text:GetWrappedWidth());
+ local textHeight = math.ceil(f.Text:GetHeight());
+ f.alertFrameWidth = math.max(192, textWidth + 48);
+ f.alertFrameHeight = textHeight + 24;
+
+ local easingFunc = addon.EasingFunctions.outSine;
+ local duration = 0.5;
+ local alertFrameFromWidth = 32;
+
+ f:SetHeight(f.alertFrameHeight);
+ f:SetAlpha(0);
+ f.Text:SetAlpha(0);
+
+ local function AlertFrame_Text_OnUpdate(_self, elapsed)
+ _self.t = _self.t + elapsed;
+ local alpha = 4 * _self.t - 1;
+ if alpha > 1 then
+ _self:SetScript("OnUpdate", nil);
+ alpha = 1;
+ elseif alpha < 0 then
+ alpha = 0;
+ end
+ f.Text:SetAlpha(alpha);
+ end
+
+ local function AlertFrame_AnimIn_OnUpdate(_self, elapsed)
+ _self.t = _self.t + elapsed;
+ local width = easingFunc(_self.t, alertFrameFromWidth, f.alertFrameWidth, duration);
+ local alpha = 4 * _self.t;
+ if _self.t > duration then
+ _self:SetScript("OnUpdate", AlertFrame_Text_OnUpdate);
+ _self.t = 0;
+ width = f.alertFrameWidth;
+ end
+ f:SetWidth(width);
+ if alpha > 1 then
+ alpha = 1;
+ end
+ f:SetAlpha(alpha);
+ end
+
+ f.t = 0;
+ f:SetScript("OnUpdate", AlertFrame_AnimIn_OnUpdate);
+ f:Show();
+
+ f:SetScript("OnMouseDown", function(_, button)
+ f:Hide();
+ if button == "LeftButton" then
+ PlumberExpansionLandingPage:ShowTraitTab();
+ end
+ end);
+ end
+
+ CallbackRegistry:RegisterCallback("LandingPage.HasPurchasableTrait", function(hasPurchasableTrait)
+ if hasPurchasableTrait and (not InCombatLockdown()) then
+ if MiniButton and MiniButton:IsVisible() and (not MiniButton.AlertFrame:IsShown()) and (not PlumberExpansionLandingPage:IsShown()) then
+ MiniButton:AlertFrame_Init();
+ MiniButton:AlertFrame_ShowText(OMNIUM_FOLIO_UNSPENT_POINTS);
+ end
+ else
+ if MiniButton then
+ MiniButton.AlertFrame:Hide();
+ end
+ end
+ end);
+end
+
+
do --Button Settings/Customize
Options.ButtonBaseSizes = {24, 36};
diff --git a/Modules/ExpansionLandingPage/MinimapButton.xml b/Modules/ExpansionLandingPage/MinimapButton.xml
index a2fa439..4ab7eef 100644
--- a/Modules/ExpansionLandingPage/MinimapButton.xml
+++ b/Modules/ExpansionLandingPage/MinimapButton.xml
@@ -82,6 +82,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Modules/ExpansionLandingPage/ModuleRegistry.lua b/Modules/ExpansionLandingPage/ModuleRegistry.lua
index 90e351c..8e14c68 100644
--- a/Modules/ExpansionLandingPage/ModuleRegistry.lua
+++ b/Modules/ExpansionLandingPage/ModuleRegistry.lua
@@ -5,6 +5,7 @@ local API = addon.API;
local L = addon.L;
local CallbackRegistry = addon.CallbackRegistry;
local FactionUtil = addon.FactionUtil;
+local LandingPageUtil = addon.LandingPageUtil;
local IsExpansionLandingPageUnlockedForPlayer = C_PlayerInfo.IsExpansionLandingPageUnlockedForPlayer or API.Nop;
@@ -34,7 +35,7 @@ local function AddonCompartment_OnEnter(menuButton, data)
tooltip:SetOwner(menuButton, "ANCHOR_NONE");
tooltip:SetPoint("TOPRIGHT", menuButton, "TOPLEFT", -12, 0);
- local title = addon.LandingPageUtil.GetModuleNameWithHotkey();
+ local title = LandingPageUtil.GetModuleNameWithHotkey();
tooltip:SetText(title, 1, 1, 1, 1, true);
tooltip:AddLine(L["Plumber Experimental Feature Tooltip"], 1, 0.82, 0, true);
tooltip:Show();
@@ -48,6 +49,15 @@ end
local EL = CreateFrame("Frame");
EL:RegisterEvent("LOADING_SCREEN_DISABLED");
+EL.events = {
+ "QUEST_ACCEPTED",
+ "QUEST_TURNED_IN",
+};
+
+if C_EventUtils.IsEventValid("TRAIT_TREE_CURRENCY_INFO_UPDATED") then
+ table.insert(EL.events, "TRAIT_TREE_CURRENCY_INFO_UPDATED");
+end
+
EL:SetScript("OnEvent", function(self, event, ...)
if event == "LOADING_SCREEN_DISABLED" then
self:UnregisterEvent(event);
@@ -56,6 +66,7 @@ EL:SetScript("OnEvent", function(self, event, ...)
if ShouldShowWarWithinLandingPage() and FactionUtil:IsAnyParagonRewardPending() then
API.TriggerExpansionMinimapButtonAlert(L["Paragon Reward Available"]);
end
+ LandingPageUtil.HandleTraitTreeCurrencyChanged(1186);
end);
end
elseif event == "QUEST_ACCEPTED" then
@@ -72,6 +83,9 @@ EL:SetScript("OnEvent", function(self, event, ...)
CallbackRegistry:Trigger("ParagonRewardQuestTurnedIn", questID);
CallbackRegistry:Trigger("LandingPage.UpdateNotification");
end
+ elseif event == "TRAIT_TREE_CURRENCY_INFO_UPDATED" then
+ local treeID = ...
+ LandingPageUtil.HandleTraitTreeCurrencyChanged(treeID);
end
end);
@@ -79,19 +93,17 @@ function EL.EnableModule(state)
if state then
if not EL.enabled then
EL.enabled = true;
- EL:RegisterEvent("QUEST_ACCEPTED");
- EL:RegisterEvent("QUEST_TURNED_IN");
+ API.RegisterFrameForEvents(EL, EL.events);
API.AddButtonToAddonCompartment(IDENTIFIER, L["Abbr NewExpansionLandingPage"], nil, AddonCompartment_OnClick, AddonCompartment_OnEnter, AddonCompartment_OnLeave);
end
else
if EL.enabled then
EL.enabled = false;
- EL:UnregisterEvent("QUEST_ACCEPTED");
- EL:UnregisterEvent("QUEST_TURNED_IN");
+ API.UnregisterFrameForEvents(EL, EL.events);
API.RemoveButtonFromAddonCompartment(IDENTIFIER);
end
end
- addon.LandingPageUtil.UpdateMinimapButtonVisibility();
+ LandingPageUtil.UpdateMinimapButtonVisibility();
end
@@ -104,7 +116,7 @@ do
categoryID = 1,
uiOrder = -10,
moduleAddedTime = 1750160000,
- optionToggleFunc = addon.LandingPageUtil.ToggleMinimapSettings,
+ optionToggleFunc = LandingPageUtil.ToggleMinimapSettings,
validityCheck = function()
return addon.IsToCVersionEqualOrNewerThan(50000);
end,
diff --git a/Modules/ExpansionLandingPage/Retail/ResourceList.lua b/Modules/ExpansionLandingPage/Retail/ResourceList.lua
index 0ffee47..d3c36ca 100644
--- a/Modules/ExpansionLandingPage/Retail/ResourceList.lua
+++ b/Modules/ExpansionLandingPage/Retail/ResourceList.lua
@@ -72,6 +72,17 @@ do --MID
{currencyID = 2797, shownIfOwned = true}, --Trophy of Strife
};
+ if addon.IS_12_0_7 then
+ local crests = addon.ItemUpgradeConstant.Crests;
+ if crests then
+ for _, currencyID in ipairs(crests) do
+ table.insert(ResourceList, 2, {
+ currencyID = currencyID,
+ });
+ end
+ end
+ end
+
if addon.ItemUpgradeConstant.CatalystCurrencyID then
table.insert(ResourceList, 2, {
currencyID = addon.ItemUpgradeConstant.CatalystCurrencyID,
diff --git a/Modules/ExpansionLandingPage/Retail/TraitFrame.lua b/Modules/ExpansionLandingPage/Retail/TraitFrame.lua
new file mode 100644
index 0000000..32a7b77
--- /dev/null
+++ b/Modules/ExpansionLandingPage/Retail/TraitFrame.lua
@@ -0,0 +1,325 @@
+local _, addon = ...
+local API = addon.API;
+local LandingPageUtil = addon.LandingPageUtil;
+
+
+local MainFrame;
+
+
+local QUEST_PROVIDER_MAP = 2649; -- The Lycaneum
+local QUEST_PIN_MAP = 2424; -- Use Isle map instead of the indoor map
+local TRAIT_SYSTEM_ID = 48; -- RUNES_OF_POWER_SYSTEM_ID
+local TRAIT_TREE_ID = 1186; -- RUNES_OF_POWER_TREE_ID;
+
+
+local function GetBestMapForQuest(questID)
+ if API.IsQuestReadyForTurnIn(questID) or (not C_QuestLog.IsOnQuest(questID)) then
+ return QUEST_PIN_MAP;
+ else
+ return GetQuestUiMapID(questID);
+ end
+end
+
+
+local TraitFrameMixin = {};
+do
+ local TraitContainer;
+
+ local DynamicEvents = {
+ "TRAIT_TREE_CURRENCY_INFO_UPDATED",
+ "TRAIT_CONFIG_UPDATED",
+ "CONFIG_COMMIT_FAILED",
+ "QUEST_LOG_UPDATE",
+ "QUESTLINE_UPDATE",
+ };
+
+ function TraitFrameMixin:Refresh()
+ if TraitContainer then
+ TraitContainer:Refresh();
+ else
+ TraitContainer = addon.CreateTraitContainer(self);
+ TraitContainer:SetPoint("CENTER", self, "CENTER", 0, 0);
+ TraitContainer:SetScale(36/40);
+ TraitContainer:SetConfigIDBySystemID(TRAIT_SYSTEM_ID);
+ TraitContainer:SetEnableAutoCommit(true);
+ end
+ self:UpdateHeader();
+ end
+
+ function TraitFrameMixin:OnShow()
+ self:Refresh();
+ API.RegisterFrameForEvents(self, DynamicEvents);
+ C_QuestLine.RequestQuestLinesForMap(QUEST_PROVIDER_MAP);
+ end
+
+ function TraitFrameMixin:OnHide()
+ API.UnregisterFrameForEvents(self, DynamicEvents);
+ end
+
+ function TraitFrameMixin:OnEvent(event, ...)
+ if event == "TRAIT_CONFIG_UPDATED" or event == "CONFIG_COMMIT_FAILED" then
+ local configID = ...
+ if configID and configID == TraitContainer.configID then
+ self:Refresh();
+ end
+ elseif event == "TRAIT_TREE_CURRENCY_INFO_UPDATED" then
+ -- Fire twice for auto commiting trait systems
+ local treeID = ...
+ if treeID and treeID == TraitContainer.treeID and TraitContainer.configID then
+ self:Refresh();
+ end
+ elseif event == "QUEST_LOG_UPDATE" or event == "QUESTLINE_UPDATE" then
+ C_QuestLine.RequestQuestLinesForMap(QUEST_PROVIDER_MAP);
+ self:RequestUpdate();
+ end
+ end
+
+ function TraitFrameMixin:RequestUpdate()
+ self.t = 0;
+ self:SetScript("OnUpdate", self.OnUpdate);
+ end
+
+ function TraitFrameMixin:OnUpdate(elapsed)
+ self.t = self.t + elapsed;
+ if self.t >= 0.5 then
+ self.t = 0;
+ self:SetScript("OnUpdate", nil);
+ self:Refresh();
+ end
+ end
+
+ function TraitFrameMixin:UpdateTraitSpentInstruction()
+ -- If player has any unspent currency, show it on the header
+ local configID = TraitContainer.configID;
+ local treeID = TraitContainer.treeID;
+
+ if configID and treeID and LandingPageUtil.HasAnyPurchasableTrait() then
+ local excludeStagedChanges = false;
+ local treeCurrencyInfo = C_Traits.GetTreeCurrencyInfo(configID, treeID, excludeStagedChanges);
+ local info = treeCurrencyInfo and treeCurrencyInfo[1];
+ if info and info.quantity > 0 then
+ local flags, type, currencyTypesID, icon = C_Traits.GetTraitCurrencyInfo(info.traitCurrencyID);
+ self.HeaderFrame:DisplayTraitCurrency(icon, info.quantity);
+ MainFrame.BlackOverlay:Show();
+ return true;
+ else
+ MainFrame.BlackOverlay:Hide();
+ return false;
+ end
+ else
+ MainFrame.BlackOverlay:Hide();
+ return false;
+ end
+ end
+
+ function TraitFrameMixin:UpdateQuestNotification()
+ -- When a new quest becomes available
+ local questID, isStartingQuest;
+ local quests = C_QuestLine.GetAvailableQuestLines(QUEST_PROVIDER_MAP);
+
+ if quests then
+ for _, quest in ipairs(quests) do
+ if quest.questLineID == 6307 then
+ -- The Empowered Folio
+ if not quest.isAccountCompleted then
+ isStartingQuest = true;
+ questID = quest.questID;
+ end
+ end
+ end
+ end
+
+ if not questID then
+ local questIDs = {96410, 96441, 96442, 96443, 96444};
+ for _, _questID in ipairs(questIDs) do
+ if C_QuestLog.IsOnQuest(_questID) then
+ questID = _questID;
+ break
+ end
+ end
+ end
+
+ if questID then
+ self.HeaderFrame:DisplayQuest(questID, isStartingQuest);
+ return true;
+ else
+ return false;
+ end
+ end
+
+ function TraitFrameMixin:ShowHeaderFrame(state)
+ if state then
+ self.HeaderFrame:SetPoint("CENTER", self.listCategoryButton, "CENTER", 0, 0);
+ self.HeaderFrame:Show();
+ self.listCategoryButton.Name:Hide();
+ self.listCategoryButton:EnableMouseMotion(false);
+ else
+ self.HeaderFrame:Hide();
+ self.listCategoryButton.Name:Show();
+ end
+ end
+
+ function TraitFrameMixin:UpdateHeader()
+ local anyShown = self:UpdateTraitSpentInstruction();
+ if not anyShown then
+ anyShown = self:UpdateQuestNotification();
+ local minimapButton = ExpansionLandingPageMinimapButton;
+ if minimapButton and minimapButton:IsShown() then
+ HelpTip:Hide(minimapButton);
+ minimapButton:ClearPulses();
+ end
+ end
+ self:ShowHeaderFrame(anyShown);
+ end
+end
+
+local HeaderFrameMixin = {};
+do
+ function HeaderFrameMixin:DisplayTraitCurrency(icon, quantity)
+ self.clickResponse = nil;
+ self.Icon:ClearAllPoints();
+ self.Text:ClearAllPoints();
+ self.Icon:SetSize(16, 16);
+ self.Icon:SetPoint("LEFT", self, "CENTER", 0, 0);
+ self.Icon:SetTexture(icon);
+ self.Text:SetPoint("RIGHT", self.Icon, "LEFT", -4, 0);
+ self.Text:SetText(quantity);
+ self.Text:SetTextColor(0.098, 1.000, 0.098);
+ self:SetScript("OnEnter", self.ShowTooltipSpell);
+ end
+
+ function HeaderFrameMixin:ShowTooltipSpell()
+ local tooltip = GameTooltip;
+ tooltip:SetOwner(self.Icon, "ANCHOR_RIGHT");
+ tooltip:SetSpellByID(1294322); -- Mote of Omnial Inquiry
+ end
+
+ function HeaderFrameMixin:DisplayQuest(questID, isStartingQuest)
+ self.clickResponse = "quest";
+ self.Icon:ClearAllPoints();
+ self.Text:ClearAllPoints();
+ self.Text:SetPoint("CENTER", self, "CENTER", 8, 0);
+ local iconOffset = 0;
+
+ if isStartingQuest then
+ self.Text:SetText(addon.L["New Quest"]);
+ self.Text:SetTextColor(0.922, 0.871, 0.761);
+ self.Icon:SetTexture("Interface/AddOns/Plumber/Art/ExpansionLandingPage/Icons/TrackerType-Quest.png");
+ self.Icon:SetTexCoord(0, 1, 0, 1);
+ elseif API.IsQuestReadyForTurnIn(questID) then
+ self.Text:SetText(QUEST_WATCH_QUEST_READY);
+ self.Text:SetTextColor(0.098, 1.000, 0.098);
+ self.Icon:SetAtlas("QuestTurnin");
+ iconOffset = -2;
+ else
+ self.Text:SetText(GARRISON_MISSION_IN_PROGRESS_TOOLTIP);
+ self.Text:SetTextColor(0.922, 0.871, 0.761);
+ self.Icon:SetTexture("Interface/AddOns/Plumber/Art/ExpansionLandingPage/Icons/InProgressRed.png");
+ self.Icon:SetTexCoord(0, 1, 0, 1);
+ iconOffset = -4;
+ end
+
+ self.Icon:SetSize(16, 16);
+ self.Icon:SetPoint("RIGHT", self.Text, "LEFT", iconOffset, 0);
+
+ self.questID = questID;
+ self:SetScript("OnEnter", self.ShowTooltipQuest);
+ end
+
+ function HeaderFrameMixin:ShowTooltipQuest()
+ if not self.questID then return; end
+ local TooltipUpdator = LandingPageUtil.TooltipUpdator;
+ TooltipUpdator:SetFocusedObject(self);
+ TooltipUpdator:SetHeaderText(API.GetQuestName(self.questID))
+ TooltipUpdator:SetQuestID(self.questID);
+ TooltipUpdator:RequestQuestProgress();
+ TooltipUpdator:RequestQuestReward();
+ local questUiMapID = GetBestMapForQuest(self.questID);
+ TooltipUpdator:SetEnableShowOnMap(questUiMapID);
+ end
+
+ function HeaderFrameMixin:OnLeave()
+ GameTooltip:Hide();
+ LandingPageUtil.TooltipUpdator:StopUpdating();
+ end
+
+ function HeaderFrameMixin:OnClick(button)
+ if self.clickResponse == "quest" then
+ if button == "LeftButton" and IsControlKeyDown() and (not InCombatLockdown()) then
+ API.SuperTrackQuestMapPin(self.questID);
+ local questUiMapID = GetBestMapForQuest(self.questID);
+ C_Map.OpenWorldMap(questUiMapID);
+ end
+ end
+ end
+end
+
+
+function LandingPageUtil.GetTraitSystemName()
+ return RUNES_OF_POWER;
+end
+
+function LandingPageUtil.HasAnyPurchasableTrait()
+ return API.HasAnyPurchasableTraitInSystem(TRAIT_SYSTEM_ID);
+end
+
+
+do --Event Handler
+ local Frame = CreateFrame("Frame");
+
+ function LandingPageUtil.HandleTraitTreeCurrencyChanged(treeID)
+ if treeID == TRAIT_TREE_ID then
+ if not Frame.t then
+ Frame.t = 0;
+ Frame:SetScript("OnUpdate", function(self, elapsed)
+ self.t = self.t + elapsed;
+ if self.t >= 0.1 then
+ self.t = nil;
+ self:SetScript("OnUpdate", nil);
+ addon.CallbackRegistry:Trigger("LandingPage.HasPurchasableTrait", LandingPageUtil.HasAnyPurchasableTrait());
+ end
+ end);
+ end
+ Frame.t = 0;
+ end
+ end
+end
+
+
+function LandingPageUtil.CreateTraitFrame(parent)
+ if MainFrame then return MainFrame; end
+
+ local f = CreateFrame("Frame", nil, parent);
+ MainFrame = f;
+ local width = 240;
+ local height = 40;
+ f:SetSize(width, height);
+ f:SetFrameLevel(LandingPageUtil.GetUIFrameLevel() + 10);
+
+ Mixin(f, TraitFrameMixin);
+ f:SetScript("OnShow", f.OnShow);
+ f:SetScript("OnHide", f.OnHide);
+ f:SetScript("OnEvent", f.OnEvent);
+
+ local HeaderFrame = CreateFrame("Button", nil, f);
+ f.HeaderFrame = HeaderFrame;
+ HeaderFrame:Hide();
+ HeaderFrame:SetSize(240, 24);
+ HeaderFrame.Icon = HeaderFrame:CreateTexture(nil, "OVERLAY");
+ HeaderFrame.Text = HeaderFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal");
+ Mixin(HeaderFrame, HeaderFrameMixin);
+ HeaderFrame:SetScript("OnEnter", HeaderFrame.OnEnter);
+ HeaderFrame:SetScript("OnLeave", HeaderFrame.OnLeave);
+ HeaderFrame:SetScript("OnClick", HeaderFrame.OnClick);
+
+ local container = PlumberExpansionLandingPage.LeftSection;
+ f.BlackOverlay = f:CreateTexture(nil, "BACKGROUND");
+ f.BlackOverlay:Hide();
+ f.BlackOverlay:SetColorTexture(0, 0, 0, 0.8);
+ f.BlackOverlay:SetPoint("TOPLEFT", container, "TOPLEFT", 8, -8);
+ f.BlackOverlay:SetPoint("BOTTOMRIGHT", container, "BOTTOMRIGHT", -8, 8);
+
+ return f, height
+end
+
+-- Interface/AddOns/Blizzard_ExpansionLandingPage/Blizzard_MidnightLandingPage.lua
diff --git a/Modules/ExpansionLandingPage/TooltipUpdator.lua b/Modules/ExpansionLandingPage/TooltipUpdator.lua
index e5e22b7..62d801d 100644
--- a/Modules/ExpansionLandingPage/TooltipUpdator.lua
+++ b/Modules/ExpansionLandingPage/TooltipUpdator.lua
@@ -12,7 +12,18 @@ TooltipUpdator:Hide();
LandingPageUtil.TooltipUpdator = TooltipUpdator;
+local SkippedQuestRewardSpell = {
+ -- Ignore random/end-of-questline reward
+ -- Enum.QuestCompleteSpellType
+ [8] = true, --QuestlineUnlock
+ [9] = true, --QuestlineReward
+ [10] = true, --QuestlineUnlockPart
+ [11] = true, --PossibleReward
+};
+
local function Tooltip_AddRewardLine(tooltip, texture, text, quality, quantity)
+ quality = quality or 1;
+ quantity = quantity or 1;
local r, g, b = ColorManager.GetColorDataForItemQuality(quality).color:GetRGB();
tooltip:AddDoubleLine(string.format("|T%s:%d:%d:%d:%d|t %s", texture, 16, 16, 0, 0, text), quantity, r, g, b, 1, 1, 1);
end
@@ -37,6 +48,8 @@ function TooltipUpdator:StopUpdating()
self.tooltipSetter = nil;
self.entryChildren = nil;
self.itemID = nil;
+ self.uiMapID = nil;
+ self.supportShowOnMap = nil;
end
function TooltipUpdator:SetFocusedObject(obj)
@@ -98,6 +111,11 @@ function TooltipUpdator:RequestItemID(itemID)
self.itemID = itemID;
end
+function TooltipUpdator:SetEnableShowOnMap(uiMapID)
+ self.uiMapID = uiMapID;
+ self.supportShowOnMap = uiMapID and C_Map.GetMapInfo(uiMapID) ~= nil;
+end
+
function TooltipUpdator:OnUpdate(elapsed)
self.t = self.t + elapsed;
if self.t >= 0.5 then
@@ -149,6 +167,9 @@ function TooltipUpdator:OnUpdate(elapsed)
if rewards.currencies then
tinsert(questRewards, rewards.currencies);
end
+ if rewards.spells then
+ tinsert(questRewards, rewards.spells);
+ end
end
if missingData then
@@ -211,9 +232,20 @@ function TooltipUpdator:OnUpdate(elapsed)
end
tooltip:AddLine(QUEST_REWARDS, 1, 0.82, 0);
+ local shouldShowReward;
for _, rewards in ipairs(questRewards) do
for index, info in ipairs(rewards) do
- Tooltip_AddRewardLine(tooltip, info.texture, info.name, info.quality, info.quantity);
+ shouldShowReward = nil;
+ if info.isSpellReward then
+ if info.type and not SkippedQuestRewardSpell[info.type] then
+ shouldShowReward = true;
+ end
+ else
+ shouldShowReward = true;
+ end
+ if shouldShowReward then
+ Tooltip_AddRewardLine(tooltip, info.texture, info.name, info.quality, info.quantity);
+ end
end
end
end
@@ -270,6 +302,11 @@ function TooltipUpdator:OnUpdate(elapsed)
--tooltip:AddLine(text, 1, 1, 1, true);
end
+ if self.uiMapID and self.supportShowOnMap and not InCombatLockdown() then
+ tooltip:AddLine(" ");
+ tooltip:AddLine(addon.L["Instruction Set Waypoint"], 0.098, 1.000, 0.098);
+ end
+
if isRetrievingData then
tooltip:AddLine(RETRIEVING_DATA, 0.5, 0.5, 0.5, true);
self.keepUpdating = true;
diff --git a/Modules/GameTooltip_TransmogEnsemble.lua b/Modules/GameTooltip_TransmogEnsemble.lua
index 56fbec8..8d0dbb6 100644
--- a/Modules/GameTooltip_TransmogEnsemble.lua
+++ b/Modules/GameTooltip_TransmogEnsemble.lua
@@ -146,13 +146,16 @@ do
local Weapons = {
--[itemSubclass] = {subclassID, itemID1, itemID2},
{10, 263952, 263954},
+ {2, 263967},
+ {3, 263941},
{6, 263950, 273874},
{8, 263966},
{7, 263960, 263963},
{4, 263946, 263956},
{15, 263942, 263943},
{13, 263970},
- {14, 263959}, -- Offhand
+ {19, 263968, 263969},
+ {14, 263959, 263949}, -- Offhand
};
function VoidTouchUtil:TryProcessItem(tooltip, itemID)
diff --git a/Modules/LootUI_Display.lua b/Modules/LootUI_Display.lua
index 8e12366..16c2440 100644
--- a/Modules/LootUI_Display.lua
+++ b/Modules/LootUI_Display.lua
@@ -1915,7 +1915,7 @@ do --Edit Mode
{type = "Checkbox", label = L["LootUI Option New Transmog"], onClickFunc = nil, dbKey = "LootUI_NewTransmogIcon", tooltip = L["LootUI Option New Transmog Tooltip"]:format("|TInterface/AddOns/Plumber/Art/LootUI/NewTransmogIcon:0:0|t"), validityCheckFunc = Validation_TransmogInvented},
{type = "Checkbox", label = L["LootUI Option Custom Quality Color"], tooltip = L["LootUI Option Custom Quality Color Tooltip"], onClickFunc = nil, dbKey = "LootUI_UseCustomColor", validityCheckFunc = function() return ColorManager and ColorManager.GetColorDataForItemQuality ~= nil end},
{type = "Checkbox", label = L["LootUI Option Grow Direction"], tooltip = Tooltip_GrowDirection, onClickFunc = Options_GrowDirection_OnClick, dbKey = "LootUI_GrowUpwards", keepTooltipAfterClicks = true},
- {type = "Checkbox", label = L["LootUI Option Combine Items"], tooltip = L["LootUI Option Combine Items Tooltip"], onClickFunc = nil, dbKey = "LootUI_CombineItems"},
+ {type = "Checkbox", label = L["LootUI Option Combine Items"], tooltip = L["LootUI Option Combine Items Tooltip"], onClickFunc = nil, dbKey = "LootUI_CombineItem"},
{type = "Checkbox", label = L["LootUI Option Low Frame Strata"], tooltip = L["LootUI Option Low Frame Strata Tooltip"], onClickFunc = nil, dbKey = "LootUI_LowFrameStrata"},
{type = "Checkbox", label = L["LootUI Option Hide Title"], tooltip = L["LootUI Option Hide Title Tooltip"], onClickFunc = nil, dbKey = "LootUI_HideTitle"},
@@ -2046,7 +2046,7 @@ do --Edit Mode
local function SettingChanged_CombineItems(state, userInput)
MERGE_SIMILAR_ITEMS = state;
end
- addon.CallbackRegistry:RegisterSettingCallback("LootUI_CombineItems", SettingChanged_CombineItems);
+ addon.CallbackRegistry:RegisterSettingCallback("LootUI_CombineItem", SettingChanged_CombineItems);
local function SettingChanged_LowFrameStrata(state, userInput)
LOW_FRAME_STRATA = state;
diff --git a/Modules/LootUI_Main.lua b/Modules/LootUI_Main.lua
index 59cd240..2851dc2 100644
--- a/Modules/LootUI_Main.lua
+++ b/Modules/LootUI_Main.lua
@@ -2026,7 +2026,7 @@ do --Rare Items
elseif data.classID == 17 then
-- Battlepet
return true;
- elseif data.classID == 2 or data.classID == 4 and data.link and C_Item.GetItemStats then
+ elseif (data.classID == 2 or data.classID == 4) and data.link and C_Item.GetItemStats then
-- Equipment with tertiary stats. Retail only.
local stats = C_Item.GetItemStats(data.link);
if stats then
@@ -2104,7 +2104,7 @@ do --Callback Registery
local function SettingChanged_CombineItems(state, userInput)
MERGE_JUNKS = state;
end
- addon.CallbackRegistry:RegisterSettingCallback("LootUI_CombineItems", SettingChanged_CombineItems);
+ addon.CallbackRegistry:RegisterSettingCallback("LootUI_CombineItem", SettingChanged_CombineItems);
end
diff --git a/Modules/Shared/Load.xml b/Modules/Shared/Load.xml
index 85c389d..efe34d1 100644
--- a/Modules/Shared/Load.xml
+++ b/Modules/Shared/Load.xml
@@ -7,4 +7,5 @@
+
diff --git a/Modules/Shared/SharedData.lua b/Modules/Shared/SharedData.lua
index f943518..41d9b94 100644
--- a/Modules/Shared/SharedData.lua
+++ b/Modules/Shared/SharedData.lua
@@ -156,7 +156,11 @@ do --Weekly Caches (Meta quest rewards)
259,
259,
+ 263,
+ 269,
},
+
+ GreatVaultWorldActivityMaxLevel = 13,
};
diff --git a/Modules/Shared/SharedWidgets.xml b/Modules/Shared/SharedWidgets.xml
index e514d47..14ac14b 100644
--- a/Modules/Shared/SharedWidgets.xml
+++ b/Modules/Shared/SharedWidgets.xml
@@ -728,4 +728,88 @@
+
+
diff --git a/Modules/Shared/TraitSystem.lua b/Modules/Shared/TraitSystem.lua
new file mode 100644
index 0000000..39ac4bf
--- /dev/null
+++ b/Modules/Shared/TraitSystem.lua
@@ -0,0 +1,1052 @@
+local _, addon = ...
+local API = addon.API;
+
+
+if not (C_Traits and C_Traits.GetNodeInfo) then
+ function API.HasAnyPurchasableTraitInSystem()
+ return false;
+ end
+end
+
+
+local Def = {
+ Art = "Interface/AddOns/Plumber/Art/Frame/TraitSystem.png",
+ ButtonSize = 40,
+ ButtonGap = 10,
+};
+
+
+local SharedNodeFocusSolver;
+local CommitUtil = CreateFrame("Frame");
+local Easing_OutQuart = addon.EasingFunctions.outQuart;
+
+
+local NodeButtonMixin = {};
+do
+ function NodeButtonMixin:OnLoad()
+ self.Border:SetTexture(Def.Art);
+ self.GreenGlow:SetTexture(Def.Art);
+ self.Sheen:SetTexture(Def.Art);
+ self:SetSquare();
+ self.Icon:SetTexCoord(4/64, 60/64, 4/64, 60/64);
+ self.Border:SetSize(64, 64);
+ self.Icon:SetSize(36, 36);
+ self.IconMask:SetSize(36, 36);
+ self:RegisterForClicks("LeftButtonUp", "RightButtonUp");
+ self:SetScript("OnEnter", self.OnEnter);
+ self:SetScript("OnLeave", self.OnLeave);
+ self:SetScript("OnDragStart", self.OnDragStart);
+ self:SetScript("OnClick", self.OnClick);
+
+
+ local function AnimSheen_OnPlay()
+ self.SheenMask:Show();
+ self.Sheen:Show();
+ end
+
+ local function AnimSheen_OnStop()
+ self.SheenMask:Hide();
+ self.Sheen:Hide();
+ end
+
+ self.AnimSheen:SetScript("OnPlay", AnimSheen_OnPlay);
+ self.AnimSheen:SetScript("OnFinished", AnimSheen_OnStop);
+ self.AnimSheen:SetScript("OnStop", AnimSheen_OnStop);
+ end
+
+ function NodeButtonMixin:OnEnter()
+ self.ownerFrame:HoverNode(self);
+ end
+
+ function NodeButtonMixin:OnLeave()
+ self.ownerFrame:HoverNode();
+ if self.entryIDs or self.isFlyoutButton then
+ if not self.ownerFrame:IsNodeFlyoutFocused(self) then
+ self.ownerFrame:CloseNodeFlyout();
+ end
+ end
+ end
+
+ function NodeButtonMixin:OnDragStart()
+
+ end
+
+ function NodeButtonMixin:OnFocused()
+ if self.entryIDs then
+ self.ownerFrame:ShowNodeFlyout(self);
+ else
+ self:ShowTooltip();
+ end
+ end
+
+ function NodeButtonMixin:SetSpell(spellID)
+ local iconID, originalIconID = C_Spell.GetSpellTexture(spellID);
+ self.Icon:SetTexture(originalIconID or iconID);
+ self.spellID = spellID;
+ end
+
+ function NodeButtonMixin:SetNode(nodeID)
+ self.nodeID = nodeID;
+ self.isNodeDirty = true;
+ end
+
+ function NodeButtonMixin:SetEntry(entryID)
+ self.entryID = entryID;
+ local entryInfo = self.ownerFrame:GetEntryInfo(entryID);
+ self.definitionID = entryInfo.definitionID;
+ self.maxRanks = entryInfo.maxRanks;
+
+ if self.entryType ~= 0 then
+ if entryInfo.type == 1 then --SpendSquare
+ self:SetSquare();
+ elseif entryInfo.type == 2 then --SpendCircle
+ self:SetCircle();
+ end
+ end
+
+ local spellID = C_Traits.GetDefinitionInfo(self.definitionID).spellID;
+ self:SetSpell(spellID);
+ end
+
+ function NodeButtonMixin:SetSquare()
+ self.entryType = 1;
+ self.IconMask:SetTexture("Interface/AddOns/Plumber/Art/BasicShape/Mask-Chamfer", "CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE");
+ self.Icon:Show();
+ self.IconMask:Show();
+ self.GreenGlow:SetTexCoord(384/1024, 544/1024, 0/1024, 160/1024);
+ end
+
+ function NodeButtonMixin:SetCircle()
+ self.entryType = 2;
+ self.IconMask:SetTexture("Interface/AddOns/Plumber/Art/BasicShape/Mask-Circle", "CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE");
+ self.Icon:Show();
+ self.IconMask:Show();
+ self.GreenGlow:SetTexCoord(544/1024, 704/1024, 0/1024, 160/1024);
+ end
+
+ function NodeButtonMixin:SetHex()
+ self.entryType = 0;
+ self.IconMask:SetTexture("Interface/AddOns/Plumber/Art/BasicShape/Mask-Hexagon", "CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE");
+ self.Icon:Show();
+ self.IconMask:Show();
+ self.GreenGlow:SetTexCoord(704/1024, 864/1024, 0/1024, 160/1024);
+ self.Sheen:SetTexCoord(768/1024, 928/1024, 576/1024, 736/1024);
+ self.SheenMask:SetTexture("Interface/AddOns/Plumber/Art/Timerunning/Mask-Halo", "CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE");
+ end
+
+ function NodeButtonMixin:SetVisualState(visualState)
+ --0:Disabled 1:Yellow 2:Green
+
+ self.visualState = visualState;
+ local disabled = visualState == 0;
+
+ if disabled then
+ self.Icon:SetDesaturated(true);
+ self.Icon:SetVertexColor(0.8, 0.8, 0.8);
+ self.GreenGlow:Hide();
+ else
+ self.Icon:SetDesaturated(false);
+ self.Icon:SetVertexColor(1, 1, 1);
+ end
+
+ local left, right, top, bottom;
+
+ if disabled then
+ top, bottom = 256, 384;
+ elseif visualState == 2 then
+ top, bottom = 128, 256;
+ else
+ top, bottom = 0, 128;
+ end
+
+ if self.entryType == 1 then
+ left, right = 0, 128;
+ elseif self.entryType == 2 then
+ left, right = 128, 256;
+ elseif self.entryType == 0 then
+ left, right = 256, 384;
+ end
+
+ self.Border:SetTexCoord(left/1024, right/1024, top/1024, bottom/1024);
+ end
+
+ function NodeButtonMixin:CanAfford()
+ return self.ownerFrame:CanAffordNode(self.nodeID);
+ end
+
+ function NodeButtonMixin:CanPurchaseRank()
+ if self.nodeInfo then
+ if self.nodeInfo.canPurchaseRank and self:CanAfford() then
+ return true;
+ elseif #self.nodeInfo.entryIDs > 1 and self.nodeInfo.entryIDsWithCommittedRanks[1] then
+ return self.entryID and self.entryID ~= self.nodeInfo.entryIDsWithCommittedRanks[1];
+ end
+ end
+ return false;
+ end
+
+ function NodeButtonMixin:CanRefundRank()
+ return self.nodeInfo and self.nodeInfo.canRefundRank;
+ end
+
+ function NodeButtonMixin:IsSelectionNode()
+ if not self.nodeInfo then
+ self.nodeInfo = self.ownerFrame:GetNodeInfo(self.nodeID);
+ end
+ return self.nodeInfo and self.nodeInfo.type == 2;
+ end
+
+ function NodeButtonMixin:Refresh(playAnimation)
+ local nodeInfo = self.nodeID and self.ownerFrame:GetNodeInfo(self.nodeID);
+ self.nodeInfo = nodeInfo;
+ if not nodeInfo then return; end
+
+ if self.isNodeDirty then
+ self.isNodeDirty = nil;
+ local entryID;
+
+ if self.isFlyoutButton and self.entryChoiceIndex then
+ entryID = nodeInfo.entryIDs[self.entryChoiceIndex];
+ end
+
+ if (not entryID) and nodeInfo.entryIDsWithCommittedRanks then
+ entryID = nodeInfo.entryIDsWithCommittedRanks[1];
+ end
+
+ if not entryID then
+ entryID = nodeInfo.entryIDs[1];
+ end
+
+ self.entryID = entryID;
+
+ if nodeInfo.type == 2 and (not self.isFlyoutButton) then --Enum.TraitNodeType.Selection
+ self:SetHex();
+ self.entryIDs = nodeInfo.entryIDs;
+ else
+ self.entryIDs = nil;
+ end
+
+ local entryInfo = self.ownerFrame:GetEntryInfo(entryID);
+ self.definitionID = entryInfo.definitionID;
+ self.maxRanks = entryInfo.maxRanks;
+
+ if self.entryType ~= 0 then
+ if entryInfo.type == 1 then --SpendSquare
+ self:SetSquare();
+ elseif entryInfo.type == 2 then --SpendCircle
+ self:SetCircle();
+ end
+ end
+
+ local spellID = C_Traits.GetDefinitionInfo(self.definitionID).spellID;
+ self:SetSpell(spellID);
+ end
+
+ if self.entryIDs then
+ self:Refresh_SelectionNode(nodeInfo, playAnimation);
+ return
+ end
+
+ if self.isFlyoutButton then
+ self:Refresh_FlyoutButton(nodeInfo, playAnimation);
+ return
+ end
+
+ local currentRank = nodeInfo.currentRank or 0;
+ local ranksPurchased = nodeInfo.ranksPurchased or 0;
+
+ local isActive = ranksPurchased > 0;
+ self.isActive = isActive;
+
+ local visualState;
+ local rankText;
+ local useGreenGlow = false;
+
+ if self:CanPurchaseRank() then
+ visualState = 2;
+ useGreenGlow = true;
+ elseif not (isActive or currentRank > 0) then
+ visualState = 0;
+ else
+ if self.entryType == 0 then
+ if self.selectedEntryID then
+ visualState = 1;
+ else
+ visualState = 2;
+ end
+ else
+ visualState = 1;
+ end
+ end
+
+ if isActive or currentRank > 0 then
+ if self.entryType == 1 then
+ rankText = currentRank;
+ elseif self.entryType == 2 then
+ rankText = currentRank;
+ elseif self.entryType == 0 then
+ if not self.selectedEntryID then
+ useGreenGlow = true;
+ end
+ end
+ self.RankText:SetTextColor(1, 0.82, 0);
+ end
+
+ self:SetVisualState(visualState);
+ self.RankText:SetText(rankText);
+ self.GreenGlow:SetShown(useGreenGlow);
+ end
+
+ function NodeButtonMixin:Refresh_SelectionNode(nodeInfo, playAnimation)
+ local currentRank = nodeInfo.currentRank or 0;
+ local activeEntryID;
+ local committedEntryID;
+ local rankText;
+
+ local isEntryCommitted = false;
+ if nodeInfo.entryIDsWithCommittedRanks then
+ local id = nodeInfo.entryIDsWithCommittedRanks[1];
+ committedEntryID = id;
+ activeEntryID = id;
+ isEntryCommitted = id and true;
+ end
+
+ if not isEntryCommitted then
+ if nodeInfo.entryIDToRanksIncreased then
+ for _entryID, totalIncreased in pairs(nodeInfo.entryIDToRanksIncreased) do
+ if totalIncreased > 0 then
+ activeEntryID = _entryID;
+ currentRank = totalIncreased;
+ break
+ end
+ end
+ end
+ end
+
+ self.selectedEntryID = committedEntryID;
+
+ local entryChanged;
+ local visualState;
+ local useGreenGlow = false;
+
+ if isEntryCommitted then
+ entryChanged = activeEntryID ~= self.entryID;
+ self:SetEntry(activeEntryID);
+ visualState = 1;
+ rankText = currentRank;
+ self.RankText:SetTextColor(1, 0.82, 0);
+ if self.shouldPlaySheen then
+ self.shouldPlaySheen = nil;
+ self:PlaySheen();
+ end
+ else
+ entryChanged = activeEntryID ~= self.selectedEntryID;
+ self:SetEntry(self.selectedEntryID or activeEntryID or nodeInfo.entryIDs[1]);
+ if self:CanPurchaseRank() then
+ visualState = 2;
+ useGreenGlow = true;
+ else
+ visualState = 0;
+ end
+ end
+
+ if visualState ~= 0 and entryChanged then
+ if self.shouldPlaySheen then
+ self.shouldPlaySheen = nil;
+ if playAnimation then
+ self:PlaySheen();
+ end
+ end
+ end
+
+ self:SetVisualState(visualState);
+ self.RankText:SetText(rankText);
+ self.GreenGlow:SetShown(useGreenGlow);
+ end
+
+ function NodeButtonMixin:Refresh_FlyoutButton(nodeInfo, playAnimation)
+ local currentRank = nodeInfo.currentRank or 0;
+ local ranksPurchased = nodeInfo.ranksPurchased or 0;
+ local increasedRanks = 0;
+
+ if nodeInfo.entryIDToRanksIncreased then
+ for _entryID, totalIncreased in pairs(nodeInfo.entryIDToRanksIncreased) do
+ if _entryID == self.entryID then
+ if currentRank == 0 then
+ currentRank = totalIncreased;
+ end
+ increasedRanks = totalIncreased;
+ break
+ end
+ end
+ end
+
+ local committedEntryID;
+ if nodeInfo.entryIDsWithCommittedRanks then
+ for _, id in ipairs(nodeInfo.entryIDsWithCommittedRanks) do
+ committedEntryID = id;
+ if committedEntryID then
+ break
+ end
+ end
+ end
+
+ local rankText;
+ local visualState;
+ local isSelectable = committedEntryID or self:CanPurchaseRank();
+
+ if isSelectable then
+ if self.entryID == committedEntryID then
+ visualState = 1;
+ rankText = currentRank;
+ else
+ visualState = 2;
+ rankText = increasedRanks;
+ end
+ else
+ if currentRank > 0 then
+ visualState = 1;
+ rankText = currentRank;
+ else
+ visualState = 0;
+ rankText = "";
+ end
+ end
+
+ self:SetVisualState(visualState);
+ self.RankText:SetText(rankText);
+
+ if visualState == 1 then
+ self.RankText:SetTextColor(1, 0.82, 0);
+ elseif visualState == 2 then
+ self.RankText:SetTextColor(0.098, 1.000, 0.098);
+ end
+ end
+
+ local function AddLine(oldText, newText)
+ if oldText then
+ return oldText.."\n"..newText
+ else
+ return newText
+ end
+ end
+
+ function NodeButtonMixin:ShowTooltip()
+ local name = C_Spell.GetSpellName(self.spellID);
+ if not name then
+ name = RETRIEVING_DATA;
+ end
+
+ local nodeInfo = self.ownerFrame:GetNodeInfo(self.nodeID);
+ local currentRank = nodeInfo.currentRank or 0;
+ local ranksPurchased = nodeInfo.ranksPurchased or 0;
+
+ --Bonus Ranks
+ local increasedRanks = nodeInfo.entryIDToRanksIncreased and nodeInfo.entryIDToRanksIncreased[self.entryID] or 0;
+ if self.isFlyoutButton then
+ if nodeInfo.entryIDsWithCommittedRanks then
+ for _, _entryID in ipairs(nodeInfo.entryIDsWithCommittedRanks) do
+ if _entryID == self.entryID then
+ increasedRanks = nodeInfo.ranksIncreased or 0;
+ else
+ ranksPurchased = 0;
+ end
+ end
+ end
+ else
+ increasedRanks = nodeInfo.ranksIncreased or 0;
+ end
+
+ local description;
+ description = string.format(TALENT_BUTTON_TOOLTIP_RANK_FORMAT, ranksPurchased, nodeInfo.maxRanks);
+
+ if increasedRanks > 0 then
+ description = description.." |cff19ff19+"..increasedRanks.."|r";
+ end
+
+ local activeEntryID = self.entryID;
+ description = AddLine(description, " ");
+ description = API.ConvertTooltipInfoToOneString(description, "GetTraitEntry", activeEntryID, currentRank);
+
+ local nextEntryInfo = nodeInfo.nextEntry; --(self.maxRanks and self.maxRanks > 1) and self.entryType ~= 1 and self.entryID; --self.nodeInfo.nextEntry; --debug
+ if nextEntryInfo and currentRank > 0 then
+ description = AddLine(description, " ");
+ description = AddLine(description, TALENT_BUTTON_TOOLTIP_NEXT_RANK);
+ local nextRank = currentRank + 1;
+ description = API.ConvertTooltipInfoToOneString(description, "GetTraitEntry", nextEntryInfo.entryID, nextRank);
+ end
+
+ local tooltip = GameTooltip;
+ tooltip:SetOwner(self, "ANCHOR_RIGHT");
+ tooltip:SetText(name, 1, 1, 1);
+ tooltip:AddLine(description, 1, 1, 1, true);
+
+ local instructionText, r, g, b;
+
+ if self:CanPurchaseRank() then
+ r, g, b = 0.098, 1.000, 0.098;
+ instructionText = TALENT_BUTTON_TOOLTIP_PURCHASE_INSTRUCTIONS;
+ elseif self:CanRefundRank() then
+ r, g, b = 0.502, 0.502, 0.502;
+ instructionText = TALENT_BUTTON_TOOLTIP_REFUND_INSTRUCTIONS;
+ end
+
+ if instructionText then
+ tooltip:AddLine(" ");
+ if InCombatLockdown() then
+ instructionText = addon.L["Error Change Trait In Combat"];
+ r, g, b = 1.000, 0.125, 0.125;
+ end
+ tooltip:AddLine(instructionText, r, g, b, true);
+ end
+
+ tooltip:Show();
+
+ self.UpdateTooltip = self.ShowTooltip;
+ end
+
+ function NodeButtonMixin:PlaySheen()
+ self.AnimSheen:Stop();
+ self.AnimSheen:Play();
+ end
+
+ function NodeButtonMixin:OnClick(button)
+ if InCombatLockdown() then return; end
+
+ if button == "RightButton" then
+ if self:CanRefundRank() then
+ self.ownerFrame:TryRefundRank(self.nodeID);
+ end
+ else
+ if self:CanPurchaseRank() then
+ if self.isFlyoutButton then
+ self.ownerFrame:TryPurchaseSelectionNodeByIndex(self.nodeID, self.entryChoiceIndex);
+ self.ownerFrame:CloseNodeFlyout();
+ elseif self:IsSelectionNode() then
+ --self.ownerFrame:TryPurchaseSelectionNodeByIndex(self.nodeID);
+ return;
+ else
+ self.ownerFrame:TryPurchaseRank(self.nodeID);
+ end
+ end
+ end
+ end
+end
+
+
+local TraitContainerMixin = {};
+do
+ function TraitContainerMixin:Init()
+ if self.nodeButtonPool then return; end
+
+ local function createObjectFunc()
+ local obj = CreateFrame("Button", nil, self, "PlumberTraitNodeButtonTemplate");
+ Mixin(obj, NodeButtonMixin);
+ obj.ownerFrame = self;
+ obj:OnLoad();
+ return obj;
+ end
+
+ self.nodeButtonPool = API.CreateObjectPool(createObjectFunc);
+
+ local highlight = CreateFrame("Frame", nil, self);
+ self.SharedNodeHighlight = highlight;
+ highlight:Hide();
+ highlight:SetUsingParentLevel(true);
+ highlight.Texture = highlight:CreateTexture(nil, "OVERLAY");
+ API.DisableSharpening(highlight.Texture);
+ highlight.Texture:SetAllPoints(true);
+ highlight.Texture:SetTexture(Def.Art);
+ highlight.Texture:SetBlendMode("ADD");
+ end
+
+ function TraitContainerMixin:SetConfigIDBySystemID(systemID)
+ local configID = C_Traits.GetConfigIDBySystemID(systemID);
+ self:SetConfigID(configID);
+ end
+
+ function TraitContainerMixin:SetConfigID(configID)
+ local configInfo = configID and C_Traits.GetConfigInfo(configID) or nil;
+ if not configInfo then return; end
+ self.configID = configID;
+
+ self.treeID = configInfo.treeIDs[1];
+ if not self.treeID then return; end
+
+ local treeNodes = C_Traits.GetTreeNodes(self.treeID);
+
+ local nodeIDs = {};
+ local nodePos = {};
+ local n = 0;
+
+ for _, nodeID in ipairs(treeNodes) do
+ local nodeInfo = self:GetNodeInfo(nodeID);
+ if nodeInfo then
+ nodePos[nodeID] = {nodeInfo.posX, nodeInfo.posY};
+ n = n + 1;
+ nodeIDs[n] = nodeID;
+ end
+ end
+
+ local function SortFunc(a, b)
+ -- Currently sort by posY, displays vertical layout horizontally
+ if nodePos[a][2] ~= nodePos[b][2] then
+ return nodePos[a][2] < nodePos[b][2];
+ end
+
+ if nodePos[a][1] ~= nodePos[b][1] then
+ return nodePos[a][1] < nodePos[b][1];
+ end
+
+ return a < b
+ end
+
+ table.sort(nodeIDs, SortFunc);
+
+ self:LoadNodeIDs(nodeIDs);
+ end
+
+ function TraitContainerMixin:LoadNodeIDs(nodeIDs)
+ self:Init();
+ self.nodeButtonPool:ReleaseAll();
+ local offsetX = 0;
+
+ for _, nodeID in ipairs(nodeIDs) do
+ local nodeButton = self.nodeButtonPool:Acquire();
+ nodeButton:SetPoint("LEFT", self, "LEFT", offsetX, 0);
+ nodeButton:SetNode(nodeID);
+ offsetX = offsetX + Def.ButtonSize + Def.ButtonGap;
+ end
+
+ local totalWidth = math.max(Def.ButtonSize, offsetX - Def.ButtonGap);
+ self:SetWidth(totalWidth);
+
+ self:Refresh();
+ end
+
+ function TraitContainerMixin:Refresh()
+ if self.nodeButtonPool then
+ for _, nodeButton in self.nodeButtonPool:EnumerateActive() do
+ nodeButton:Refresh();
+ end
+ self:UpdateNodeFlyoutFrame();
+ end
+ end
+
+ function TraitContainerMixin:HoverNode(nodeButton)
+ local f = self.SharedNodeHighlight;
+ f:Hide();
+ f:ClearAllPoints();
+ GameTooltip:Hide();
+
+ SharedNodeFocusSolver:SetParent(self);
+ SharedNodeFocusSolver:SetFocus(nodeButton);
+
+ if nodeButton then
+ f:SetParent(nodeButton);
+ f:SetPoint("TOPLEFT", nodeButton.Border, "TOPLEFT", 0, 0);
+ f:SetPoint("BOTTOMRIGHT", nodeButton.Border, "BOTTOMRIGHT", 0, 0);
+ if nodeButton.entryType == 1 then
+ f.Texture:SetTexCoord(0/1024, 128/1024, 384/1024, 512/1024);
+ elseif nodeButton.entryType == 2 then
+ f.Texture:SetTexCoord(128/1024, 256/1024, 384/1024, 512/1024);
+ elseif nodeButton.entryType == 0 then
+ f.Texture:SetTexCoord(256/1024, 384/1024, 384/1024, 512/1024);
+ end
+ f:SetAlpha(1);
+ f:Show();
+
+ --nodeButton:OnFocused();
+ end
+ end
+
+ function TraitContainerMixin:GetNodeInfo(nodeID)
+ if self.configID then
+ return C_Traits.GetNodeInfo(self.configID, nodeID);
+ end
+ end
+
+ function TraitContainerMixin:GetEntryInfo(entryID)
+ if self.configID then
+ return C_Traits.GetEntryInfo(self.configID, entryID);
+ end
+ end
+
+ function TraitContainerMixin:CanAffordNode(nodeID)
+ if self.configID and self.treeID and nodeID then
+ local treeCurrencyInfo = C_Traits.GetTreeCurrencyInfo(self.configID, self.treeID, false);
+ local costs = C_Traits.GetNodeCost(self.configID, nodeID);
+ local idToCost = {};
+ if costs and treeCurrencyInfo then
+ for _, cost in ipairs(costs) do
+ idToCost[cost.ID] = cost.amount;
+ end
+ else
+ return true;
+ end
+
+ local idToAmount = {};
+ for _, info in ipairs(treeCurrencyInfo) do
+ idToAmount[info.traitCurrencyID] = info.quantity;
+ end
+
+ for id, required in pairs(idToCost) do
+ if (not idToAmount[id]) or (idToAmount[id] < required) then
+ return false;
+ end
+ end
+
+ return true;
+ end
+ end
+
+ function TraitContainerMixin:TryPurchaseRank(nodeID)
+ if self.configID and nodeID then
+ CommitUtil:TryPurchaseRank(self.configID, nodeID, self.shouldAutoCommit);
+ end
+ end
+
+ function TraitContainerMixin:TryPurchaseSelectionNodeByIndex(nodeID, entryChoiceIndex)
+ if self.configID and nodeID then
+ local entryID;
+
+ local nodeInfo = C_Traits.GetNodeInfo(self.configID, nodeID);
+ if nodeInfo and nodeInfo.entryIDs then
+ entryID = entryChoiceIndex and nodeInfo.entryIDs[entryChoiceIndex] or nodeInfo.entryIDs[1];
+ end
+
+ if entryID then
+ CommitUtil:TryPurchaseSelectionNode(self.configID, nodeID, entryID, self.shouldAutoCommit);
+ end
+ end
+ end
+
+ function TraitContainerMixin:TryRefundRank(nodeID)
+ if self.configID and nodeID then
+ CommitUtil:TryRefundRank(self.configID, nodeID, self.shouldAutoCommit);
+ end
+ end
+
+ function TraitContainerMixin:SetEnableAutoCommit(shouldAutoCommit)
+ self.shouldAutoCommit = shouldAutoCommit;
+ end
+
+ function TraitContainerMixin:CloseNodeFlyout()
+ if self.NodeFlyoutFrame then
+ self.NodeFlyoutFrame:Hide();
+ end
+ end
+
+ function TraitContainerMixin:IsNodeFlyoutFocused(nodeButton)
+ if self.NodeFlyoutFrame then
+ if not self.NodeFlyoutFrame:IsVisible() then return end;
+ if self.NodeFlyoutFrame:IsMouseOver() then
+ return true;
+ end
+
+ if self.NodeFlyoutFrame.owner:IsMouseOver() then
+ return true;
+ end
+
+ if nodeButton then
+ if self.NodeFlyoutFrame.owner ~= nodeButton then
+ return false;
+ end
+ end
+
+ for _, button in ipairs(self.flyoutButtonPool:GetActiveObjects()) do
+ if button:IsMouseMotionFocus() then
+ return true;
+ end
+ end
+ end
+ end
+
+ function TraitContainerMixin:UpdateNodeFlyoutFrame()
+ if self.NodeFlyoutFrame and self.NodeFlyoutFrame:IsVisible() then
+ for _, button in ipairs(self.flyoutButtonPool:GetActiveObjects()) do
+ button:Refresh();
+ end
+ end
+ end
+
+ function TraitContainerMixin:ShowNodeFlyout(nodeButton)
+ if not nodeButton.entryIDs then return; end
+
+ local f = self.NodeFlyoutFrame;
+ if not f then
+ f = CreateFrame("Frame", nil, self);
+ self.NodeFlyoutFrame = f;
+ f:EnableMouse(true);
+ f:EnableMouseMotion(true);
+ f:SetSize(80, 80);
+ f:SetAttribute("nodeignoremime", true);
+ f:SetClampedToScreen(true);
+
+ f:SetScript("OnLeave", function()
+ if not(f:IsMouseOver() or (f.owner and f.owner:IsVisible() and f.owner:IsMouseOver())) then
+ self:CloseNodeFlyout();
+ end
+ end);
+
+ f:SetScript("OnHide", function()
+ self:CloseNodeFlyout();
+ end);
+
+ local function FlyoutButton_Create()
+ local obj = CreateFrame("Button", nil, f, "PlumberTraitNodeButtonTemplate");
+ Mixin(obj, NodeButtonMixin);
+ obj.ownerFrame = self;
+ obj.isFlyoutButton = true;
+ obj:OnLoad();
+ local shadow = obj:CreateTexture(nil, "BACKGROUND");
+ shadow:SetPoint("CENTER", obj, "CENTER", 0, -8);
+ shadow:SetSize(128, 128);
+ shadow:SetTexture(Def.Art);
+ shadow:SetTexCoord(384/1024, 480/1024, 160/1024, 256/1024);
+ return obj
+ end
+ self.flyoutButtonPool = API.CreateObjectPool(FlyoutButton_Create);
+ end
+
+ if f:IsVisible() and f.owner == nodeButton then
+ return;
+ end
+
+ f:ClearAllPoints();
+ self.flyoutButtonPool:Release();
+
+ local buttonSize = Def.ButtonSize;
+ local gapH = 4;
+ local offsetX = 0;
+ local nodeID = nodeButton.nodeID;
+
+ for i, entryID in ipairs(nodeButton.entryIDs) do
+ local button = self.flyoutButtonPool:Acquire();
+ button.entryChoiceIndex = i;
+ button.parentNodeButton = nodeButton;
+ button:SetPoint("TOPLEFT", f, "TOPLEFT", offsetX, 0);
+ button:SetNode(nodeID);
+ button:Refresh();
+ button:SetFrameStrata("DIALOG");
+ offsetX = offsetX + buttonSize + gapH;
+ end
+
+ local totalWidth = offsetX - gapH;
+ local bottomPadding = 6;
+ f:SetSize(totalWidth, buttonSize + bottomPadding);
+ f:SetPoint("BOTTOM", nodeButton, "TOP", 0, -4 -bottomPadding);
+ f:SetFrameStrata("DIALOG");
+ f.owner = nodeButton;
+
+ local duration = 0.2;
+ local toScale = 1.5;
+
+ local function FlyoutFrame_OnUpdate(_self, elapsed)
+ _self.t = _self.t + elapsed;
+ local scale;
+ if _self.t < duration then
+ scale = Easing_OutQuart(_self.t, 1.0, toScale, duration);
+ else
+ scale = toScale;
+ _self:SetScript("OnUpdate", nil);
+ end
+ _self:SetScale(scale);
+
+ local alpha = _self.t * 8;
+ if alpha > 1 then
+ alpha = 1;
+ end
+ _self:SetAlpha(alpha);
+ end
+
+ f:SetScale(1.0);
+ f:SetAlpha(0);
+ f.t = 0;
+ f:SetScript("OnUpdate", FlyoutFrame_OnUpdate);
+ f:Show();
+ end
+end
+
+
+do
+ function CommitUtil:IsCommittingInProcess()
+ return self.committingConfigID ~= nil;
+ end
+
+ function CommitUtil:StartCommitting(configID)
+ if (not configID) and (InCombatLockdown() or self:IsCommittingInProcess()) then
+ return;
+ end
+
+ self.committingConfigID = configID;
+ self.t = 0;
+ self:SetScript("OnUpdate", self.OnUpdate_Committing);
+ self:SetScript("OnEvent", self.OnEvent);
+ self:RegisterEvent("TRAIT_CONFIG_UPDATED");
+ self:RegisterEvent("CONFIG_COMMIT_FAILED");
+
+ C_Traits.CommitConfig(configID);
+ end
+
+ function CommitUtil:OnCommitFinished()
+ local configID = self.committingConfigID;
+ self.committingConfigID = nil;
+ self:SetScript("OnUpdate", nil);
+ self:UnregisterEvent("TRAIT_CONFIG_UPDATED");
+ self:UnregisterEvent("CONFIG_COMMIT_FAILED");
+ if self.commitResult == 1 then
+ --addon.CallbackRegistry:Trigger("TraitSystem.CommitSucceeded", configID);
+ elseif self.commitResult == 0 then
+ --addon.CallbackRegistry:Trigger("TraitSystem.CommitFailed", configID);
+ end
+ self.commitResult = nil;
+ end
+
+ function CommitUtil:OnUpdate_Committing(elapsed)
+ self.t = self.t + elapsed;
+ if self.t >= 1 then
+ self.t = 0;
+ self:SetScript("OnUpdate", nil);
+ self.committingConfigID = nil;
+ end
+ end
+
+ function CommitUtil:OnEvent(event, ...)
+ if event == "TRAIT_CONFIG_UPDATED" then
+ local configID = ...
+ if configID == self.committingConfigID then
+ self.commitResult = 1;
+ end
+ elseif event == "CONFIG_COMMIT_FAILED" then
+ local configID = ...
+ if configID == self.committingConfigID then
+ self.commitResult = 0;
+ end
+ end
+
+ if self.commitResult then
+ self:OnCommitFinished();
+ end
+ end
+
+ function CommitUtil:TryPurchaseRank(configID, nodeID, autoCommit)
+ if self:IsCommittingInProcess() then
+ return;
+ end
+
+ local success = C_Traits.PurchaseRank(configID, nodeID);
+ if success and autoCommit then
+ self:StartCommitting(configID);
+ end
+ return success;
+ end
+
+ function CommitUtil:TryPurchaseSelectionNode(configID, nodeID, entryID, autoCommit)
+ if self:IsCommittingInProcess() then
+ return;
+ end
+
+ local nodeInfo = C_Traits.GetNodeInfo(configID, nodeID);
+ if nodeInfo then
+ local isNewEntryIDValid;
+
+ if nodeInfo.entryIDs then
+ for _, _entryID in ipairs(nodeInfo.entryIDs) do
+ if _entryID == entryID then
+ isNewEntryIDValid = true;
+ break
+ end
+ end
+ end
+
+ if not isNewEntryIDValid then return end;
+
+ local canChangeEntry = true;
+
+ if nodeInfo.entryIDsWithCommittedRanks then
+ for _, committedEntryID in ipairs(nodeInfo.entryIDsWithCommittedRanks) do
+ if committedEntryID == entryID then
+ canChangeEntry = false;
+ break
+ end
+ end
+ end
+
+ if canChangeEntry then
+ local success = C_Traits.SetSelection(configID, nodeID, entryID);
+ if success and autoCommit then
+ self:StartCommitting(configID);
+ end
+ return success;
+ end
+ end
+ end
+
+ function CommitUtil:TryRefundRank(configID, nodeID, autoCommit)
+ if self:IsCommittingInProcess() then
+ return;
+ end
+
+ local success = C_Traits.RefundRank(configID, nodeID);
+ if success and autoCommit then
+ self:StartCommitting(configID);
+ end
+ return success;
+ end
+end
+
+
+function addon.CreateTraitContainer(parent)
+ local f = CreateFrame("Frame", nil, parent);
+ f:SetSize(Def.ButtonSize, Def.ButtonSize);
+ Mixin(f, TraitContainerMixin);
+
+ if not SharedNodeFocusSolver then
+ SharedNodeFocusSolver = API.CreateFocusSolver(f);
+ SharedNodeFocusSolver:SetDelay(0.15);
+ end
+
+ return f;
+end
+
+function API.HasAnyPurchasableTraitInSystem(systemID)
+ local configID = C_Traits.GetConfigIDBySystemID(systemID);
+ if not configID then
+ return false;
+ end
+
+ local configInfo = C_Traits.GetConfigInfo(configID);
+ local treeID = configInfo and configInfo.treeIDs and configInfo.treeIDs[1];
+ if not treeID then
+ return false;
+ end
+
+ local excludeStagedChanges = false;
+ local treeCurrencies = C_Traits.GetTreeCurrencyInfo(configID, treeID, excludeStagedChanges);
+ if #treeCurrencies <= 0 then
+ return false;
+ end
+
+ local unspentCurrency = treeCurrencies[1] and treeCurrencies[1].quantity or 0;
+ if unspentCurrency <= 0 then
+ return false;
+ end
+
+ local nodeIDs = C_Traits.GetTreeNodes(treeID);
+ for _nodeIndex, nodeID in ipairs(nodeIDs) do
+ local nodeCosts = C_Traits.GetNodeCost(configID, nodeID);
+ -- Assume only 1 type of currency is needed
+ local canAffordNode = (not nodeCosts) or (#nodeCosts == 0) or (unspentCurrency >= nodeCosts[1].amount);
+ if canAffordNode then
+ local nodeInfo = C_Traits.GetNodeInfo(configID, nodeID);
+ for _entryIndex, entryID in ipairs(nodeInfo.entryIDs) do
+ if C_Traits.CanPurchaseRank(configID, nodeID, entryID) then
+ return true;
+ end
+ end
+ end
+ end
+
+ return false;
+end
diff --git a/Plumber.toc b/Plumber.toc
index 6132ab4..d4090ff 100644
--- a/Plumber.toc
+++ b/Plumber.toc
@@ -76,7 +76,6 @@ Modules\Macros.lua
Modules\ProfessionsBook.lua
Modules\ExpansionLandingPage\ExpansionLandingPage_Retail.xml
Modules\BlizzFix_ModelLoading.lua
-Modules\Timerunning\LegionRemix.xml
Modules\HolidayDungeon.lua
Modules\QueueStatus.lua
Modules\RaidCheck\RaidCheck.xml
@@ -104,3 +103,4 @@ Modules\PreyQuestSuperTrack.lua
#Modules\MinimapMouseover.lua
#Modules\ObjectiveTrackerModule.xml
#Modules\BlizzFix_EndCaps.lua
+#Modules\Timerunning\LegionRemix.xml