local _, addon = ... local API = addon.API; local MainFrame = addon.DialogueUI; local IsInteractingWithDialogNPC = API.IsInteractingWithDialogNPC; local CancelClosingGossipInteraction = API.CancelClosingGossipInteraction; local QuestIsFromAreaTrigger = API.QuestIsFromAreaTrigger; local GossipDataProvider = addon.GossipDataProvider; local QuestGetAutoAccept = API.QuestGetAutoAccept; local ShouldMuteQuestDetail = API.ShouldMuteQuestDetail; local CloseQuest = CloseQuest; local InCombatLockdown = InCombatLockdown; local IsInInstance = IsInInstance; local GetQuestID = GetQuestID; local EVENT_PROCESS_DELAY = 0.017; --Affected by CameraMovement local MAINTAIN_CAMERA_POSITION = false; local USE_AUTO_QUEST_POPUP = true; local DISABLE_DUI_IN_INSTANCE = false; local HANDLE_EVENT_EXTERNALLY = false; --If true, Events will be handled by Skimmers local EL = CreateFrame("Frame"); local Muter = {}; local function GetCustomGossipHandler() end local GossipEvents = { "GOSSIP_SHOW", "GOSSIP_CLOSED", "CONFIRM_TALENT_WIPE", --Classic }; local QuestEvents = { "QUEST_PROGRESS", "QUEST_DETAIL", "QUEST_FINISHED", --Close QuestFrame "QUEST_GREETING", --Offer several quests "QUEST_COMPLETE", --Talk to turn in quest }; local MapEvents = { PLAYER_ENTERING_WORLD = true, ZONE_CHANGED_NEW_AREA = true, }; local CloseDialogEvents = {}; if not addon.IsToCVersionEqualOrNewerThan(50000) then local ClassicEvents = { "CONFIRM_TALENT_WIPE", "CONFIRM_TALENT_WIPE", }; for _, event in ipairs(ClassicEvents) do table.insert(GossipEvents, event); CloseDialogEvents[event] = true; end end local function ShouldMuteQuest() local questID = GetQuestID(); return ShouldMuteQuestDetail(questID) end function EL:OnManualEvent(event, ...) self:SetScript("OnUpdate", nil); if event == "QUEST_FINISHED" or event == "QUEST_FINISHED_FORCED" then --For the issue where the quest window fails to close: --Sometimes QUEST_FINISHED fires but IsInteractingWithNpcOfType still thinks we are interacting with QuestGiver --/dump C_PlayerInteractionManager.IsInteractingWithNpcOfType(Enum.PlayerInteractionType.QuestGiver) --print(event, "IS INTERACTING", IsInteractingWithDialogNPC(), GetTimePreciseSec()) --debug if (event == "QUEST_FINISHED_FORCED") or (not IsInteractingWithDialogNPC()) then self.timeSinceQuestFinish = nil; MainFrame:HideUI(); end elseif event == "GOSSIP_SHOW" then MainFrame:ShowUI(event); elseif event == "GOSSIP_CLOSED" then self:OnGossipClosed(...); end end function EL:OnGossipClosed(interactionIsContinuing) if self.customFrame then local f = self.customFrame; self.customFrame = nil; if not InCombatLockdown() then HideUIPanel(f); end return end if not IsInteractingWithDialogNPC() then if not MainFrame:IsGossipCloseConsumed() then --MainFrame:SetInteractionIsContinuing(interactionIsContinuing); self.timeSinceQuestFinish = nil; MainFrame:HideUI(); end GossipDataProvider:OnInteractStopped(); end end function EL:NegateLastEvent(event) if event == self.lastEvent then self.lastEvent = nil; end end function EL:OnEvent(event, ...) if HANDLE_EVENT_EXTERNALLY then return end if event == "GOSSIP_SHOW" then self.lastEvent = event; local handler = GetCustomGossipHandler(...); if handler then self.customFrame = handler(...); else if self:ThrottleGossipEvent() then GossipDataProvider:OnInteractWithNPC(); MainFrame:ShowUI(event); --Depends on the options, we may select the non-gossip one directly without openning the UI CancelClosingGossipInteraction(); end end self:NegateLastEvent(event); elseif event == "GOSSIP_CLOSED" then self.lastEvent = event; self:ProcessEventNextUpdate(0.1); --self:OnGossipClosed(...); elseif event == "QUEST_FINISHED" then --When the quest giver has more than one quest --sometimes there is a delay between QUEST_FINISHED and GOSSIP_SHOW (presumably depends on various of factors including latency) --the game determinates interaction then re-engage, messing up ActionCam and gossip info --our workaround is setting s delay to this event --print(event, GetTimePreciseSec(), IsInteractingWithDialogNPC()); local delay = MainFrame:GetQuestFinishedDelay(); self.timeSinceQuestFinish = -delay; if self.lastEvent ~= "QUEST_FINISHED_FORCED" then self.lastEvent = event; self:ProcessEventNextUpdate(delay); end elseif event == "QUEST_DETAIL" then --Can fire multiple times in rare occasions, possibly due to cross-character progress if ShouldMuteQuest() then if API.IsQuestAutoAccepted() then API.AcknowledgeAutoAcceptQuest(); end CloseQuest(); return end self.lastEvent = event; local questStartItemID = ... if USE_AUTO_QUEST_POPUP and questStartItemID and questStartItemID ~= 0 then addon.WidgetManager:AddAutoQuestPopUp(questStartItemID); CloseQuest(); return end if USE_AUTO_QUEST_POPUP and QuestIsFromAreaTrigger() and (QuestGetAutoAccept() or InCombatLockdown()) then --"QuestIsFromAreaTrigger" and "QuestGetAutoAccept" Doesn't work in Classic --Some quests that triggered upon login aren't "QuestIsFromAreaTrigger" addon.WidgetManager:AddAutoQuestPopUp(); CloseQuest(); else MainFrame:ShowUI(event, questStartItemID); end elseif event == "QUEST_PROGRESS" or event == "QUEST_COMPLETE" or event == "QUEST_GREETING" then --Sometimes QUEST_FINISHED fires before QUEST_COMPLETE self.lastEvent = event; MainFrame:ShowUI(event); elseif CloseDialogEvents[event] then self.lastEvent = event; self.timeSinceQuestFinish = nil; MainFrame:HideUI(); elseif MapEvents[event] then if DISABLE_DUI_IN_INSTANCE then Muter:UpdateForInstance(); end end --print(event, GetTimePreciseSec(), ...); --debug end function EL:ListenEvents(state) local method; if state then method = "RegisterEvent"; self:SetScript("OnEvent", self.OnEvent); else method= "UnregisterEvent"; if DISABLE_DUI_IN_INSTANCE then self:SetScript("OnEvent", self.OnEvent); else self:SetScript("OnEvent", nil); end end for _, event in ipairs(GossipEvents) do self[method](self, event); end for _, event in ipairs(QuestEvents) do self[method](self, event); end --self[method](self, "PLAYER_INTERACTION_MANAGER_FRAME_SHOW"); --debug --self[method](self, "PLAYER_INTERACTION_MANAGER_FRAME_HIDE"); end function EL:OnUpdate(elapsed) if self.timeSinceQuestFinish then self.timeSinceQuestFinish = self.timeSinceQuestFinish + elapsed; if self.timeSinceQuestFinish > EVENT_PROCESS_DELAY then self.timeSinceQuestFinish = nil; if self.lastEvent == "QUEST_FINISHED" or self.lastEvent == "QUEST_FINISHED_FORCED" then if not IsInteractingWithDialogNPC() then --print("COUNTER STOP", UnitExists("npc")) self.processEvent = nil; self.lastEvent = nil; MainFrame:HideUI(); end end end end if self.processEvent then self.t = self.t + elapsed; if self.t > EVENT_PROCESS_DELAY then self.t = 0; self.processEvent = nil; if self.lastEvent then --print("LAST EVENT", self.lastEvent) self:OnManualEvent(self.lastEvent); self.lastEvent = nil; end end end if self.pauseGossip then self.pauseGossip = self.pauseGossip + elapsed; if self.pauseGossip >= 0 then self.pauseGossip = nil; end end if not (self.processEvent or self.pauseGossip) then self:SetScript("OnUpdate", nil); end end function EL:ThrottleGossipEvent() if not self.pauseGossip then self.pauseGossip = 0.016; self:SetScript("OnUpdate", self.OnUpdate); return true end return false end function EL:ProcessEventNextUpdate(customDelay) customDelay = customDelay or 0; self.t = -customDelay; self.processEvent = true; self:SetScript("OnUpdate", self.OnUpdate); end do local DEFAULT_CAMERA_MODE = 1; local function OnCameraModeChanged(_, mode) if mode == 0 then --0: No Zoom EVENT_PROCESS_DELAY = 0.017; elseif mode == 1 then --1: Zoom to NPC if MAINTAIN_CAMERA_POSITION then EVENT_PROCESS_DELAY = 0.5; else EVENT_PROCESS_DELAY = 0.017; end elseif mode == 2 then --2: Shift camear horizontally EVENT_PROCESS_DELAY = 0.5; end DEFAULT_CAMERA_MODE = mode; end addon.CallbackRegistry:Register("Camera.ModeChanged", OnCameraModeChanged, EL); local function Settings_CameraMovement1MaintainPosition(dbValue) MAINTAIN_CAMERA_POSITION = dbValue == true; if DEFAULT_CAMERA_MODE then OnCameraModeChanged(nil, DEFAULT_CAMERA_MODE); end end addon.CallbackRegistry:Register("SettingChanged.CameraMovement1MaintainPosition", Settings_CameraMovement1MaintainPosition); local function ManualTriggerQuestFinished(isAutoComplete) --print("TRIGGER FINISH", GetTimePreciseSec()) --debug if EL.lastEvent ~= "QUEST_FINISHED_FORCED" then EL.lastEvent = "QUEST_FINISHED_FORCED"; EL:ProcessEventNextUpdate(1.5); --Force trigger QUEST_FINISHED event to close the UI. We use extended delay (1s) due to unavailable server latency end end addon.CallbackRegistry:Register("TriggerQuestFinished", ManualTriggerQuestFinished); local function Settings_AutoQuestPopup(dbValue) USE_AUTO_QUEST_POPUP = dbValue ~= false; end addon.CallbackRegistry:Register("SettingChanged.AutoQuestPopup", Settings_AutoQuestPopup); end do --Unlisten events from default UI --CustomGossipFrameManager: --We need to mute this so HideUI doesn't trigger CloseGossip --It handle NPE (Be A Guide) and Torghast Floor Selection Muter.questEvents = { QUEST_GREETING = true, QUEST_DETAIL = true, QUEST_PROGRESS = true, QUEST_COMPLETE = true, QUEST_FINISHED = true, QUEST_ITEM_UPDATE = true, QUEST_LOG_UPDATE = true, UNIT_PORTRAIT_UPDATE = true, PORTRAITS_UPDATED = true, }; if addon.IsToCVersionEqualOrNewerThan(50000) then Muter.questEvents.LEARNED_SPELL_IN_SKILL_LINE = true; else Muter.questEvents.LEARNED_SPELL_IN_TAB = true; --Classic end local function SetUseDialogueUI(state) if state == nil then state = true end; if state then if Muter.muted then return end; Muter.muted = true; EL:ListenEvents(true); if CustomGossipFrameManager then --CustomGossipFrameBase.lua (Retail) CustomGossipFrameManager:UnregisterEvent("GOSSIP_SHOW"); CustomGossipFrameManager:UnregisterEvent("GOSSIP_CLOSED"); end if GossipFrame then --Classic GossipFrame:UnregisterEvent("GOSSIP_SHOW"); GossipFrame:UnregisterEvent("GOSSIP_CLOSED"); GossipFrame:UnregisterEvent("QUEST_LOG_UPDATE"); end local hideQuestFrame = true; --false when we do debug if hideQuestFrame then QuestFrame:UnregisterAllEvents(); else QuestFrame:SetParent(nil); QuestFrame:SetScale(2/3); end elseif Muter.muted then Muter.muted = false; EL:ListenEvents(false); if CustomGossipFrameManager then --CustomGossipFrameBase.lua (Retail) CustomGossipFrameManager:RegisterEvent("GOSSIP_SHOW"); CustomGossipFrameManager:RegisterEvent("GOSSIP_CLOSED"); end if GossipFrame then --Classic GossipFrame:RegisterEvent("GOSSIP_SHOW"); GossipFrame:RegisterEvent("GOSSIP_CLOSED"); GossipFrame:RegisterEvent("QUEST_LOG_UPDATE"); end local hideQuestFrame = true; --false when we do debug if hideQuestFrame then local qf = QuestFrame; for event, valid in pairs(Muter.questEvents) do if valid then qf:RegisterEvent(event); end end else QuestFrame:SetParent(UIParent); QuestFrame:SetScale(1); end end addon.EnableBookUI(state); end addon.SetUseDialogueUI = SetUseDialogueUI; SetUseDialogueUI(true); function Muter:UpdateForInstance() if IsInInstance() then SetUseDialogueUI(false); else SetUseDialogueUI(true); end end local function Settings_DisableDUIInInstance(dbValue, userInput) DISABLE_DUI_IN_INSTANCE = dbValue == true; if DISABLE_DUI_IN_INSTANCE then for event in pairs(MapEvents) do EL:RegisterEvent(event); end Muter:UpdateForInstance(); if userInput and IsInInstance() and MainFrame:IsShown() then MainFrame:Hide(); end else for event in pairs(MapEvents) do EL:UnregisterEvent(event); end SetUseDialogueUI(true); end end addon.CallbackRegistry:Register("SettingChanged.DisableDUIInInstance", Settings_DisableDUIInInstance); end do --See Blizzard_UIPanels_Game/CustomGossipFrameBase.lua local function HandleNPEGuideGossipShow(textureKit) C_AddOns.LoadAddOn("Blizzard_NewPlayerExperienceGuide"); ShowUIPanel(GuideFrame); return GuideFrame end local function HandleTorghastLevelPickerGossipShow(textureKit) C_AddOns.LoadAddOn("Blizzard_TorghastLevelPicker"); TorghastLevelPickerFrame:TryShow(textureKit) return TorghastLevelPickerFrame end local function HandleDelvesDifficultyPickerGossipShow(textureKit) --TWW C_AddOns.LoadAddOn("Blizzard_DelvesDifficultyPicker"); DelvesDifficultyPickerFrame:TryShow(textureKit); return DelvesDifficultyPickerFrame end local Handlers = {}; function EL:RegisterHandler(textureKit, func) Handlers[textureKit] = func; end function EL:RegisterCustomGossipFrames() self:RegisterHandler("npe-guide", HandleNPEGuideGossipShow); self:RegisterHandler("skoldushall", HandleTorghastLevelPickerGossipShow); self:RegisterHandler("mortregar", HandleTorghastLevelPickerGossipShow); self:RegisterHandler("coldheartinterstitia", HandleTorghastLevelPickerGossipShow); self:RegisterHandler("fracturechambers", HandleTorghastLevelPickerGossipShow); self:RegisterHandler("soulforges", HandleTorghastLevelPickerGossipShow); self:RegisterHandler("theupperreaches", HandleTorghastLevelPickerGossipShow); self:RegisterHandler("twistingcorridors", HandleTorghastLevelPickerGossipShow); self:RegisterHandler("delves-difficulty-picker", HandleDelvesDifficultyPickerGossipShow); --For some reason this textureKit is sometimes nil, causing issue end EL:RegisterCustomGossipFrames(); function GetCustomGossipHandler(textureKit) return textureKit and Handlers[textureKit] end addon.GetCustomGossipHandler = GetCustomGossipHandler; end do --DEBUG Skimmer local function SetHandleEventExternally(state) HANDLE_EVENT_EXTERNALLY = state == true; end addon.SetHandleEventExternally = SetHandleEventExternally; end