local _, addon = ... if not addon.IsGame_11_0_2 then return end; local L = addon.L; local API = addon.API; local UIFrameFade = API.UIFrameFade; local TimeLeftTextToSeconds = API.TimeLeftTextToSeconds; local IsInDelves = API.IsInDelves; local GetDelvesForMap = C_AreaPoiInfo.GetDelvesForMap; --Fail to obtain Bountiful Delves (Bountiful Delves use AreaPOIPinTemplate, Other Delves use DelveEntrancePinTemplate) local GetAreaPOIInfo = C_AreaPoiInfo.GetAreaPOIInfo; local C_UIWidgetManager = C_UIWidgetManager; local GetMajorFactionRenownInfo = C_MajorFactions.GetMajorFactionRenownInfo; local GetStepInfo = C_Scenario.GetStepInfo; local UIParent = UIParent; local DELVES_SEASON_FACTION; --2644 local DelvePOI = { --{mapID, normalPoi, bountifulPoi} --Isle of Dorn {2248, 7863, 7787}, --Earthcrawl Mines {2248, 7864, 7779}, --Fungal Folly {2248, 7865, 7781}, --Kriegval's Rest --Ringing Deeps {2214, 7867, 7788}, --The Dread Pit {2214, 7866, 7782}, --The Waterworks --Hallowfall {2215, 7869, 7780}, --Mycomancer Cavern {2215, 7868, 7785}, --Nightfall Sanctum {2215, 7871, 7789}, --Skittering Breach {2215, 7870, 7783}, --The Sinkhole --Azj-Kahet {2255, 7873, 7784}, --Tak-Rethan Abyss {2255, 7874, 7790}, --The Spiral Weave {2255, 7872, 7786}, --The Underkeep --{0, 7875, nil}, --Zekvir's Lair (Mystery 13th Delve) }; local DelveMaps = { 2248, 2214, 2215, 2255, }; local POIxDelveIndex = {}; do --Format Data local poiID; for delveIndex, data in ipairs(DelvePOI) do poiID = data[2]; if poiID then POIxDelveIndex[poiID] = delveIndex; end poiID = data[3]; if poiID then POIxDelveIndex[poiID] = delveIndex; end end end local EL = CreateFrame("Frame"); EL.handlers = {}; EL:SetScript("OnEvent", function(self, event, ...) if self.handlers[event] then self.handlers[event](...) end end); function EL:AddHandler(event, func) self.handlers[event] = func; end function EL:OnUpdate(elapsed) self.t = self.t + elapsed; if self.t > 0 then self.t = nil; self:SetScript("OnUpdate", nil); for _, queuedFunc in ipairs(self.queuedFuncs) do queuedFunc(); end self.queuedFuncs = nil; end end function EL:ProcessOnNextCycle(func) if not self.queuedFuncs then self.queuedFuncs = {}; end self.t = -0.2; self:SetScript("OnUpdate", self.OnUpdate); for _, queuedFunc in ipairs(self.queuedFuncs) do if queuedFunc == func then return end end table.insert(self.queuedFuncs, func); end local function Dev_GetDelveMapInfo() --Bountfiul Delves have different poiID different from their regular modes --C_AreaPoiInfo.GetAreaPOISecondsLeft returns nil --/dump C_AreaPoiInfo.GetAreaPOIInfo(2215, 7783) for delveIndex, data in ipairs(DelvePOI) do data.isBountiful = true; end local areaPoiIDs, poiInfo; local n = 0; local poiData = {}; local delveIndex; for _, mapID in ipairs(DelveMaps) do areaPoiIDs = GetDelvesForMap(mapID); for _, poiID in ipairs(areaPoiIDs) do delveIndex = POIxDelveIndex[poiID]; if delveIndex then DelvePOI[delveIndex].isBountiful = nil; end end end local tooltipWidgetSet; local mapID, poiID; for delveIndex, data in ipairs(DelvePOI) do mapID = data[1]; if data.isBountiful then poiID = data[3]; else poiID = data[2]; end poiInfo = GetAreaPOIInfo(mapID, poiID) if poiInfo then if not data.name then data.name = poiInfo.name; end if data.isBountiful then print(delveIndex, "|cnGREEN_FONT_COLOR:"..data.name.."|r"); if not tooltipWidgetSet then tooltipWidgetSet = poiInfo.tooltipWidgetSet; end local x, y = poiInfo.position:GetXY(); API.ConvertMapPositionToContinentPosition(mapID, x, y, poiID); else print(delveIndex, data.name); end end end if tooltipWidgetSet then local widgets = C_UIWidgetManager.GetAllWidgetsBySetID(tooltipWidgetSet); local widgetID = widgets and widgets[1] and widgets[1].widgetID; if widgetID then local widgetInfo = C_UIWidgetManager.GetTextWithStateWidgetVisualizationInfo(widgetID); if widgetInfo then local seconds = TimeLeftTextToSeconds(widgetInfo.text); print(seconds); print(API.SecondsToTime(seconds)) end end end end local function GetEnemyGroupCount() local widgetSetID = select(12, GetStepInfo()); if not widgetSetID then return end; local widgets = C_UIWidgetManager.GetAllWidgetsBySetID(widgetSetID); if not widgets then return end; local TYPE_ID = Enum.UIWidgetVisualizationType.ScenarioHeaderDelves or 29; local widgetID; for _, widgetInfo in ipairs(widgets) do if widgetInfo.widgetType == TYPE_ID then widgetID = widgetInfo.widgetID; end end if not widgetID then return end; local SPELL_ID = 456037; --Zekvir's Influence local isSpellFound; local widgetInfo = C_UIWidgetManager.GetScenarioHeaderDelvesWidgetVisualizationInfo(widgetID); if widgetInfo and widgetInfo.shownState == 1 and widgetInfo.spells then for _, spellInfo in ipairs(widgetInfo.spells) do if spellInfo.spellID == SPELL_ID then if spellInfo.shownState == 1 then isSpellFound = true; end break end end end if isSpellFound then local isPet = false; local showSubtext = true; local tooltipData = C_TooltipInfo.GetSpellByID(SPELL_ID, isPet, showSubtext); if tooltipData and tooltipData.lines then local numLines = #tooltipData.lines; local lineText = tooltipData.lines[numLines].leftText; print(lineText); --Zekvir, the Hand of the Harbinger...Enemy groups remaining: 3/3 local current, total = string.match(lineText, "(%d+)%s*/%s*(%d+)"); print(current, total) end end end local BonusObjectiveTrackerMixin = {}; do --Show Enemy Group Count bellow affix spell on the ScenarioHeaderDelves local GetCurrencyInfo = C_CurrencyInfo.GetCurrencyInfo; local CURRENCY_AFFIX_ACTIVE = 3103; local CURRENCY_AFFIX_MAXIMUM = 3104; function BonusObjectiveTrackerMixin:OnEvent(event, ...) --Fire CURRENCY_DISPLAY_UPDATE 1/2 times After killing one group, 3103 changed to 0 then the actual number if event == "CURRENCY_DISPLAY_UPDATE" then local currencyID = ... if currencyID and (currencyID == CURRENCY_AFFIX_ACTIVE or currencyID == CURRENCY_AFFIX_MAXIMUM) then self:RequestUpdate(); end end end function BonusObjectiveTrackerMixin:Remove() if self.isActive then self.isActive = false; self:Hide(); self:ClearAllPoints(); self:UnregisterEvent("CURRENCY_DISPLAY_UPDATE"); self:SetScript("OnUpdate", nil); self:SetParent(UIParent); self:SetPoint("TOP", UIParent, "BOTTOM", 0, -8); end end function BonusObjectiveTrackerMixin:OnUpdate(elapsed) self.t = self.t + elapsed; if self.t >= 0.5 then self.t = nil; self:SetScript("OnUpdate", nil); self:Update(); end end function BonusObjectiveTrackerMixin:RequestUpdate(newDelve) self.t = 0; self:SetScript("OnUpdate", self.OnUpdate); self:Show(); if newDelve then self.newDelve = true; end end function BonusObjectiveTrackerMixin:Reposition() local widgetSetID = select(12, GetStepInfo()); if (not widgetSetID) or widgetSetID == 0 then return end; local widgets = C_UIWidgetManager.GetAllWidgetsBySetID(widgetSetID); if not widgets then return end; local TYPE_ID = Enum.UIWidgetVisualizationType.ScenarioHeaderDelves or 29; local widgetID; for _, widgetInfo in ipairs(widgets) do if widgetInfo.widgetType == TYPE_ID then widgetID = widgetInfo.widgetID; end end if not widgetID then return end; local SPELL_ID = 456037; --Zekvir's Influence local spellIndex; local widgetInfo = C_UIWidgetManager.GetScenarioHeaderDelvesWidgetVisualizationInfo(widgetID); if widgetInfo and widgetInfo.shownState == 1 and widgetInfo.spells then for i, spellInfo in ipairs(widgetInfo.spells) do if spellInfo.spellID == SPELL_ID then if spellInfo.shownState == 1 then spellIndex = i; end break end end end self:ClearAllPoints(); self:SetParent(UIParent); self:SetPoint("TOP", UIParent, "BOTTOM", 0, -8); local parentObject; if spellIndex then for widgetContainer in pairs(UIWidgetManager.registeredWidgetContainers) do if widgetContainer.widgetSetID == widgetSetID then if widgetContainer and widgetContainer.widgetFrames and widgetContainer.widgetFrames[widgetID] then local p = widgetContainer.widgetFrames[widgetID]; if p.SpellContainer and p.SpellContainer.GetLayoutChildren then local frames = p.SpellContainer:GetLayoutChildren(); parentObject = frames[spellIndex]; end end break end end end if parentObject then self:SetParent(parentObject); self:SetPoint("TOP", parentObject, "BOTTOM", 0, 0); end end function BonusObjectiveTrackerMixin:Update() local info; local current, max; info = GetCurrencyInfo(CURRENCY_AFFIX_ACTIVE); if info then current = info.quantity; end info = GetCurrencyInfo(CURRENCY_AFFIX_MAXIMUM); if info then max = info.quantity; end if current and max and max > 0 then if current <= 0 then self.Text:SetText("|TInterface\\AddOns\\Plumber\\Art\\Button\\Checkmark-Green:16|t"); else self.Text:SetText(current); end self.isActive = true; self:RegisterEvent("CURRENCY_DISPLAY_UPDATE"); if self.newDelve then self.newDelve = nil; self:Reposition(); end else self:Remove(); end end end local SeasonTracker = {}; do --Seasonal Journey Progress --Currently can't track it on the ReputationBar by C_Reputation.SetWatchedFactionByID (C_Reputation.IsMajorFaction returns true but no C_MajorFactions.GetMajorFactionData) local FADEOUT_DELAY = 4; local MAPID_DORNOGAL = 2339; --Only listen UPDATE_FACTION in Delves and Dornogal (Weekly Quest Turn-in) local ProgressBar; local BonusObjectiveTracker; local ZoneTriggerModule; local function ProgressBar_Init() if ProgressBar then return end; ProgressBar = addon.CreateLevelProgressBar(UIParent); ProgressBar:SetPoint("CENTER", UIParent, "TOP", 0, -128); ProgressBar:SetLabel("Seasonal Journey"); ProgressBar:SetLevel(5); ProgressBar:SetFrameStrata("HIGH"); ProgressBar:SetFrameLevel(990); ProgressBar:Hide(); ProgressBar:SetAlpha(0); ProgressBar:SetUseAnimation(true); ProgressBar:SetAutoFadeOut(true); ProgressBar.FadeUpdateFrame = CreateFrame("Frame"); ProgressBar.FadeUpdateFrame:Hide(); ProgressBar.FadeUpdateFrame:SetScript("OnUpdate", function(f, elapsed) f.t = f.t + elapsed; if f.t >= FADEOUT_DELAY then f.t = 0; f:Hide(); UIFrameFade(ProgressBar, 0.25, 0); end end); ProgressBar.onEnterFunc = function() ProgressBar:FadeIn(); end ProgressBar.onLeaveFunc = function() ProgressBar:StartFadeOutCountdown(2); end function ProgressBar:FadeIn() ProgressBar.FadeUpdateFrame:Hide(); UIFrameFade(self, 0.25, 1); end function ProgressBar:StartFadeOutCountdown(delay) if self:IsAnimating() then return end ProgressBar.FadeUpdateFrame:Show(); ProgressBar.FadeUpdateFrame.t = delay or 0; end function ProgressBar:ShowProgress() self:FadeIn(); local level, earned, threshold = SeasonTracker:GetProgress(); self:SetLevel(level); self:SetMaxValue(threshold); self:SetValue(earned); end local level, earned, threshold = SeasonTracker:GetProgress(); ProgressBar:SetLevel(level, threshold <= 0); ProgressBar:SetMaxValue(threshold); ProgressBar:SetValue(earned); PB = ProgressBar; --Debug function ProgressBar:Test() local level, earned, threshold = 2, 3300, 4200; local deltaEarned, levelUp, reachMaxLevel = 290, true, false; self:FadeIn(); self:SetMaxValue(threshold); self:SetValue(earned, levelUp); self:AnimateDeltaValue(deltaEarned); self:SetLevel(level, reachMaxLevel); end end local function BonusObjectiveTracker_Init() if BonusObjectiveTracker then return end; local f = CreateFrame("Frame"); BonusObjectiveTracker = f; f:SetSize(30, 24); local Text = f:CreateFontString(nil, "OVERLAY", "GameFontHighlight"); f.Text = Text; Text:SetJustifyH("CENTER"); Text:SetPoint("CENTER", f, "CENTER", 0, 0); local Bg = f:CreateTexture(nil, "BORDER"); Bg:SetAllPoints(true); Bg:SetTexture("Interface/AddOns/Plumber/Art/Delves/Delves-Scenario"); Bg:SetTexCoord(0, 60/256, 0, 48/256); API.Mixin(f, BonusObjectiveTrackerMixin); f:SetScript("OnEvent", f.OnEvent); end local function BonusObjectiveTracker_Hide() if BonusObjectiveTracker then BonusObjectiveTracker:Remove(); end end function SeasonTracker:GetProgress() if not DELVES_SEASON_FACTION then DELVES_SEASON_FACTION = C_DelvesUI and C_DelvesUI.GetDelvesFactionForSeason and C_DelvesUI.GetDelvesFactionForSeason() or 0; end local renownInfo = GetMajorFactionRenownInfo(DELVES_SEASON_FACTION); if not renownInfo then return 0, 0, 1 end; local level = renownInfo.renownLevel or 1; local earned = renownInfo.renownReputationEarned or 0; local threshold = renownInfo.renownLevelThreshold or 1; return level, earned, threshold end function SeasonTracker:SnapShotSeasonalProgress() --Assume the amount of renown required to gain 1 level is a constant (4200) local level, earned, threshold = self:GetProgress(); local deltaLevel, deltaEarned; if self.renownLevel then deltaLevel = level - self.renownLevel; else deltaLevel = 0; end local levelUp = deltaLevel > 0; if self.renownReputationEarned and self.renownLevelThreshold then if levelUp then deltaEarned = earned + self.renownLevelThreshold - self.renownReputationEarned; else deltaEarned = earned - self.renownReputationEarned; end end self.renownLevel = level; self.renownReputationEarned = earned; self.renownLevelThreshold = threshold; --print(level, earned, threshold); local reachMaxLevel = (not threshold) or threshold <= 0; if deltaEarned and deltaEarned > 0 then return true, level, earned, threshold, deltaEarned, levelUp, reachMaxLevel end end local function UpdateInDelveStatus() --Seasonal Progress changes after looting a chest in the end --To reduce usage, we start listening UPDATE_FACTION after the scenario is completed (the event fires after killing the final boss) (Companion level is also a faction) --If player resets UI after scenario completion, we won't show the progress bar --C_DelvesUI.HasActiveDelve doesn't change immediately after PLAYER_MAP_CHANGED if IsInDelves() then EL:RegisterEvent("SCENARIO_COMPLETED"); EL:RegisterEvent("PLAYER_MAP_CHANGED"); BonusObjectiveTracker_Init(); BonusObjectiveTracker:RequestUpdate(true); --print("IN Delve"); return true else EL:UnregisterEvent("SCENARIO_COMPLETED"); EL:UnregisterEvent("PLAYER_MAP_CHANGED"); EL:UnregisterEvent("UPDATE_FACTION"); BonusObjectiveTracker_Hide(); --print("NOT in Delve"); return false end end EL:AddHandler("PLAYER_MAP_CHANGED", function() EL:ProcessOnNextCycle(UpdateInDelveStatus); end); local function OnDelveEntered() if UpdateInDelveStatus() then ProgressBar_Init(); end end EL:AddHandler("WALK_IN_DATA_UPDATE", function() EL:ProcessOnNextCycle(OnDelveEntered); end); local function OnScenarioCompleted() EL:RegisterEvent("UPDATE_FACTION"); end EL:AddHandler("SCENARIO_COMPLETED", OnScenarioCompleted); local function OnFactionUpdated() EL:RegisterEvent("UPDATE_FACTION"); local anyChange, level, earned, threshold, deltaEarned, levelUp, reachMaxLevel = SeasonTracker:SnapShotSeasonalProgress(); if anyChange then ProgressBar:FadeIn(); ProgressBar:SetValue(earned, levelUp); ProgressBar:AnimateDeltaValue(deltaEarned); ProgressBar:SetLevel(level, reachMaxLevel); ProgressBar:SetMaxValue(threshold); ProgressBar:StartFadeOutCountdown(0); end end EL:AddHandler("UPDATE_FACTION", function() EL:ProcessOnNextCycle(OnFactionUpdated); end); local function ZoneTriggerModule_Enable(state) if state then if not ZoneTriggerModule then local module = API.CreateZoneTriggeredModule("dornogal"); ZoneTriggerModule = module; module:SetValidZones(MAPID_DORNOGAL); local function OnEnterZoneCallback() SeasonTracker:SnapShotSeasonalProgress(); ProgressBar_Init(); EL:RegisterEvent("UPDATE_FACTION"); end local function OnLeaveZoneCallback() UpdateInDelveStatus(); end module:SetEnterZoneCallback(OnEnterZoneCallback); module:SetLeaveZoneCallback(OnLeaveZoneCallback); end ZoneTriggerModule:SetEnabled(true); ZoneTriggerModule:Update(); else if ZoneTriggerModule then ZoneTriggerModule:SetEnabled(false); end end end function SeasonTracker:SetEnabled(state) if state then EL:RegisterEvent("WALK_IN_DATA_UPDATE"); --Always registered self:SnapShotSeasonalProgress(); OnDelveEntered(); else EL:UnregisterEvent("WALK_IN_DATA_UPDATE"); EL:UnregisterEvent("PLAYER_MAP_CHANGED"); EL:UnregisterEvent("SCENARIO_COMPLETED"); EL:UnregisterEvent("UPDATE_FACTION"); end ZoneTriggerModule_Enable(state); end C_Timer.After(2, function() --debug SeasonTracker:SetEnabled(true); end); end