Mercurial > wow > buffalo2
view ObjectiveTracker/ObjectiveCore.lua @ 37:e84d645c8ab8
- revised the tracker update function to build its complete data list up front and use the values as points of comparison for determining possible out of place blocks, which will be iterated over afterward to remove what wasn't re-used
- also entailed revising the exact role of global event handlers and function hooks, limiting their directions of communication so one doesn't end up calling the other multiple or inifinity times
- schema handling polish
author | Nenue |
---|---|
date | Mon, 18 Apr 2016 07:56:23 -0400 |
parents | 4b3da1b221de |
children |
line wrap: on
line source
--- ${PACKAGE_NAME} -- @file-author@ -- @project-revision@ @project-hash@ -- @file-revision@ @file-hash@ -- Created: 3/26/2016 1:51 AM local B, _G = select(2,...).frame, _G local pairs, setmetatable, type, tostring, band, format = _G.pairs, _G.setmetatable, _G.type, _G.tostring, bit.band, string.format local ipairs, tinsert, hooksecurefunc = _G.ipairs, _G.tinsert, _G.hooksecurefunc local PlaySoundFile, IsQuestTask, SortQuestWatches, GetCurrentMapAreaID, GetZoneText, GetMinimapZoneText = PlaySoundFile, IsQuestTask, SortQuestWatches, GetCurrentMapAreaID, GetZoneText, GetMinimapZoneText local AddQuestWatch, SetSuperTrackedQuestID, GetNumQuestWatches, AUTO_QUEST_WATCH, MAX_WATCHABLE_QUESTS = AddQuestWatch, SetSuperTrackedQuestID, GetNumQuestWatches, AUTO_QUEST_WATCH, MAX_WATCHABLE_QUESTS local QuestPOIUpdateIcons, GetCVar, IsPlayerInMicroDungeon, WorldMapFrame, GetCVarBool, SetMapToCurrentZone = QuestPOIUpdateIcons, GetCVar, IsPlayerInMicroDungeon, WorldMapFrame, GetCVarBool, SetMapToCurrentZone local AddAutoQuestPopUp = AddAutoQuestPopUp local T = B:RegisterModule("ObjectiveTracker", _G.VeneerObjectiveWrapper, 'BuffFrame') local print = B.print('Objectives') local ObjectiveTrackerFrame, VeneerObjectiveScroll, CreateFrame = _G.ObjectiveTrackerFrame, _G.VeneerObjectiveScroll, _G.CreateFrame local Wrapper = _G.VeneerObjectiveWrapper local Scroller = VeneerObjectiveWrapper.scrollArea local Scroll = _G.VeneerObjectiveScroll --- Performance values --[[ self:RegisterEvent("QUEST_LOG_UPDATE"); self:RegisterEvent("TRACKED_ACHIEVEMENT_LIST_CHANGED"); self:RegisterEvent("QUEST_WATCH_LIST_CHANGED"); self:RegisterEvent("QUEST_AUTOCOMPLETE"); self:RegisterEvent("QUEST_ACCEPTED"); self:RegisterEvent("SUPER_TRACKED_QUEST_CHANGED"); self:RegisterEvent("SCENARIO_UPDATE"); self:RegisterEvent("SCENARIO_CRITERIA_UPDATE"); self:RegisterEvent("TRACKED_ACHIEVEMENT_UPDATE"); self:RegisterEvent("ZONE_CHANGED_NEW_AREA"); self:RegisterEvent("ZONE_CHANGED"); self:RegisterEvent("QUEST_POI_UPDATE"); self:RegisterEvent("VARIABLES_LOADED"); self:RegisterEvent("QUEST_TURNED_IN"); self:RegisterEvent("PLAYER_MONEY"); ]] --- These are the bitfields used by Blizzard_ObjectiveTracker to determine which segments get parsed. --- They are replicated here so that plugins can make use of any securehook args involving this info. local OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST = OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST -- 0x0100 local OBJECTIVE_TRACKER_UPDATE_MODULE_AUTO_QUEST_POPUP = OBJECTIVE_TRACKER_UPDATE_MODULE_AUTO_QUEST_POPUP -- 0x0200 local OBJECTIVE_TRACKER_UPDATE_MODULE_BONUS_OBJECTIVE = OBJECTIVE_TRACKER_UPDATE_MODULE_BONUS_OBJECTIVE -- 0x0400 local OBJECTIVE_TRACKER_UPDATE_MODULE_SCENARIO = OBJECTIVE_TRACKER_UPDATE_MODULE_SCENARIO -- 0x0800 local OBJECTIVE_TRACKER_UPDATE_MODULE_ACHIEVEMENT = OBJECTIVE_TRACKER_UPDATE_MODULE_ACHIEVEMENT -- 0x1000 local OBJECTIVE_TRACKER_UPDATE_STATIC = OBJECTIVE_TRACKER_UPDATE_STATIC -- 0x0000 local OBJECTIVE_TRACKER_UPDATE_ALL = OBJECTIVE_TRACKER_UPDATE_ALL -- 0xFFFF local OBJECTIVE_TRACKER_UPDATE_ID = OBJECTIVE_TRACKER_UPDATE_ID -- 0 local OBJECTIVE_TRACKER_UPDATE_QUEST = OBJECTIVE_TRACKER_UPDATE_QUEST -- 0x0001 local OBJECTIVE_TRACKER_UPDATE_QUEST_ADDED = OBJECTIVE_TRACKER_UPDATE_QUEST_ADDED -- 0x0002 local OBJECTIVE_TRACKER_UPDATE_TASK_ADDED = OBJECTIVE_TRACKER_UPDATE_TASK_ADDED -- 0x0004 local OBJECTIVE_TRACKER_UPDATE_SCENARIO = OBJECTIVE_TRACKER_UPDATE_SCENARIO -- 0x0008 local OBJECTIVE_TRACKER_UPDATE_SCENARIO_NEW_STAGE = OBJECTIVE_TRACKER_UPDATE_SCENARIO_NEW_STAGE -- 0x0010 local OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT = OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT -- 0x0020 local OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT_ADDED = OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT_ADDED -- 0x0040 local OBJECTIVE_TRACKER_UPDATE_SCENARIO_BONUS_DELAYED = OBJECTIVE_TRACKER_UPDATE_SCENARIO_BONUS_DELAYED -- 0x0080 local OBJECTIVE_TRACKER_UPDATE_REASON = OBJECTIVE_TRACKER_UPDATE_ALL -- default --- Used to determine which trackers are listening for money events T.strings = {} T.strings.CLICK_TO_ACCCEPT = 'Click to Accept' T.strings.CLICK_TO_COMPLETE = 'Click to complete' T.colors ={ enable = true, default = { titlebg = {'HORIZONTAL', 1, 0, .7, .25, 1, 0, .7, .125}, textbg = {'HORIZONTAL', 0, 0, 0, 0.4, 0, 0, 0, 0 }, selectionbg = {'HORIZONTAL', 1, 1, 1, 0, 1, 1, 1, 0.225}, }, daily = { titlebg = {'HORIZONTAL', 0, .7, 1, .25, 0, 1, .7, .125}, textbg = {'HORIZONTAL', 0, .7, 1, .1, 0, 1, .7, .075 }, }, weekly = { titlebg = {'HORIZONTAL', 0, .35, .7, .25, 0, .35, .7, .125}, textbg = {'HORIZONTAL', 0, .35, .7, .1, 0, .35, .7, .075 }, }, account = { titlebg = {'HORIZONTAL', .1, .1, .1, .25, .1, .1, .1, .125}, textbg = {'HORIZONTAL', .1, .1, .1, 0.4, .1, .1, .1, .085 }, }, -- alliance faction_1 = { titlebg = {'HORIZONTAL', .2, .4, 1, 0.4, .2, .4, 1, .085 }, textbg = {'HORIZONTAL', .2, .4, 1, 0.4, .2, .4, 1, .085 }, }, -- horde faction_2 = { titlebg = {'HORIZONTAL', .6, 0, 0.4, 0.4, .6, 0, 0.4, .085 }, textbg = {'HORIZONTAL', .6, 0, 0.4, 0.4, .6, 0, 0.4, .085 }, } } T.watchMoneyReasons = 0 --- Baseline defaults table; values defined in the files that they pertain to T.defaults = {} --- Tracker display order T.orderedNames = {'Bonus', 'AutoQuest', 'Quest', 'Cheevs'} --- ipairs() argument tables T.orderedHandlers = setmetatable({}, {__mode = "k"}) T.orderedTrackers = setmetatable({}, {__mode = "k"}) T.indexedTrackers = setmetatable({}, {__mode = "k"}) --- pairs() argument tables T.namedTrackers = setmetatable({}, {__mode = "k"}) local WRAPPER_ANCHOR_POINT = 'TOPRIGHT' local WRAPPER_OFFSET_X = 0 local WRAPPER_OFFSET_Y = -200 local WRAPPER_MAX_HEIGHT = 670 local WRAPPER_WIDTH = 280 local WRAPPER_HEADER_HEIGHT = 24 T.defaults.Wrapper = { AnchorPoint = WRAPPER_ANCHOR_POINT, OffsetX = WRAPPER_OFFSET_X, OffsetY = WRAPPER_OFFSET_Y, Height = WRAPPER_MAX_HEIGHT, Width = WRAPPER_WIDTH, HeaderHeight = WRAPPER_HEADER_HEIGHT, TextSpacing = 3, TitleSpacing = 3, } --- Tracker module definitions begin here; innards dealing with data retreival and output are defined further in T.DefaultHandler = { previousHeight = 0, name = "temp", displayName = "temp", updateReasonModule = 0xFF00, updateReasonEvent = 0x00FF, numWatched = 0, --- number of entries being handled numBlocks = 0, --- number of blocks created actualBlocks = 0, --- number of blocks in use freeBlocks = {}, --- block heap usedBlocks = {}, Info = {}, -- find data by ID BlockInfo = {}, -- find data by block ID Watched = {}, -- find watchIndex by data ID WatchInfo = {}, -- find data by watch index WatchBlock = {}, -- find block by watch index } T.AutoQuest = { name = "AutoQuest", displayName = "Notice", updateReasonModule = OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST, updateReasonEvents = OBJECTIVE_TRACKER_UPDATE_QUEST + OBJECTIVE_TRACKER_UPDATE_QUEST_ADDED, } T.Quest = { name = "Quest", displayName = "Quests", updateReasonModule = OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST, updateReasonEvents = OBJECTIVE_TRACKER_UPDATE_QUEST + OBJECTIVE_TRACKER_UPDATE_QUEST_ADDED, itemButtons = {}, freeButtons = {}, } T.Cheevs = { name = "Cheevs", displayName = "Achievements", updateReasonModule = OBJECTIVE_TRACKER_UPDATE_MODULE_ACHIEVEMENT, updateReasonEvents = OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT + OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT_ADDED, } T.Bonus = { name = "Bonus", displayName = "Bonus Objectives", updateReasonModule = OBJECTIVE_TRACKER_UPDATE_MODULE_BONUS_OBJECTIVE, updateReasonEvents = OBJECTIVE_TRACKER_UPDATE_QUEST + OBJECTIVE_TRACKER_UPDATE_TASK_ADDED } local Tracker_string = function (self) return self.name end local Tracker_call = function (self, reason) self:Update(reason) end local Handler_Initialize = function (self, name, index) local c = T.Conf.Wrapper print('Initializing |cFF00FFFF'..name..'|r module...') local handler = setmetatable(T[name] or {}, { __tostring = Tracker_string, __call = Tracker_call }) if type(T.orderedHandlers[index]) == 'table' then return T.orderedHandlers[index] end print('|cFFFFFF00Acquiring locals') local preset = {} for k, _ in pairs(handler) do preset[k] = true end print('|cFFFF8800Inheriting') for k, v in pairs(self) do if not handler[k] then if type(v) == 'table' then -- assume all tables to be local data; don't inherit or ref handler[k] = {} else handler[k] = self[k] end print('copying', k) end end print('|cFFFF4400'..tostring(name)..'|r:') for k, v in pairs(handler) do print(format("%32s %8s %s", (preset[k] and '|cFFFFFFFF' or '|cFFFFFF00') .. k .. '|r', type(v), tostring(v))) end T[name] = handler local trackerName = 'Veneer'..name..'Tracker' local handler = T[name] local frame = CreateFrame('Frame', trackerName, _G.VeneerObjectiveScroll, 'VeneerTrackerTemplate') frame.title:SetText(handler.displayName) frame:SetWidth(c.Width) handler.frame = frame handler.trackerName = trackerName handler.lines = {} T.orderedTrackers[index] = frame T.namedTrackers[name] = frame T.indexedTrackers[handler] = frame print('|cFFFF0088' .. trackerName .. '|r created for |cFF00FFFF' .. handler.displayName .. '|r module') T.orderedHandlers[index] = handler return true end local Event = {} Event.QUEST_LOG_UPDATE = function() return OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST + OBJECTIVE_TRACKER_UPDATE_MODULE_BONUS_OBJECTIVE end Event.QUEST_ACCEPTED = function(questLogIndex, questID) if ( IsQuestTask(questID) ) then return OBJECTIVE_TRACKER_UPDATE_TASK_ADDED, questID else if ( AUTO_QUEST_WATCH == "1" and GetNumQuestWatches() < MAX_WATCHABLE_QUESTS ) then AddQuestWatch(questLogIndex); SetSuperTrackedQuestID(questID); end return OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST end end Event.QUEST_WATCH_LIST_CHANGED = function(questID, added) if ( added ) then if ( not IsQuestTask(questID) ) then return OBJECTIVE_TRACKER_UPDATE_QUEST_ADDED, questID, added end else return OBJECTIVE_TRACKER_UPDATE_QUEST, questID, added end end Event.QUEST_POI_UPDATE = function() QuestPOIUpdateIcons(); if ( GetCVar("trackQuestSorting") == "proximity" ) then SortQuestWatches(); end return OBJECTIVE_TRACKER_UPDATE_ALL end Event.SUPER_TRACKED_QUEST_CHANGED = function() return OBJECTIVE_TRACKER_UPDATE_QUEST end Event.ZONE_CHANGED = function() local inMicroDungeon = IsPlayerInMicroDungeon(); if ( inMicroDungeon ~= T.inMicroDungeon ) then if ( not WorldMapFrame:IsShown() and GetCVarBool("questPOI") ) then SetMapToCurrentZone(); -- update the zone to get the right POI numbers for the tracker end --SortQuestWatches(); T.inMicroDungeon = inMicroDungeon; end end Event.QUEST_AUTOCOMPLETE = function(questId) AddAutoQuestPopUp(questId, "COMPLETE"); return OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST + OBJECTIVE_TRACKER_UPDATE_MODULE_AUTO_QUEST_POPUP end Event.SCENARIO_CRITERIA_UPDATE = function() return OBJECTIVE_TRACKER_UPDATE_SCENARIO end Event.SCENARIO_UPDATE = function(newStage) if ( newStage ) then return OBJECTIVE_TRACKER_UPDATE_SCENARIO_NEW_STAGE else return OBJECTIVE_TRACKER_UPDATE_SCENARIO end end Event.TRACKED_ACHIEVEMENT_UPDATE = function() return OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT end Event.TRACKED_ACHIEVEMENT_LIST_CHANGED = function(achievementID, added) if ( added ) then return OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT_ADDED, achievementID else return OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT end end Event.ZONE_CHANGED_NEW_AREA = function () if ( not WorldMapFrame:IsShown() and GetCVarBool("questPOI") ) then SetMapToCurrentZone(); -- update the zone to get the right POI numbers for the tracker end SortQuestWatches(); T.currentZoneArea = GetCurrentMapAreaID() print('Updating zone ID to', T.currentZoneArea, '=', GetZoneText(), GetMinimapZoneText()) return OBJECTIVE_TRACKER_UPDATE_TASK_ADDED end Event.PLAYER_MONEY = function() if T.watchMoneyReasons > 0 then return T.watchMoneyReasons end end Event.CRITERIA_COMPLETE = function() return OBJECTIVE_TRACKER_UPDATE_MODULE_BONUS_OBJECTIVE end Event.QUEST_TURN_IN = function(questID, xp, money) if ( IsQuestTask(questID) ) then T.Bonus:OnTurnIn(questID, xp, money) print('updating bonus modules (code', OBJECTIVE_TRACKER_UPDATE_MODULE_BONUS_OBJECTIVE, ',', questID, xp, money) return OBJECTIVE_TRACKER_UPDATE_MODULE_BONUS_OBJECTIVE, questID, xp, money else return OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST, questID, xp, money end end T.Event = Event --- Done once per ui load local BlizzHooks = { ['AddQuestWatch'] = 'AddQuestWatch', ['RemoveQuestWatch'] = 'RemoveQuestWatch', ['AbandonQuest'] = 'AbandonQuest', ['AcknowledgeAutoAcceptQuest'] = 'AcknowledgeAutoAcceptQuest', ['AddAutoQuestPopUp'] = 'AddAutoQuestPopUp', ['RemoveAutoQuestPopUp'] = 'RemoveAutoQuestPopUp', ['AddTrackedAchievement'] = 'AddTrackedAchievement', ['RemoveTrackedAchievement'] = 'RemoveTrackedAchievement', ['SetSuperTrackedQuestID'] = 'SetSuperTrackedQuestID' } local VeneerData T.SetWatchMoney = function(watchMoney, reason) if watchMoney then if band(T.watchMoneyReasons, reason) == 0 then T.watchMoneyReasons = T.watchMoneyReasons + reason; end else if band(T.watchMoneyReasons, reason) > 0 then T.watchMoneyReasons = T.watchMoneyReasons - reason; end end end T.animateReasons = 0 T.SetAnimate = function(reason) print('comparing', T.animateReasons, reason) if animate then if band(T.animateReasons, reason) == 0 then T.animateReasons = T.animateReasons + reason end else if band(T.animateReasons, reason) > 0 then T.animateReasons = T.animateReasons - reason end end end local Play = function(file) if Devian and Devian.InWorkspace() then PlaySoundFile(file) end end function T:OnEvent (event, ...) local isHandled print('OnEvent(|cFF00FF00'.. event ..'|r):', ...) local reason, arg1, arg2, arg3 if Event[event] then if type(Event[event]) == 'function' then Play([[Interface\Addons\SharedMedia_MyMedia\sound\Info.ogg]]) reason, arg1, arg2, arg3 = Event[event](...) elseif type(Event[event]) == 'table' then Play([[Interface\Addons\SharedMedia_MyMedia\sound\Link.ogg]]) for i, action in ipairs(Event[event]) do if type(action) == 'function' then reason, arg1, arg2, arg3 = action(event, ...) else reason = action end if reason then T:Update(reason, arg1, arg2, arg3) end end else Play([[Interface\Addons\SharedMedia_MyMedia\sound\Heart.ogg]]) reason = Event[event] end else Play([[Interface\Addons\SharedMedia_MyMedia\sound\Quack.ogg]]) end if reason then T:Update(reason, arg1, arg2, arg3) else print('no reason value returned') Play([[Interface\Addons\SharedMedia_MyMedia\sound\Quack.ogg]]) end end function T:OnInitialize() local c = T.Conf.Wrapper VeneerData = _G.VeneerData VeneerData.CallLog = VeneerData.CallLog or {} if not T.isHooked then T.isHooked = true for blizzFunc, veneerFunc in pairs(BlizzHooks) do if T[veneerFunc] then hooksecurefunc(blizzFunc, T[veneerFunc]) else hooksecurefunc(blizzFunc, function(...) print('|cFFFF0088securehook('..tostring(blizzFunc)..')|r args:', ...) tinsert(VeneerData.CallLog, {blizzFunc, ...}) end) end end end T.Conf.TasksLog = T.Conf.TasksLog or {} ObjectiveTrackerFrame:UnregisterAllEvents() ObjectiveTrackerFrame:Hide() for id, name in ipairs(T.orderedNames) do if not T.orderedHandlers[id] then Handler_Initialize(T.DefaultHandler, name, id) end end self:SetSize(c.Width, 40) T.InitializeWidgets() end --- Done any time the the minimize button is toggled up function T:OnEnable() print(B.Conf.VeneerObjectiveWrapper.enabled) if not B.Conf.VeneerObjectiveWrapper.enabled then return end for event, action in pairs(Event) do print('|cFFFF0088listen to', event, 'for action|r', tostring(action)) Wrapper:RegisterEvent(event) end local c = T.Conf.Wrapper Scroller:SetScrollChild(Scroll) Scroller:SetWidth(c.Width) Scroll:SetWidth(c.Width) Scroll:ClearAllPoints() Scroll:SetPoint('TOP', Scroller, 'TOP') self:SetScript('OnEvent', T.OnEvent) Scroller:Show() local from, target, to, x, y = Wrapper:GetPoint(1) print(from, target:GetName(), to, x,y) T:Update() -- run once to prime the data structure T.UpdateActionButtons() end function T:OnDisable() self:UnregisterAllEvents() Scroller:Hide() end