Mercurial > wow > buffalo2
view ObjectiveTracker/Quests.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 | 69d03f8e293e |
children | 1f8f9cc3d956 |
line wrap: on
line source
local B = select(2,...).frame local T = B:RegisterModule("ObjectiveTracker", _G.VeneerObjectiveWrapper, 'BuffFrame') local _G, ipairs, max, min, unpack, floor, pairs, tostring, type, band = _G, ipairs, max, min, unpack, floor, pairs, tostring, type, bit.band local GetQuestWatchInfo, GetQuestLogCompletionText = GetQuestWatchInfo, GetQuestLogCompletionText local GetQuestLogLeaderBoard, GetNumQuestLogEntries, GetQuestLogTitle = GetQuestLogLeaderBoard, GetNumQuestLogEntries, GetQuestLogTitle local GetQuestLogSpecialItemInfo, GetQuestLogSpecialItemCooldown = GetQuestLogSpecialItemInfo, GetQuestLogSpecialItemCooldown local GetSuperTrackedQuestID, GetMoney, C_Scenario, GetCVarBool, GetNumQuestWatches = GetSuperTrackedQuestID, GetMoney, C_Scenario, GetCVarBool, GetNumQuestWatches local GetQuestTagInfo, GetMoneyString, GetDistanceSqToQuest, GetQuestFactionGroup = GetQuestTagInfo, GetMoneyString, GetDistanceSqToQuest, GetQuestFactionGroup local QUEST_TAG_ACCOUNT, LE_QUEST_FACTION_HORDE, LE_QUEST_FREQUENCY_DAILY, LE_QUEST_FREQUENCY_WEEKLY = QUEST_TAG_ACCOUNT, LE_QUEST_FACTION_HORDE, LE_QUEST_FREQUENCY_DAILY, LE_QUEST_FREQUENCY_WEEKLY local QUEST_TAG_TCOORDS, IsQuestSequenced = QUEST_TAG_TCOORDS, IsQuestSequenced local Default, Quest = T.DefaultHandler, T.Quest local format, wipe, select = format, table.wipe, select local wipeall = B.wipeall local lprint, iprint, tprint = B.print('Line'), B.print('Info'), B.print('Tracker') local print = tprint local superTrackQuestID, playerMoney, inScenario, showPOIs Quest.Update = function(self, reason, ...) local print = tprint print('QuestTracker:Update() received') T.UpdateActionButtons() Default.Update(self, reason, ...) end T.Quest.numButtons = 0 local usedButtons = T.Quest.itemButtons local freeButtons = T.Quest.freeButtons Quest.UpdateObjectives = function(handler, block) local print = lprint print('|cFF00FFFFUpdateObjectives()') local info = block.info print((info.isAccount and 'isAccount' or ''), (info.isFaction and 'isFaction' or ''), (info.isDaily and 'isDaily' or ''), (info.isWeekly and 'isWeekly' or ''), info.tagID, info.tagName) local block_schema = 'default' if info.isAccount then if info.isFaction then print(' faction', info.tagID) block_schema = 'faction_'..info.tagID else print(' account', info.isAccount, info.isFaction) block_schema = 'account' end elseif info.isDaily then print(' daily', info.frequency) block_schema = 'daily' elseif info.isWeekly then print(' weekly', info.frequency) block_schema = 'weekly' end local completionText if info.isComplete then if info.isAutoComplete then local questID, popupType = GetAutoQuestPopUp(info.logIndex) if popupType == 'COMPLETE' then print(' :: auto-complete quest :: set the message') info.completionText = T.strings.CLICK_TO_COMPLETE end else if not completionText or info.completionText then info.completionText = GetQuestLogCompletionText(info.logIndex) end end print(' :: complete quest :: show instruction: "'.. tostring(info.completionText) .. '"') end Default.UpdateObjectives(handler, block, block_schema) return block_schema end Quest.UpdateLine = function(handler, block, line, data) local print = lprint local objectiveType = data.type return data.text, nil, objectiveType end ----------------------------- --- QUEST local tremove, tinsert = tremove, tinsert local GetQuestLogIndexByID, IsQuestWatched = GetQuestLogIndexByID, IsQuestWatched Quest.QuestBlock = {} Quest.LogBlock = {} Quest.LogInfo = {} function Quest:FreeBlock (block) local used = Quest.usedBlocks local free = Quest.freeBlocks local reason = '' local doRelease = false local info = block.info local questID = info.questID local logIndex = info.logIndex if info.posIndex then if used[info.posIndex] == block then doRelease = true reason = 'posIndex mismatch' end elseif logIndex then if not IsQuestWatched(logIndex) then reason = 'not being watched' elseif not self.LogBlock[logIndex] then doRelease = true reason = 'missing logBlock entry' elseif (self.LogBlock[logIndex] ~= block) then doRelease = true reason = 'different block using index' end elseif info.questID then if not GetQuestLogIndexByID(info.questID) then doRelease = true reason = 'no identifiable quest log entry' end end if doRelease then print(' |cFF00FF00FreeBlock (' .. block:GetName() .. '):', reason) block:Hide() tremove(used, info.posIndex) tinsert(free, block) end end local watchesChecked = {} local infosChecked = {} local blocksChecked = {} local GetQuestWatchIndex = GetQuestWatchIndex --- Get a total of things to show, and straighten out the index while we're at it --- Return the number shown, total in log, and the info table to parse Quest.GetNumWatched = function (self, id, added) superTrackQuestID = GetSuperTrackedQuestID() playerMoney = GetMoney(); inScenario = C_Scenario.IsInScenario(); showPOIs = GetCVarBool("questPOI"); local numAll = GetNumQuestLogEntries() local numWatched = GetNumQuestWatches() local bottomIndex = 1 print(' |cFF00FF88GetNumWatched:|r',self.name, numWatched, 'of', numAll) local start, limit = 1, numAll --- start a list of blocks affected by this function wipe(blocksChecked) if id and not added then if self.InfoBlock[id] then tinsert(blocksChecked, self.InfoBlock[id]) end end for logIndex = start, limit do local reason1, reason2 = '', '' local title, level, suggestedGroup, isHeader, isCollapsed, isComplete, frequency, questID, startEvent, displayQuestID, isOnMap, hasLocalPOI, isTask, isStory = GetQuestLogTitle(logIndex) local watchIndex = GetQuestWatchIndex(logIndex) if watchIndex and watchIndex >= bottomIndex then local watchInfo = self.WatchInfo[watchIndex] local watchBlock = self.WatchBlock[watchIndex] if watchInfo and watchInfo.questID ~= questID then print(' |cFFBBFF00GetNumWatched: trimming WatchInfo ['..watchIndex..'] =/=', questID) self.WatchInfo[watchIndex] = nil end if watchBlock and watchBlock.info.questID ~= questID then print(' |cFFBBFF00GetNumWatched: trimming WatchBlock ['..watchIndex..'] =/=', watchBlock:GetName()) self.WatchBlock[watchIndex] = nil tinsert(blocksChecked, watchBlock) end end local logBlock = self.LogBlock[logIndex] if logBlock and logBlock.info.questID ~= questID then --print(' |cFFBBFF00GetNumWatched: trimming LogBlock ['..logIndex..'] =/=', logBlock:GetName()) self.LogBlock[logIndex] = nil tinsert(blocksChecked, logBlock) end if questID ~= 0 then self.Info[questID] = self:GetInfo(logIndex, watchIndex) --print(' |cFF44BBFFGetNumWatched:|r map', questID, 'to', logIndex, (watchIndex and ('('..watchIndex..')') or '')) end end --- remove any orphaned blocks from view and, if possible, free it for re-use for i, block in ipairs(blocksChecked) do if not GetQuestLogIndexByID(block.info.questID, 'player') then print(' |cFFBBFF00GetNumWatched:|r literating a block without an index |cFFBBFF00'.. block:GetName()..'|r') block:Hide() self:FreeBlock(block) end if not IsQuestWatched(block.info.logIndex) then print(' |cFFBBFF00GetNumWatched:|r hiding untracked quest |cFFBBFF00'.. block:GetName()..'|r') block:Hide() end end self.numWatched = numWatched self.numAll = numAll return numWatched, numAll, self.WatchList end --- Returns an iterable table from which tracker blocks can be filled out. Data includes: -- All entry-layer GetXInfo return values -- Manifest of line data to be displayed in relation to the tracked object Quest.GetInfo = function (self, logIndex, watchIndex) local print = iprint local title, level, suggestedGroup, isHeader, isCollapsed, isComplete, frequency, questID, startEvent, displayQuestID, isOnMap, hasLocalPOI, isTask, isStory = GetQuestLogTitle(logIndex) if ( not questID ) then tprint(' |cFFFF0088GetInfo:|r', logIndex, watchIndex, '|cFFFF2299no data|r') return end Quest.Info[questID] = Quest.Info[questID] or {} local q = Quest.Info[questID] q.questID = questID q.id = questID q.logIndex = logIndex q.watchIndex = watchIndex local numObjectives, requiredMoney, isAutoComplete, failureTime, timeElapsed, questType = 0, 0, nil, false, false, 0 if watchIndex then local _ _,_,_, numObjectives, requiredMoney, _, _, isAutoComplete, failureTime, timeElapsed, questType = GetQuestWatchInfo(watchIndex) self.WatchList[watchIndex] = q --tprint(' |cFF88FF00GetInfo:|r set watch entry', watchIndex) tprint(' |cFFFFBB00GetInfo:|r', logIndex, watchIndex, '|cFFFF2299'..title..'|r') end self.LogInfo[logIndex] = q q.numObjectives = numObjectives q.requiredMoney = requiredMoney q.failureTime = failureTime q.timeElapsed = timeElapsed q.type = 'Quest' q.title = title q.level = level q.displayQuestID = displayQuestID q.suggestedGroup = suggestedGroup -- re-use Blizzard logic for consistency local showQuest = true if isTask then showQuest = false end local watchMoney = false; local tagID, typeTag, frequencyTag, completionTag, completionText local isAccount, isFaction, isWeekly, isDaily = false, false, false, false local isBreadcrumb = false local questFailed = false local watchMoney = false local timerInfo, moneyInfo = false, false local objectives = q.objectives or {} -- Case 1: completed quest or "go to thing" breadcrumb -- * 1 line containing the completion text if ( isComplete and isComplete < 0 ) then isComplete = false questFailed = true elseif ( numObjectives == 0 and playerMoney >= requiredMoney and not startEvent ) then isComplete = true; questFailed = false if ( requiredMoney == 0 ) then isBreadcrumb = true; end end print('|cFF0088FFflags:|r', (isComplete and 'isComplete' or ''), (questFailed and 'questFailed' or ''), (isBreadcrumb and 'isBreadcrumb' or '')) -- completion message? local isSequenced = IsQuestSequenced(questID) local temp_status = '' if ( isComplete ) then temp_status = 'COMPLETED_OBJECTIVES' objectives = Quest.GetObjectives(questLogIndex, numObjectives, true, isSequenced, isStory) if ( isAutoComplete ) then temp_status = 'AUTOCOMPLETE_OBJECTIVES' completionText = _G.QUEST_WATCH_CLICK_TO_COMPLETE else if ( isBreadcrumb ) then temp_status = 'COMPLETE_BREADCRUMB' completionText = GetQuestLogCompletionText(questLogIndex) else temp_status = 'COMPLETE_READY_FOR_TURN_IN' completionText = _G.QUEST_WATCH_QUEST_READY end end elseif ( questFailed ) then temp_status = 'FAILED' -- Case 2: failed quest -- * 1 status line; hide other info completionText = _G.FAILED else temp_status = 'PROGRESS_OBJECTIVES' -- Case 3: quest in progress -- * Multiple objective lines -- * Possible extra lines for money and timer data respectively objectives = Quest.GetObjectives(logIndex, numObjectives, false, isSequenced, isStory) q.objectives = objectives --- anything past here gets appended to existing objectives -- money if ( requiredMoney > playerMoney ) then temp_status = temp_status .. '_MONEY' local text = GetMoneyString(playerMoney).." / "..GetMoneyString(requiredMoney); moneyInfo = { type = 'money', text = text, finished = false, requiredMoney = requiredMoney, playerMoney = playerMoney, } tinsert(objectives, moneyInfo) end -- time limit if ( failureTime ) then temp_status = temp_status .. '_TIMED' if ( timeElapsed and timeElapsed <= failureTime ) then timerInfo = { type = 'timer', finished = false, timeElapsed = timeElapsed, failureTime = failureTime, } tinsert(objectives, timerInfo) end end end q.objectives = objectives q.moneyInfo = moneyInfo q.timerInfo = timerInfo q.completionText = completionText -- POI data local POI = false if ( showPOIs ) then POI = { questID = questID, logIndex = logIndex, watchIndex = watchIndex } local poiButton; if ( hasLocalPOI ) then if ( isComplete ) then POI.type = 'normal' else POI.type = 'numeric' end elseif ( isComplete ) then POI.type = 'remote' end local distance, onContinent = GetDistanceSqToQuest(logIndex) if distance ~= nil and distance > 0 then POI.distance = distance POI.onContinent = onContinent end end q.POI = POI --- Block Tags -- completionTag - in progres, complete, failed, autocomplete -- typeTag - account, faction, pvp, dungeon, group -- frequencyTag - daily/weekly local schema = 'default' local questTagID, tagName = GetQuestTagInfo(questID) local tagInfo = {} local tagCoords = {} local factionGroup = GetQuestFactionGroup(questID); if( questTagID and questTagID == QUEST_TAG_ACCOUNT ) then if( factionGroup ) then tagID = "ALLIANCE" schema = 'alliance' if ( factionGroup == LE_QUEST_FACTION_HORDE ) then tagID = "HORDE" schema = 'horde' end isFaction = true else tagID = QUEST_TAG_ACCOUNT isAccount = true end tagInfo['typeTag'] = tagID tagCoords['typeTag'] = QUEST_TAG_TCOORDS[tagID] elseif ( factionGroup) then tagID = "ALLIANCE" if ( factionGroup == LE_QUEST_FACTION_HORDE ) then tagID = "HORDE" end isFaction = true tagInfo['typeTag'] = tagID tagCoords['typeTag'] = QUEST_TAG_TCOORDS[tagID] end if( frequency == LE_QUEST_FREQUENCY_DAILY and (not isComplete or isComplete == 0) ) then tagID = 'DAILY' tagInfo['frequencyTag'] = tagID tagCoords['frequencyTag'] = QUEST_TAG_TCOORDS[tagID] isDaily = true schema = 'daily' elseif( frequency == LE_QUEST_FREQUENCY_WEEKLY and (not isComplete or isComplete == 0) )then tagID = 'WEEKLY' tagInfo['frequencyTag'] = tagID tagCoords['frequencyTag'] = QUEST_TAG_TCOORDS[tagID] isWeekly = true schema = 'weekly' elseif( questTagID ) then tagID = questTagID end if( isComplete ) then tagInfo['completionTag'] = 'COMPLETED' elseif ( questFailed ) then tagInfo['completionTag'] = 'FAILED' end tagCoords['completionTag'] = QUEST_TAG_TCOORDS[tagInfo['completionTag']] q.tagInfo = tagInfo q.tagCoords = tagCoords -- establishes the primary block tag for view compacting q.tagID = tagID q.tagName = tagName -- action button information local link, icon, charges = GetQuestLogSpecialItemInfo(logIndex) local start, duration, enable = GetQuestLogSpecialItemCooldown(logIndex) if link or icon or charges then q.specialItem = { questID = questID, logIndex = questLogIndex, link = link, charges = charges, icon = icon, start = start, duration = duration, enable = enable, } end if moneyInfo or timerInfo then numObjectives = #objectives end -- raw data q.isComplete = isComplete q.startEvent = startEvent q.isAutoComplete = isAutoComplete q.questType = questType q.isTask = isTask q.isStory = isStory q.isOnMap = isOnMap q.hasLocalPOI = hasLocalPOI q.frequency = frequency q.isComplete = isComplete q.isStory = isStory q.isTask = isTask q.statusKey = temp_status q.selected = (questID == superTrackQuestID) T.SetRewards(q, questID) q.questID = questID q.logIndex = logIndex q.watchIndex = watchIndex q.id = questID q.schema = schema if Devian and Devian.InWorkspace() then print('|cFF00DDFFstatus:|r', temp_status, '|cFF00FF00questLogIndex|r:', logIndex, title) local temp ={} local data_txt = '|cFFFF4400values:|r' for k,v in pairs(q) do if type(v) =='number' then data_txt = data_txt .. ' |cFFFFFF00'..k..'|r: ' .. tostring(v) elseif type(v) == 'table' then tinsert(temp, k) end end print(data_txt) sort(temp, function(a,b) return a < b end) for i, k in ipairs(temp) do print('|cFF00FF00'..k..'|r') for kk,v in pairs(q[k]) do print(' ', kk, '=', v) end end end return q end Quest.GetObjectives = function(logIndex, numObjectives, isComplete, isSequenced, isStory) local objectives = {} for i = 1, numObjectives do local text, type, finished = GetQuestLogLeaderBoard(i, logIndex) print(format(' |cFFFF4400GetObjectives:|r #%d %s %s %s', i, tostring(type), tostring(text), tostring(finished))) objectives[i] = { index = i, type = type, text = text, finished = finished } end return objectives end local huge, sqrt = math.huge, math.sqrt Quest.GetClosest = function() local minID, minTitle local minDist = huge local numQuests = GetNumQuestLogEntries() for questIndex = 1, numQuests do local distance, onContinent = GetDistanceSqToQuest(questIndex) local title, level, _, _, _, _, _, _, questID = GetQuestLogTitle(questIndex) if onContinent and distance < minDist then minDist = distance minTitle = title minID = questID end end print('nearest quest is', minTitle, 'by', sqrt(minDist)) return minID, minTitle, minDist end Quest.OnTurnIn = function(self, questID, xp, money) end Quest.Select = function (handler, block) if block.info.isAutoComplete and block.info.isComplete then ShowQuestComplete(block.info.logIndex) else SetSuperTrackedQuestID(block.info.questID) end end Quest.Link = function(handler, block) local questLink = GetQuestLink(block.info.logIndex); if ( questLink ) then ChatEdit_InsertLink(questLink); end end Quest.Open = function(handler, block) QuestMapFrame_OpenToQuestDetails(block.info.questID) end Quest.Remove = function(handler, block) print('removing', block.info.logIndex, 'from watcher') RemoveQuestWatch(block.info.logIndex) end