Nenue@0: -- WorldPlan.lua Nenue@0: -- Created: 8/16/2016 8:19 AM Nenue@0: -- %file-revision% Nenue@0: Nenue@30: local addonName, db = ... Nenue@0: Nenue@29: local ICON_UNKNOWN = "Interface\\ICONS\\inv_misc_questionmark" Nenue@29: local ICON_MONEY = "Interface\\Buttons\\UI-GroupLoot-Coin-Up" Nenue@29: Nenue@29: local POI_BORDER_MASK = "Interface\\Minimap\\UI-Minimap-Background" Nenue@29: local POI_BORDER_FILL = "Interface\\BUTTONS\\YELLOWORANGE64" Nenue@29: local POI_BORDER_BLUE = "Interface\\BUTTONS\\GRADBLUE" Nenue@29: local POI_BORDER_RED = "Interface\\BUTTONS\\RedGrad64" Nenue@29: local POI_BORDER_YELLOW = "Interface\\BUTTONS\\YELLOWORANGE64" Nenue@29: local POI_BORDER_GREEN = "Interface\\BUTTONS\\GREENGRAD64" Nenue@29: Nenue@29: WorldPlanCore = { Nenue@29: defaults = {}, Nenue@30: modules = {}, Nenue@30: } Nenue@30: WorldPlanQuestsMixin = { Nenue@30: QuestsByZone = {}, Nenue@30: QuestsByID = {}, Nenue@30: freePins = {}, Nenue@29: } Nenue@0: WorldPlanPOIMixin = {} Nenue@30: WorldPlanFilterPinMixin = setmetatable({ QuestsByID = {}, freePins = {} }, {__tostring = function() return 'QuestHandler' end}) Nenue@0: local WorldPlanFlightMapMixin = setmetatable({}, {__tostring = function() return 'FlightMapHandler' end}) Nenue@30: local WorldQuests = WorldPlanQuestsMixin Nenue@30: Nenue@0: Nenue@0: local WorldPlan = WorldPlanCore Nenue@0: local QuestPOI = WorldPlanPOIMixin Nenue@0: local FilterPin = WorldPlanFilterPinMixin Nenue@0: local WP_VERSION = "1.0" Nenue@0: Nenue@9: local db Nenue@0: local print = DEVIAN_WORKSPACE and function(...) _G.print('WP', ...) end or function() end Nenue@7: local qprint = DEVIAN_WORKSPACE and function(...) _G.print('POI', ...) end or function() end Nenue@7: local iprint = DEVIAN_WORKSPACE and function(...) _G.print('ItemScan', ...) end or function() end Nenue@9: local wqprint = DEVIAN_WORKSPACE and function(...) _G.print('WorldQuests', ...) end or function() end Nenue@9: local fbprint = DEVIAN_WORKSPACE and function(...) _G.print('FilterBar', ...) end or function() end Nenue@7: Nenue@0: local wipe, tremove, tinsert, pairs, floor, tContains = table.wipe, table.remove, table.insert, pairs, floor, tContains Nenue@0: local TQ_GetQuestsForPlayerByMapID = C_TaskQuest.GetQuestsForPlayerByMapID -- This function is not yet documented Nenue@29: local TQ_GetQuestZoneID = C_TaskQuest.GetQuestZoneID Nenue@0: local TQ_IsActive = C_TaskQuest.IsActive Nenue@0: local ITEM_QUALITY_COLORS = ITEM_QUALITY_COLORS Nenue@0: local WorldMap_DoesWorldQuestInfoPassFilters = WorldMap_DoesWorldQuestInfoPassFilters Nenue@0: local QuestMapFrame_IsQuestWorldQuest = QuestMapFrame_IsQuestWorldQuest Nenue@0: local GameTooltip = GameTooltip Nenue@0: local GetItemIcon = GetItemIcon Nenue@0: Nenue@0: Nenue@0: local GetMapInfo, QuestPOIGetIconInfo = GetMapInfo, QuestPOIGetIconInfo Nenue@0: local GetQuestTagInfo, HaveQuestData = GetQuestTagInfo, HaveQuestData Nenue@0: local GetNumQuestLogRewards, GetNumQuestLogRewardCurrencies, GetQuestLogRewardMoney = GetNumQuestLogRewards, GetNumQuestLogRewardCurrencies, GetQuestLogRewardMoney Nenue@0: local GetQuestLogRewardInfo, GetQuestLogRewardCurrencyInfo, GetMoneyString = GetQuestLogRewardInfo, GetQuestLogRewardCurrencyInfo, GetMoneyString Nenue@0: Nenue@0: local GetCurrentMapAreaID, GetMapNameByID, GetSuperTrackedQuestID = GetCurrentMapAreaID, GetMapNameByID, GetSuperTrackedQuestID Nenue@0: local MC_GetNumZones, MC_GetZoneInfo = C_MapCanvas.GetNumZones, C_MapCanvas.GetZoneInfo Nenue@0: Nenue@0: Nenue@0: -- default color templates Nenue@0: local ARTIFACT_COLOR = ITEM_QUALITY_COLORS[LE_ITEM_QUALITY_ARTIFACT] Nenue@0: local MONEY_COLOR = {hex ='|cFFFFFF00', r=1, g=1, b=0} Nenue@0: local COMMON_COLOR = ITEM_QUALITY_COLORS[LE_ITEM_QUALITY_COMMON] Nenue@29: local DEFAULT_TYPE = { Nenue@0: a = 1, Nenue@0: r = 1, g = 1, b = 1, Nenue@0: x = 0, y = 0, Nenue@0: desaturated = true, Nenue@27: pinMask = POI_BORDER_MASK, Nenue@27: rewardMask = POI_BORDER_MASK, Nenue@0: texture = POI_BORDER_FILL, Nenue@0: continent = { Nenue@30: PinSize = 14, Nenue@30: Border = 2, Nenue@30: TrackingBorder = 1, Nenue@0: TagSize = 6, Nenue@30: TimeleftStage = 0, Nenue@27: showNumber = true, Nenue@30: numberFontObject = 'WorldPlanFont' Nenue@0: }, Nenue@0: zone = { Nenue@0: PinSize = 22, Nenue@0: Border = 3, Nenue@0: TrackingBorder = 2, Nenue@0: TagSize = 12, Nenue@0: TimeleftStage = 3, Nenue@27: showNumber = true, Nenue@30: numberFontObject = 'WorldPlanNumberFontThin' Nenue@0: }, Nenue@0: minimized = { Nenue@0: PinSize = 4, Nenue@30: Border = 0, Nenue@30: TrackingBorder = 1, Nenue@0: NoIcon = true, Nenue@0: TimeleftStage = 1, Nenue@27: showNumber = false, Nenue@0: } Nenue@0: } Nenue@29: Nenue@29: Nenue@0: local POI_FILTER_STYLE = setmetatable({ Nenue@0: continentBorder = 2, Nenue@0: zoneBorder = 2, Nenue@29: }, {__index = DEFAULT_TYPE}) Nenue@0: Nenue@9: Nenue@9: local defaults = { Nenue@9: defaultPinStyle = POI_DEFAULT_TYPE, Nenue@9: rewardStyle = POI_REWARD_TYPE, Nenue@9: filterStyle = POI_FILTER_STYLE, Nenue@27: ShowAllProfessionQuests = false, Nenue@9: DisplayContinentSummary = true, Nenue@9: DisplayContinentPins = true, Nenue@9: NotifyWhenNewQuests = true, Nenue@9: EnablePins = true, Nenue@9: FadeWhileGrouped = true, Nenue@9: } Nenue@9: Nenue@0: -- Summary header structure Nenue@29: Nenue@29: local REWARD_CASH = WORLD_QUEST_REWARD_TYPE_FLAG_GOLD Nenue@29: local REWARD_ARTIFACT_POWER = WORLD_QUEST_REWARD_TYPE_FLAG_ARTIFACT_POWER Nenue@29: local REWARD_GEAR = WORLD_QUEST_REWARD_TYPE_FLAG_EQUIPMENT Nenue@29: local REWARD_CURRENCY = WORLD_QUEST_REWARD_TYPE_FLAG_ORDER_RESOURCES Nenue@29: local REWARD_REAGENT = WORLD_QUEST_REWARD_TYPE_FLAG_MATERIALS Nenue@29: Nenue@29: Nenue@29: local LE_QUEST_TAG_TYPE_PVP = LE_QUEST_TAG_TYPE_PVP Nenue@29: local LE_QUEST_TAG_TYPE_PET_BATTLE = LE_QUEST_TAG_TYPE_PET_BATTLE Nenue@29: local LE_QUEST_TAG_TYPE_DUNGEON = LE_QUEST_TAG_TYPE_DUNGEON Nenue@29: local LE_QUEST_TAG_TYPE_PROFESSION = LE_QUEST_TAG_TYPE_PROFESSION Nenue@29: local LE_QUEST_TAG_TYPE_NORMAL = LE_QUEST_TAG_TYPE_NORMAL Nenue@0: local POI_FILTER_OPTIONS = { Nenue@0: { label = 'Filters', texture = "Interface\\WorldMap\\WorldMap-Icon" }, Nenue@0: { filterKey= 'rewardType', filterValue = REWARD_ARTIFACT_POWER, label = 'Artifact Power', texture = "Interface\\ICONS\\inv_7xp_inscription_talenttome01" }, Nenue@29: { filterKey= 'rewardType', filterValue = REWARD_CURRENCY,label = 'Order Resources', texture = "Interface\\ICONS\\inv_misc_elvencoins" }, Nenue@0: { filterKey= 'rewardType', filterValue = REWARD_GEAR, label = 'Equipment', texture = "Interface\\ICONS\\garrison_bluearmorupgrade" }, Nenue@29: { filterKey= 'rewardType', filterValue = REWARD_REAGENT, label = 'Materials', texture = 1417744 }, Nenue@29: { filterKey= 'rewardType', filterValue = REWARD_CASH, label = 'Gold', texture = ICON_MONEY }, Nenue@0: { filterKey= 'worldQuestType', filterValue = LE_QUEST_TAG_TYPE_PVP, label = 'PvP', texture = "Interface\\Icons\\Ability_PVP_GladiatorMedallion", spacing = 10 }, Nenue@0: { filterKey= 'worldQuestType', filterValue = LE_QUEST_TAG_TYPE_PET_BATTLE, label = 'Pet Battle', texture = "Interface\\Icons\\PetJournalPortrait", }, Nenue@0: { filterKey= 'worldQuestType', filterValue = LE_QUEST_TAG_TYPE_DUNGEON, label = 'Dungeon', texture = "Interface\\LFGFRAME\\UI-LFR-PORTRAIT", }, Nenue@9: { filterKey= 'worldQuestType', filterValue = LE_QUEST_TAG_TYPE_PROFESSION, label = 'Profession', texture = "Interface\\ICONS\\70_professions_scroll_02", }, Nenue@0: } Nenue@8: WorldPlanCore.BrokenIsleID = BROKEN_ISLES_ID Nenue@8: WorldPlanCore.FilterStyle = POI_FILTER_STYLE Nenue@8: Nenue@9: WorldPlanCore.FilterOptions = {} Nenue@14: WorldPlanCore.UsedFilters = {} Nenue@0: Nenue@9: Nenue@9: -- operating flags Nenue@9: local superTrackedID Nenue@9: local currentMapName Nenue@9: local hasNewQuestPins Nenue@9: local isContinentMap Nenue@0: local numPins = 0 Nenue@9: local FilterInclusions = {rewardType = {}, worldQuestType = {}, factionID = {}} Nenue@1: local NotificationTypes = {} Nenue@0: local ZoneInfo = {} Nenue@0: local SummaryHeaders = {} Nenue@0: Nenue@0: local FreePins = {} Nenue@0: local NumPinFrames = 1 Nenue@0: Nenue@0: local hasPendingQuestData Nenue@0: local notifyPlayed Nenue@0: local scanner, wmtt, WorldMapPOIFrame Nenue@0: Nenue@0: Nenue@0: local tasksQueue = {} Nenue@0: local function OnNext (func) Nenue@0: if #tasksQueue == 0 then Nenue@0: _G.WorldPlan:SetScript('OnUpdate', function() Nenue@0: local func = tremove(tasksQueue, 1) Nenue@0: if func then Nenue@0: func() Nenue@0: end Nenue@0: if #tasksQueue == 0 then Nenue@0: _G.WorldPlan:SetScript('OnUpdate', nil) Nenue@0: end Nenue@0: end) Nenue@0: end Nenue@27: print('inserting task #', #tasksQueue+1, func) Nenue@0: tinsert(tasksQueue, func) Nenue@0: end Nenue@0: Nenue@0: -- update a masked texture without messing up its blending mask Nenue@0: local SetMaskedTexture = function(region, file, mask) Nenue@0: mask = mask or POI_BORDER_MASK Nenue@0: region:SetMask(nil) Nenue@0: region:SetTexture(file) Nenue@0: region:SetMask(mask) Nenue@0: end Nenue@0: Nenue@0: -- tracking menu toggler Nenue@0: local DropDown_OnClick = function(self) Nenue@0: local key = self.value Nenue@0: if key then Nenue@0: if WorldPlanData[key] then Nenue@0: WorldPlanData[key] = nil Nenue@0: else Nenue@0: WorldPlanData[key] = true Nenue@0: end Nenue@0: end Nenue@9: WorldPlan:RefreshAll() Nenue@0: end Nenue@0: Nenue@0: function WorldPlan:print(...) Nenue@0: local msg Nenue@0: for i = 1, select('#', ...) do Nenue@0: msg = (msg and (msg .. ' ') or '') .. tostring(select(i, ...)) Nenue@0: end Nenue@0: DEFAULT_CHAT_FRAME:AddMessage("|cFF0088FFWorldPlan|r: " .. msg) Nenue@0: end Nenue@0: Nenue@30: local current_type_owner Nenue@30: function WorldPlan:AddHandler (frame, defaults) Nenue@30: print('|cFFFFFF00'..self:GetName()..':AddHandler()', frame:GetName()) Nenue@30: tinsert(self.modules, frame) Nenue@30: self.defaults[frame] = defaults Nenue@30: frame.GetTypeInfo = function(frame, typeID) Nenue@30: return self:GetTypeInfo(frame, typeID) Nenue@30: end Nenue@30: end Nenue@30: Nenue@0: function WorldPlan:OnLoad () Nenue@29: Nenue@29: self.Types = setmetatable({}, { Nenue@29: __newindex = function(t, k, v) Nenue@29: if type(v) == 'table' then Nenue@30: print('adding owner', k) Nenue@30: v = setmetatable(v, { Nenue@30: __newindex = function(t2,k2,v2) Nenue@30: if type(v2) == 'table' then Nenue@30: print('adding type', k2) Nenue@30: v2 = setmetatable(v2, {__index = function(t3,k3) Nenue@30: --print('##deferring to default key', k3) Nenue@30: return DEFAULT_TYPE[k3] Nenue@30: end}) Nenue@30: end Nenue@30: rawset(t2,k2,v2) Nenue@29: end}) Nenue@29: end Nenue@29: rawset(t,k,v) Nenue@29: end Nenue@29: }) Nenue@29: Nenue@30: self.Types[self] = {} Nenue@29: Nenue@29: for index, color in pairs(ITEM_QUALITY_COLORS) do Nenue@30: self:AddTypeInfo(self, index, { r = color.r, g = color.g, b = color.b, hex = color.hex, }) Nenue@29: end Nenue@29: Nenue@0: WorldPlan = self Nenue@0: scanner = _G.WorldPlanTooltip Nenue@0: wmtt = _G.WorldMapTooltip Nenue@0: WorldMapPOIFrame = _G.WorldMapPOIFrame Nenue@0: Nenue@0: WorldPlan:print('v'..WP_VERSION) Nenue@0: Nenue@0: self:RegisterEvent("QUESTLINE_UPDATE") Nenue@0: self:RegisterEvent("QUEST_LOG_UPDATE") Nenue@0: self:RegisterEvent("WORLD_MAP_UPDATE") Nenue@0: self:RegisterEvent("WORLD_QUEST_COMPLETED_BY_SPELL") Nenue@0: self:RegisterEvent("SUPER_TRACKED_QUEST_CHANGED") Nenue@0: self:RegisterEvent("SKILL_LINES_CHANGED") Nenue@0: self:RegisterEvent("ARTIFACT_XP_UPDATE") Nenue@0: self:RegisterEvent("ADDON_LOADED") Nenue@27: self:SetParent(WorldMapFrame) Nenue@0: end Nenue@0: Nenue@27: function WorldPlan:OnShow() Nenue@27: print(self:GetName()..':OnShow()') Nenue@27: if self.isStale then Nenue@30: self:Refresh() Nenue@27: end Nenue@27: Nenue@27: end Nenue@27: Nenue@0: function WorldPlan:OnEvent (event, ...) Nenue@0: print() Nenue@27: print(event, self.initialized) Nenue@0: if event == 'ADDON_LOADED' then Nenue@0: local addon = ... Nenue@0: if addon == "Blizzard_FlightMap" then Nenue@0: print('do mixin junk') Nenue@0: self.OnFlightMapLoaded() Nenue@0: Nenue@0: end Nenue@0: if IsLoggedIn() and not self.initialized then Nenue@0: self:Setup() Nenue@0: end Nenue@0: elseif event == 'WORLD_MAP_UPDATE' then Nenue@9: self.currentMapID = GetCurrentMapAreaID() Nenue@30: print('|cFFFF4400currentMapID =', self.currentMapID) Nenue@30: self:Refresh() Nenue@27: else Nenue@27: for i, module in ipairs(self.modules) do Nenue@27: if module.OnEvent then Nenue@27: print('forwarding to', tostring(module)) Nenue@27: module:OnEvent(event, ...) Nenue@27: end Nenue@0: end Nenue@0: end Nenue@0: end Nenue@0: Nenue@0: Nenue@0: function WorldPlan:Setup () Nenue@0: if not WorldPlanData then Nenue@0: WorldPlanData = {key = 0 } Nenue@0: end Nenue@0: WorldPlanData.key = (WorldPlanData.key or 0) + 1 Nenue@0: self.db = WorldPlanData Nenue@9: self.db.WorldQuests = self.db.WorldQuests or {} Nenue@9: db = self.db Nenue@9: for k,v in pairs(defaults) do Nenue@18: --[===[@non-debug@ Nenue@18: if not db[k] then Nenue@18: db[k] = v Nenue@18: end Nenue@18: Nenue@18: --@end-non-debug@]===] Nenue@18: --@debug@ Nenue@9: db[k] = v Nenue@18: --@end-debug@ Nenue@9: end Nenue@9: Nenue@9: self.currentMapID = GetCurrentMapAreaID() Nenue@0: Nenue@0: for i, module in ipairs(self.modules) do Nenue@9: module.db = self.db Nenue@0: if module.Setup then module:Setup() end Nenue@0: if not module.RegisterEvent then Nenue@0: module.RegisterEvent = self.RegisterEvent Nenue@0: end Nenue@0: end Nenue@0: self.initialized = true Nenue@0: Nenue@9: hooksecurefunc("UIDropDownMenu_Initialize", self.OnDropDownInitialize) Nenue@0: end Nenue@0: Nenue@30: function WorldPlan:AddTypeInfo(owner, id, info) Nenue@30: self.Types[owner] = self.Types[owner] or {} Nenue@30: self.Types[owner][id] = info Nenue@30: print('Type('..owner:GetName()..')('..id..') = '.. tostring(info)) Nenue@30: end Nenue@30: Nenue@30: function WorldPlan:GetTypeInfo(owner, typeID) Nenue@29: local info, extraInfo Nenue@30: if not owner then Nenue@30: --print('## deferring to default type list') Nenue@30: else Nenue@30: --print('## pulling for', owner:GetName(), 'id =', typeID) Nenue@30: end Nenue@30: Nenue@30: owner = owner or self Nenue@30: if (not typeID) or (not self.Types[owner][typeID]) then Nenue@30: --print('## sending list default') Nenue@29: info = DEFAULT_TYPE Nenue@29: else Nenue@30: --print('## sent list definition', typeID) Nenue@30: info = self.Types[owner][typeID] Nenue@29: end Nenue@29: Nenue@29: if isContinentMap then Nenue@29: extraInfo = info.continent Nenue@30: --print('### continent subtype', extraInfo) Nenue@29: else Nenue@29: extraInfo = info.zone Nenue@29: Nenue@30: --print('### zone subtype', extraInfo) Nenue@29: end Nenue@29: return info, extraInfo Nenue@29: end Nenue@29: Nenue@29: do Nenue@29: local timeStates = { Nenue@29: {maxSeconds = 60, Nenue@29: r=1, g=0.25, b =0, format = function (minutes) return '|cFFFF4400'.. minutes .. 'm' end, Nenue@29: }, Nenue@29: {maxSeconds = 240, Nenue@29: r=1, g=.5, b=0, format = function(minutes) return '|cFFFF4400'.. floor(minutes/60) .. 'h' end, Nenue@29: }, Nenue@29: {maxSeconds = 1440, Nenue@29: r=1, g=1, b=0, format = function(minutes) return '|cFFFFFF00'.. floor(minutes/60) .. 'h' end, Nenue@29: }, Nenue@29: {maxSeconds = 10081, Nenue@29: r=0, g=1, b=0, Nenue@29: }, -- 7 days + 1 minute Nenue@29: } Nenue@29: -- Generates a timeleft string Nenue@29: function WorldPlan:GetTimeInfo(timeLeft, limit) Nenue@29: limit = limit or #timeStates Nenue@29: for index = 1, limit do Nenue@29: local state = timeStates[index] Nenue@29: if timeLeft <= state.maxSeconds then Nenue@29: local text Nenue@29: if state.format then Nenue@29: text = state.format(timeLeft) Nenue@29: end Nenue@29: return text, index Nenue@29: end Nenue@29: end Nenue@29: return nil, nil Nenue@29: end Nenue@29: end Nenue@29: Nenue@30: function WorldPlan:Refresh (forced) Nenue@30: print('|cFFFFFF00'..self:GetName()..':Refresh()|r forced:', forced, 'init:', self.initialized) Nenue@9: if not self.initialized then Nenue@9: return Nenue@9: end Nenue@9: Nenue@9: POI_DEFAULT_TYPE = db.defaultPinStyle Nenue@9: POI_REWARD_TYPE = db.rewardStyle Nenue@9: POI_FILTER_STYLE = db.filterStyle Nenue@9: Nenue@9: Nenue@9: for i, module in ipairs(self.modules) do Nenue@9: if module.Reset then Nenue@9: print(module, 'Reset()') Nenue@9: module:Reset() Nenue@9: end Nenue@9: end Nenue@9: Nenue@0: for i, module in ipairs(self.modules) do Nenue@0: if module.Refresh then Nenue@9: print(module, 'Refresh()') Nenue@0: module:Refresh() Nenue@0: end Nenue@0: end Nenue@9: Nenue@9: for i, module in ipairs(self.modules) do Nenue@9: if module.Cleanup then Nenue@9: print(module, 'Cleanup()') Nenue@9: module:Cleanup() Nenue@9: end Nenue@9: end Nenue@0: end Nenue@0: function WorldPlan:UpdateAnchors () Nenue@0: for i, module in ipairs(self.modules) do Nenue@0: if module.UpdateAnchors then Nenue@0: module:UpdateAnchors() Nenue@0: end Nenue@0: end Nenue@0: end Nenue@0: Nenue@0: -- insert visual options into the tracking button menu Nenue@0: WorldPlan.OnDropDownInitialize = function (self, callback, dropType) Nenue@0: if self ~= WorldMapFrameDropDown then Nenue@0: return Nenue@0: end Nenue@9: local db = WorldPlan.db Nenue@0: Nenue@0: local info = UIDropDownMenu_CreateInfo() Nenue@0: info.text = "" Nenue@0: info.isTitle = true Nenue@0: UIDropDownMenu_AddButton(info) Nenue@0: info.text = "|cFF00AAFFWorldPlan|r" Nenue@0: info.isTitle = true Nenue@0: UIDropDownMenu_AddButton(info) Nenue@0: info.isTitle = nil Nenue@0: info.disabled = nil Nenue@0: info.keepShownOnClick = true Nenue@0: info.tooltipOnButton = 1 Nenue@0: Nenue@9: info.text = "Enable" Nenue@9: info.isNotRadio = true Nenue@9: info.value = "EnablePins" Nenue@9: info.checked = db.EnablePins Nenue@9: info.tooltipTitle = "Enable World Quest Overlays" Nenue@9: info.tooltipText = "Toggle the detail layers here." Nenue@9: info.func = DropDown_OnClick Nenue@9: UIDropDownMenu_AddButton(info) Nenue@9: Nenue@9: info.text = "Display All Profession Quests" Nenue@0: info.isNotRadio = true Nenue@0: info.value = "ShowAllProfessionQuests" Nenue@9: info.checked = db.ShowAllProfessionQuests Nenue@0: info.tooltipTitle = "Hidden Quests" Nenue@0: info.tooltipText = "Display work order and profession-related quests that are skipped by the default UI." Nenue@0: info.func = DropDown_OnClick Nenue@0: UIDropDownMenu_AddButton(info) Nenue@0: Nenue@0: info.text = "Show Continent Pins" Nenue@0: info.isNotRadio = true Nenue@0: info.value = "DisplayContinentPins" Nenue@9: info.checked = db.DisplayContinentPins Nenue@0: info.tooltipTitle = "Continent Pins" Nenue@0: info.tooltipText = "Display quest pins on the continent map (may get cramped)." Nenue@0: info.func = DropDown_OnClick Nenue@0: UIDropDownMenu_AddButton(info) Nenue@0: Nenue@0: info.text = "Show Summary" Nenue@0: info.isNotRadio = true Nenue@0: info.value = "DisplayContinentSummary" Nenue@0: info.tooltipTitle = "Summary Bar" Nenue@0: info.tooltipText = "Display a summary of active world quests. Note: requires directly viewing Broken Isle and Dalaran maps to gain complete info." Nenue@9: info.checked = db.DisplayContinentSummary Nenue@9: info.func = DropDown_OnClick Nenue@9: UIDropDownMenu_AddButton(info) Nenue@9: Nenue@9: info.text = "Fade In Groups" Nenue@9: info.isNotRadio = true Nenue@9: info.value = "FadeWhileGrouped" Nenue@9: info.tooltipTitle = "Group Fade" Nenue@9: info.tooltipText = "Reduce pin alpha when grouped, so player dots are easier to see." Nenue@9: info.checked = db.DisplayContinentSummary Nenue@0: info.func = DropDown_OnClick Nenue@0: UIDropDownMenu_AddButton(info) Nenue@0: end Nenue@0: Nenue@30: -------------------------------------------------------------------------------------------------------------------- Nenue@30: -------------------------------------------------------------------------------------------------------------------- Nenue@30: Nenue@30: local PinBaseIndex = 1600 Nenue@30: local BROKEN_ISLES_ID, DALARAN_ID, AZSUNA_ID, VALSHARAH_ID, HIGHMOUNTAIN_ID, STORMHEIM_ID, SURAMAR_ID, EOA_ID = 1007, 1014, 1015,1018, 1024, 1017, 1033, 1096 Nenue@30: Nenue@30: -- maps where we do our own anchors Nenue@30: local CONTINENT_MAPS = { [BROKEN_ISLES_ID] = BROKEN_ISLES_ID, } Nenue@30: local WORLD_QUEST_MAPS = { [DALARAN_ID] = 'Dalaran70', [AZSUNA_ID] = 'Azsuna', [VALSHARAH_ID] = "Val'sharah", Nenue@30: [HIGHMOUNTAIN_ID] = 'Highmountain', [STORMHEIM_ID] = 'Stormheim', [SURAMAR_ID] = 'Suramar', [EOA_ID] = 'EyeOfAszhara', } Nenue@30: Nenue@0: function WorldQuests:Setup() Nenue@29: Nenue@29: Nenue@29: for mapID, mapName in pairs(WORLD_QUEST_MAPS) do Nenue@30: self.QuestsByZone[mapID] = {} Nenue@29: end Nenue@29: Nenue@29: Nenue@0: -- refresh positions any time blizzard does so (i.e. mousewheel zoom) Nenue@0: hooksecurefunc("WorldMapScrollFrame_ReanchorQuestPOIs", function() Nenue@9: self:Refresh(true) Nenue@0: end) Nenue@0: Nenue@0: -- hide the original world quest POIs Nenue@0: hooksecurefunc("WorldMap_UpdateQuestBonusObjectives", function() Nenue@0: for i = 1, NUM_WORLDMAP_TASK_POIS do Nenue@0: local button = _G['WorldMapFrameTaskPOI'..i] Nenue@0: if button and button.worldQuest then Nenue@0: button:Hide() Nenue@0: end Nenue@0: end Nenue@0: end) Nenue@0: Nenue@0: end Nenue@0: Nenue@30: local defaults = {} Nenue@30: function WorldQuests:OnLoad() Nenue@30: print('|cFF00FF88'..self:GetName()..':OnLoad') Nenue@30: Nenue@30: WorldPlan:AddHandler(self, defaults) Nenue@30: Nenue@30: local rgbWhite = {1, 1, 1} Nenue@30: WorldPlan:AddTypeInfo(self, REWARD_REAGENT, { r = 0, g = 1, b = 1 }) Nenue@30: WorldPlan:AddTypeInfo(self, REWARD_ARTIFACT_POWER, { r = 1, g = .25, b = .5, hasNumeric = true, numberRGB = rgbWhite }) Nenue@30: WorldPlan:AddTypeInfo(self, REWARD_GEAR, { r = .1, g = .2, b = 1 }) Nenue@30: WorldPlan:AddTypeInfo(self, REWARD_CURRENCY, { r = 1, g = 1, b = 0, hasNumeric = true, numberRGB = {1,1,0}, }) Nenue@30: WorldPlan:AddTypeInfo(self, REWARD_CASH, { r = 0, g = 0, b = 0, }) Nenue@30: Nenue@30: for areaID, fileName in pairs(WORLD_QUEST_MAPS) do Nenue@30: self.QuestsByZone[areaID] = {} Nenue@30: end Nenue@30: Nenue@30: self:RegisterEvent('QUEST_LOG_UPDATE') Nenue@30: self:RegisterEvent('WORLD_QUEST_COMPLETED_BY_SPELL') Nenue@30: self:RegisterEvent('SKILL_LINES_CHANGED') Nenue@30: Nenue@30: Nenue@30: end Nenue@30: Nenue@0: function WorldQuests:OnEvent (event, ...) Nenue@30: local print = wqprint Nenue@30: print('|cFFFFFF00'..self:GetName()..':OnEvent()'..event..'|r', GetTime(), ...) Nenue@0: if event == 'QUEST_LOG_UPDATE' then Nenue@0: local questID, added = ... Nenue@0: if questID and added then Nenue@29: local questPOI = self:AcquirePin(questID) Nenue@29: self.hasUpdate, self.isPending = questPOI:RefreshData() Nenue@0: else Nenue@29: self:RefreshData() Nenue@0: end Nenue@30: print('WorldMapFrame', WorldMapFrame:IsVisible(), 'hasUpdates:', self.hasUpdate) Nenue@0: elseif event == 'WORLD_QUEST_COMPLETED_BY_SPELL' then Nenue@0: local questID = ... Nenue@30: if questID and self.QuestsByID[questID] then Nenue@30: self:ReleasePin(self.QuestsByID[questID]) Nenue@0: end Nenue@0: elseif event == 'SKILL_LINES_CHANGED' then Nenue@30: self.hasUpdate = true Nenue@30: end Nenue@30: end Nenue@30: Nenue@30: function WorldQuests:OnUpdate() Nenue@30: if self.hasUpdate then Nenue@30: wqprint('|cFF00FF00pushing update') Nenue@9: self:Refresh(true) Nenue@0: end Nenue@0: end Nenue@30: Nenue@29: local TQ_GetQuestLocation = C_TaskQuest.GetQuestLocation Nenue@29: function WorldQuests:AcquirePin (questID, mapID) Nenue@30: local pin = self.QuestsByID[questID] Nenue@0: local isNew = false Nenue@0: if not pin then Nenue@0: isNew = true Nenue@0: local numFree = #self.freePins Nenue@0: if numFree >= 1 then Nenue@0: pin = tremove(self.freePins, numFree) Nenue@0: --print('|cFF00FF00Re-using', pin:GetName()) Nenue@0: else Nenue@0: local name = 'WorldPlanQuestMarker' .. NumPinFrames Nenue@0: --print('|cFF00FF00Creating', name) Nenue@0: pin = CreateFrame('Frame', name, WorldMapPOIFrame, 'WorldPlanQuestPin') Nenue@29: Nenue@0: pin:SetFrameStrata('HIGH') Nenue@30: pin.GetTypeInfo = function(frame, typeID) Nenue@30: return self:GetTypeInfo(typeID) Nenue@30: end Nenue@0: NumPinFrames = NumPinFrames + 1 Nenue@0: --pin.iconBorder:SetVertexColor(0,0,0,1) Nenue@0: end Nenue@29: pin:SetID(questID) Nenue@0: pin.isNew = true Nenue@0: pin.currentWidth = nil Nenue@0: Nenue@0: -- used by TaskPOI_x scripts Nenue@0: pin.questID = questID Nenue@0: pin.worldQuest = true Nenue@7: Nenue@30: self.QuestsByID[questID] = pin Nenue@0: else Nenue@0: --print('|cFF00FF00Using', pin:GetName()) Nenue@0: end Nenue@29: mapID = mapID or TQ_GetQuestZoneID(questID) Nenue@30: self.QuestsByZone[mapID][questID] = pin Nenue@0: Nenue@0: return pin, isNew Nenue@0: end Nenue@0: Nenue@0: -- remove from index and add it to the recycling heap Nenue@0: function WorldQuests:ReleasePin (pin) Nenue@0: Nenue@0: local id = pin.questId Nenue@0: if id then Nenue@30: self.QuestsByID[id] = nil Nenue@30: for i, zone in pairs(self.QuestsByZone) do Nenue@0: print('-', i, zone[i]) Nenue@0: zone[id] = nil Nenue@0: end Nenue@0: end Nenue@0: pin:Hide() Nenue@0: pin:ClearAllPoints() Nenue@0: tinsert(self.freePins, pin) Nenue@0: print('|cFFFF4400Clearing out', pin:GetName(),id) Nenue@0: end Nenue@0: Nenue@0: -- create of update quest pins for a map and its underlying zones Nenue@29: function WorldQuests:RefreshData (mapID) Nenue@9: local print = wqprint Nenue@0: mapID = mapID or GetCurrentMapAreaID() Nenue@0: superTrackedID = GetSuperTrackedQuestID() Nenue@0: if not mapID then Nenue@0: -- info not available yet Nenue@0: return Nenue@0: end Nenue@30: Nenue@30: print('|cFF00FF88'..self:GetName()..':RefreshData()|r', 'map:', mapID, 'realMap:', GetCurrentMapAreaID()) Nenue@30: Nenue@0: if mapID == BROKEN_ISLES_ID then Nenue@29: self.hasUpdate = false Nenue@30: print('|cFF00FFFFContinent:|r', mapID, GetMapNameByID(mapID), superTrackedID) Nenue@0: self.fullSearch = true Nenue@0: for i = 1, MC_GetNumZones(mapID) do Nenue@0: local submapID, name, depth = MC_GetZoneInfo(mapID, i) Nenue@29: self:RefreshData(submapID) Nenue@0: end Nenue@0: self.fullSearch = nil Nenue@30: elseif self.QuestsByZone[mapID] then Nenue@0: local taskInfo = TQ_GetQuestsForPlayerByMapID(mapID) Nenue@0: local numQuests = 0 Nenue@0: if taskInfo and #taskInfo >= 1 then Nenue@30: print('|cFF00FFFF Zone:|r', mapID, GetMapNameByID(mapID), #taskInfo) Nenue@30: wipe(self.QuestsByZone[mapID]) Nenue@0: ZoneInfo[mapID] = taskInfo Nenue@30: qprint('|cFFFF4400START of', GetMapNameByID(mapID)) Nenue@0: for taskID, info in pairs(taskInfo) do Nenue@0: local questID = info.questId Nenue@0: info.mapID = mapID Nenue@29: local questPOI = self:AcquirePin(questID, mapID) Nenue@30: local hasUpdate, isPending = questPOI:RefreshData(info) Nenue@29: self.hasUpdate = (self.hasUpdate or hasUpdate) Nenue@29: self.isPending = (self.isPending or isPending) Nenue@0: numQuests = numQuests + 1 Nenue@0: end Nenue@30: qprint('|cFFFF4400END of', GetMapNameByID(mapID)) Nenue@0: end Nenue@0: end Nenue@0: Nenue@30: if not self.fullSearch then Nenue@30: print(' hasUpdate:', self.hasUpdate, 'isPending:', self.isPending, 'timer:', (self.OnNext and 'waiting' or '')) Nenue@30: Nenue@0: end Nenue@30: Nenue@0: end Nenue@0: Nenue@0: function WorldQuests:Refresh(forced) Nenue@9: local print = wqprint Nenue@30: print('|cFF00FF88'..self:GetName()..':Refresh()|r') Nenue@0: if not WorldMapPOIFrame:IsVisible() then Nenue@0: return Nenue@0: end Nenue@9: if forced then Nenue@9: self:Reset() Nenue@9: end Nenue@0: self:UpdateAnchors() Nenue@9: Nenue@9: if forced then Nenue@9: self:Cleanup () Nenue@9: end Nenue@0: end Nenue@0: Nenue@0: -- prepares elements for a map update Nenue@0: function WorldQuests:Reset () Nenue@9: local print = wqprint Nenue@30: print('|cFF00FF88'..self:GetName()..':Reset()|r') Nenue@30: for questID, pin in pairs(self.QuestsByID) do Nenue@0: pin.used = nil Nenue@0: end Nenue@0: end Nenue@0: Nenue@0: -- update visibility states of all pins Nenue@0: function WorldQuests:UpdateAnchors (submapID) Nenue@9: local print = wqprint Nenue@9: local db = WorldPlan.db Nenue@0: local mapFileName, textureHeight, textureWidth, isMicroDungeon, microDungeonMapName = GetMapInfo() Nenue@0: if isMicroDungeon then Nenue@0: return Nenue@0: end Nenue@0: Nenue@0: local currentMap = GetCurrentMapAreaID() Nenue@0: local submapID = submapID or currentMap Nenue@0: Nenue@9: if submapID == BROKEN_ISLES_ID and (not db.DisplayContinentPins) then Nenue@0: print('not updating map for reasons') Nenue@0: return Nenue@0: end Nenue@30: print('|cFF88FF00'..self:GetName()..':UpdateAnchors|r', submapID, GetMapNameByID(submapID), 'pin count:', numPins) Nenue@0: local numZones = MC_GetNumZones(submapID) Nenue@0: if numZones then Nenue@0: for i = 1, numZones do Nenue@0: local subMapID = MC_GetZoneInfo(submapID, i) Nenue@0: self:UpdateAnchors(subMapID) Nenue@0: end Nenue@0: end Nenue@30: local pins = self.QuestsByZone[submapID] Nenue@0: Nenue@0: if pins then Nenue@0: local hostFrame = WorldMapPOIFrame Nenue@0: local mapWidth, mapHeight = hostFrame:GetSize() Nenue@0: for questID, pin in pairs(pins) do Nenue@30: pin.hasUpdate = true Nenue@9: pin:IsShowable() Nenue@9: if pin.used then Nenue@9: pin:SetFrameLevel(PinBaseIndex+ (pin.whiteListed and 200 or 0) +numPins) Nenue@9: print('level', PinBaseIndex+ (pin.whiteListed and 200 or 0) +numPins) Nenue@0: pin:SetAnchor(WorldMapPOIFrame, currentMap, mapWidth, mapHeight) Nenue@0: numPins = numPins + 1 Nenue@0: end Nenue@0: end Nenue@0: end Nenue@0: end Nenue@0: Nenue@0: -- shows, animates, or hides pins based on their current visibility flags Nenue@0: local debug_show = {} Nenue@0: local debug_animate = {} Nenue@0: local debug_hide = {} Nenue@0: function WorldQuests:Cleanup () Nenue@9: local print = wqprint Nenue@9: local showQuestPOI = db.EnablePins Nenue@0: print('|cFFFFFF00'..tostring(self)..':Cleanup()|r') Nenue@9: local mapID = GetCurrentMapAreaID() Nenue@9: isContinentMap = (mapID == BROKEN_ISLES_ID) Nenue@9: Nenue@0: wipe(debug_show) Nenue@0: wipe(debug_animate) Nenue@0: wipe(debug_hide) Nenue@0: -- continent or zone sizing Nenue@9: local fadeGrouped = (db.FadeWhileGrouped and IsInGroup()) Nenue@0: Nenue@0: numPins = 0 Nenue@30: for questID, pin in pairs(self.QuestsByID) do Nenue@0: -- can we show it? Nenue@0: if showQuestPOI and (pin.used) then Nenue@0: Nenue@30: pin.hasUpdate = true Nenue@9: if fadeGrouped then Nenue@9: pin:SetAlpha(0.25) Nenue@9: else Nenue@9: pin:SetAlpha(1) Nenue@9: end Nenue@0: -- is it a new quest? Nenue@0: if pin.isNew then Nenue@0: if not pin.isAnimating then Nenue@0: pin.isAnimating = true Nenue@0: OnNext(function() Nenue@27: pin:ShowNew() Nenue@0: end) Nenue@0: if not notifyPlayed then Nenue@1: for k,v in pairs(NotificationTypes) do Nenue@1: if v[pin[k]] then Nenue@1: notifyPlayed = true Nenue@1: PlaySoundKitID(23404) Nenue@1: end Nenue@1: end Nenue@0: end Nenue@0: tinsert(debug_animate,questID) Nenue@9: else Nenue@9: Nenue@9: print('animating? ', questID, 'filtered:', pin.filtered) Nenue@0: end Nenue@1: -- trap new but animating pins here Nenue@0: else Nenue@0: -- hard show existing pin Nenue@30: print('refresh #', questID, 'filtered:', pin.filtered, 'hasUpdate', pin.hasUpdate) Nenue@30: pin:Show() Nenue@0: tinsert(debug_show,questID) Nenue@0: end Nenue@0: else Nenue@9: if pin:IsShown() then Nenue@9: tinsert(debug_hide,questID) Nenue@9: end Nenue@9: pin.isAnimating = nil Nenue@9: pin.FadeIn:Stop() Nenue@0: pin:Hide() Nenue@0: end Nenue@0: end Nenue@0: print(' adding:', table.concat(debug_animate, ',' )) Nenue@9: print(' refresh:', table.concat(debug_show, ',' )) Nenue@9: print(' hiding:', table.concat(debug_hide, ',' )) Nenue@0: hasNewQuestPins = nil Nenue@1: notifyPlayed = nil Nenue@30: self.hasUpdate = nil Nenue@0: end Nenue@0: Nenue@0: -- data provider manipulations for the taxi map Nenue@0: WorldPlan.OnFlightMapLoaded = function() Nenue@0: if true then return end Nenue@0: -- todo: figure out how to layer inside the map canvas Nenue@0: local res = {} Nenue@0: local t = {} Nenue@0: for k,v in pairs(FlightMapFrame) do Nenue@0: tinsert(res, tostring(k)) Nenue@0: end Nenue@0: Nenue@0: table.sort(res) Nenue@0: for i, k in ipairs(res) do Nenue@0: print(k) Nenue@0: end Nenue@0: hooksecurefunc(FlightMapFrame, 'RefreshAll', function(self) Nenue@0: print('|cFF0088FFWQDP RefreshAllData ', GetTime()) Nenue@0: Nenue@0: WorldPlan:GetPinsForMap(self:GetMapID()) Nenue@0: Nenue@0: for pin in self:EnumerateAllPins() do Nenue@0: if pin.worldQuest then Nenue@0: --print('got pin #', pin.questID) Nenue@30: local wp = self.QuestsByID[pin.questID] Nenue@0: if wp then Nenue@0: wp:ClearAllPoints() Nenue@0: wp:SetParent(FlightMapFrame.ScrollContainer) Nenue@0: wp:SetFrameStrata('MEDIUM') Nenue@0: wp:SetPoint('CENTER', pin, 'CENTER') Nenue@0: wp:Show() Nenue@0: end Nenue@0: end Nenue@0: end Nenue@0: end) Nenue@0: end Nenue@0: Nenue@0: Nenue@0: Nenue@0: local throttle = 0 Nenue@0: local tooltip = CreateFrame ("GameTooltip", "VeneerWorldQuestsScanner", nil, "GameTooltipTemplate") Nenue@0: local tooltipLine1 = _G['VeneerWorldQuestsScannerTextLeft1'] Nenue@0: local tooltipLine3 = _G['VeneerWorldQuestsScannerTextLeft3'] Nenue@0: local GetTime, mod = GetTime, mod Nenue@0: Nenue@0: Nenue@0: Nenue@0: Nenue@9: function WorldQuests:FilterCheckByID(questID) Nenue@9: local pin = WorldQuests:GetPinByQuestID(questID) Nenue@9: return pin:IsShowable() Nenue@0: end Nenue@0: Nenue@0: Nenue@9: function QuestPOI:IsShowable () Nenue@9: local print = wqprint Nenue@9: local db = WorldPlan.db Nenue@0: local qType = self.worldQuestType Nenue@0: local rType = self.rewardType Nenue@0: self.filtered = nil Nenue@9: self.used = true Nenue@9: Nenue@14: print(' |cFFFF4400IsShowable()|r', self.title) Nenue@14: Nenue@30: if not self.passesBlizzFilters then Nenue@30: self.filtered = true Nenue@0: end Nenue@0: if not TQ_IsActive(self.questID) then Nenue@9: self.used = nil Nenue@30: elseif qType == LE_QUEST_TAG_TYPE_PROFESSION then Nenue@9: if not (db.ShowAllProfessionQuests or (self.tradeskillLineIndex and GetProfessionInfo(self.tradeskillLineIndex))) then Nenue@9: self.used = nil Nenue@0: end Nenue@0: end Nenue@9: return self.used, self.filtered Nenue@0: end Nenue@0: Nenue@0: function QuestPOI:UpdateTimer (timeLeft, timeType) Nenue@0: print('|cFF0088FFUpdatePinTimer()|r') Nenue@0: end Nenue@0: Nenue@9: --- Fixes icons upon size update Nenue@9: function QuestPOI:UpdateSize (style, subStyle) Nenue@27: style = style or self.style Nenue@27: subStyle = subStyle or self.subStyle Nenue@0: Nenue@30: --qprint('|cFF00FF88'..self:GetName()..'|r:UpdateSize()', style, subStyle) Nenue@0: Nenue@0: self.currentWidth = subStyle.PinSize Nenue@0: self.borderSize = subStyle.Border Nenue@0: self.trackingBorderSize = subStyle.TrackingBorder Nenue@0: self.tagSize = subStyle.TagSize Nenue@0: self.TimeleftStage = subStyle.TimeleftStage Nenue@0: self.NoIcon = subStyle.NoIcon Nenue@0: Nenue@27: Nenue@0: self:SetSize(self.currentWidth, self.currentWidth) Nenue@0: Nenue@27: local icon = self.icon Nenue@0: local iconBorder = self.iconBorder Nenue@0: local trackingBorder = self.supertrackBorder Nenue@0: local tag = self.tagIcon Nenue@27: local pinMask = style.pinMask Nenue@27: local rewardMask = style.rewardMask Nenue@0: Nenue@27: if self.NoIcon then Nenue@27: self.icon:Hide() Nenue@27: else Nenue@27: self.icon:Show() Nenue@27: icon:SetMask(nil) Nenue@27: icon:SetMask(rewardMask) Nenue@27: icon:SetTexture(self.icon:GetTexture()) Nenue@27: end Nenue@27: iconBorder:SetMask(nil) Nenue@27: trackingBorder:SetMask(nil) Nenue@0: Nenue@0: Nenue@0: local borderWidth = self.borderSize Nenue@0: local trackingWidth = self.trackingBorderSize Nenue@0: Nenue@0: iconBorder:ClearAllPoints() Nenue@0: iconBorder:SetPoint('BOTTOMLEFT', self, 'BOTTOMLEFT', -borderWidth + (style.x or 0), -borderWidth + (style.y or 0)) Nenue@0: iconBorder:SetPoint('TOPRIGHT', self, 'TOPRIGHT', borderWidth + (style.x or 0), borderWidth + (style.y or 0)) Nenue@0: Nenue@0: trackingBorder:ClearAllPoints() Nenue@0: trackingBorder:SetPoint('BOTTOMLEFT', iconBorder, 'BOTTOMLEFT', -trackingWidth, -trackingWidth) Nenue@0: trackingBorder:SetPoint('TOPRIGHT', iconBorder, 'TOPRIGHT', trackingWidth, trackingWidth) Nenue@0: Nenue@0: if self.tagSize then Nenue@0: tag:Show() Nenue@0: tag:ClearAllPoints() Nenue@0: tag:SetPoint('BOTTOMRIGHT', self, 'BOTTOMRIGHT', borderWidth, -borderWidth) Nenue@0: else Nenue@0: tag:Hide() Nenue@0: end Nenue@0: Nenue@9: --qprint('using mask:', mask, self.name ) Nenue@27: iconBorder:SetMask(pinMask) Nenue@27: trackingBorder:SetMask(pinMask) Nenue@0: Nenue@0: Nenue@0: end Nenue@0: Nenue@0: Nenue@0: Nenue@0: --%debug% Nenue@0: local SetTimedCallbackForAllPins = function(seconds, callback) Nenue@0: C_Timer.After(seconds, function() Nenue@30: for id, pin in pairs(WorldPlanQuests.QuestsByID) do Nenue@0: callback(pin) Nenue@0: end Nenue@0: end) Nenue@0: end Nenue@0: Nenue@0: SLASH_WORLDPLAN1 = "/worldplan" Nenue@0: SLASH_WORLDPLAN2 = "/wp" Nenue@0: SlashCmdList.WORLDPLAN = function() Nenue@0: print('command pop') Nenue@0: WorldPlan:GetPinsForMap() Nenue@0: WorldPlan:RefreshPins() Nenue@0: Nenue@0: SetTimedCallbackForAllPins(0, function(self) self.FadeIn:Play() self.FlashIn:Play() end) Nenue@0: SetTimedCallbackForAllPins(5, function(self) self.PendingFade:Play() end) Nenue@0: SetTimedCallbackForAllPins(8, function(self) self.PendingFade:Stop() end) Nenue@0: end Nenue@0: --%end-debug%