diff ObjectiveTracker/QuestData.lua @ 43:9480bd904f4c

- file name organizing
author Nenue
date Mon, 25 Apr 2016 13:51:58 -0400
parents ObjectiveTracker/Quests.lua@03ed70f846de
children 756e8aeb040b
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ObjectiveTracker/QuestData.lua	Mon Apr 25 13:51:58 2016 -0400
@@ -0,0 +1,577 @@
+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 fprint = B.print('Frame')
+
+
+local superTrackQuestID, playerMoney, inScenario, showPOIs
+Quest.Update = function(self, reason, ...)
+  local print = self.print
+  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(self, block)
+  local print = lprint
+  print('|cFF'..self.internalColor..'UpdateObjectives()')
+  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 displayObjectives = true
+  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')
+        self:AddLine(block, T.strings.CLICK_TO_COMPLETE, nil, 'complete')
+      end
+    else
+      if not completionText or info.completionText then
+        info.completionText = GetQuestLogCompletionText(info.logIndex)
+      end
+    end
+    self:AddLine(block, info.completionText, nil, 'complete')
+    displayObjectives = false
+    print('|cFF'..self.internalColor..'    :: complete quest :: show instruction: "'.. tostring(info.completionText) .. '"')
+  end
+
+  Default.UpdateObjectives(self, block, block_schema, displayObjectives)
+  return 0, block_schema
+end
+
+Quest.UpdateLine = function(handler, block, data)
+  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 = {}
+
+Quest.OnRemoved = function(block)
+
+end
+
+Quest.GetBlock = function(self, index)
+  local block = Default.GetBlock(self, index)
+  block:SetScript('OnEvent', Quest.OnRemoved)
+  block:RegisterEvent('QUEST_REMOVED')
+  return block
+end
+
+local GetQuestWatchIndex = GetQuestWatchIndex
+local numAnimating = 0
+local blocksChecked = {}
+
+--- 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)
+  local print = self.print
+  B.print('Block')('########')
+  B.print('Block')('########')
+  superTrackQuestID = GetSuperTrackedQuestID()
+  playerMoney = GetMoney();
+  inScenario = C_Scenario.IsInScenario();
+  showPOIs = GetCVarBool("questPOI");
+  local numAll = GetNumQuestLogEntries()
+  local numWatched = GetNumQuestWatches()
+  local bottomIndex = 1
+  print('GetNumWatched', self.name, numWatched, 'of', numAll)
+  local start, limit = 1, numAll
+
+  if id and not added then
+    -- if a particular id is supplied, add to checklist
+    if self.InfoBlock[id] then
+      blocksChecked[self.InfoBlock[id]] = self.InfoBlock[id]
+    end
+  end
+
+  numAnimating = 0
+  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)
+
+    --- Start of crazy audit flagging
+    if watchIndex and watchIndex >= bottomIndex then
+      -- do watch data pointers match?
+      local watchInfo = self.WatchInfo[watchIndex]
+      local watchBlock = self.WatchBlock[watchIndex]
+      if watchInfo and watchInfo.questID ~= questID then
+        print('GetNumWatched', 'trimming WatchInfo ['..watchIndex..'] =/=', questID)
+        self.WatchInfo[watchIndex] = nil
+      end
+      if watchBlock and watchBlock.info.questID ~= questID then
+        print('GetNumWatched', 'trimming WatchBlock ['..watchIndex..'] =/=', watchBlock:GetName())
+        self.WatchBlock[watchIndex] = nil
+        blocksChecked[watchBlock] = watchBlock
+      end
+    end
+
+    -- check log-block pointer
+    local logBlock = self.LogBlock[logIndex]
+    if logBlock then
+      -- check later that the block isn't for a dropped quest
+      if logBlock.info.questID ~= questID then
+        print('GetQuests', 'replace info', logBlock.info.questID, '->', questID)
+        self.LogBlock[logIndex] = nil
+        blocksChecked[logBlock] = logBlock
+      end
+    end
+    --- end of crazy audit flagging
+
+    -- add to watch index if: the questID is non-zero
+    if questID ~= 0 then
+      self.Info[questID] = self:GetInfo(logIndex, watchIndex)
+      print('GetQuests', format('request info |cFF00FF00%2d|r |cFFFFFF00%6d|r |cFFFF4400%3s|r', logIndex, questID, tostring(watchIndex or '')))
+    end
+  end
+
+  --- After GetInfo pass, look for any non-conformant blocks and deal with them
+  for _, block in pairs(blocksChecked) do
+    local logIndex = GetQuestLogIndexByID(block.info.questID, 'player')
+    -- animating blocks have been evaluated
+    if not block.isAnimating then
+      if not logIndex then
+        self:ClearBlock(block)
+      elseif not IsQuestWatched(block.info.logIndex) then
+        self:ClearBlock(block)
+      end
+    end
+    blocksChecked[block] = nil
+  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('GetNumWatched', 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)
+
+    -- ensure that the entry being populated isn't animating
+    if self.WatchBlock[watchIndex] then
+      local checkIndex = watchIndex
+      while self.WatchBlock[checkIndex + numAnimating] and self.WatchBlock[checkIndex + numAnimating].isAnimating do
+        print(self.WatchBlock[checkIndex + numAnimating]:GetName(), 'is in an animation sequence')
+        numAnimating = numAnimating + 1
+      end
+    end
+
+    self.WatchList[watchIndex + numAnimating] = q
+    --tprint('      |cFF88FF00GetInfo:|r set watch entry', watchIndex)
+    print('GetInfo:', 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('QuestFlags', (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
+    self.print('    QuestInfo', title, questType, isAutoComplete)
+    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('QuestStatus', temp_status, '|cFF00FF00questLogIndex|r:', logIndex, title)
+    local temp  ={}
+    local data_txt = '|cFF'..self.internalColor..'values:|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('DataStatus',data_txt)
+    sort(temp, function(a,b) return a < b end)
+    for i, k in ipairs(temp) do
+      iprint('GetInfo', questID, ''..k..'|r')
+      for kk,v in pairs(q[k]) do
+        iprint('GetInfo', questID, kk, '=', v)
+      end
+    end
+  end
+
+  return q
+end
+
+Quest.GetObjectives = function(logIndex, numObjectives, isComplete, isSequenced, isStory)
+  local print = Quest.print
+  local objectives = {}
+  if not logIndex then
+    return
+  end
+
+  for i = 1, numObjectives do
+    local text, type, finished = GetQuestLogLeaderBoard(i, logIndex)
+
+    local progress = 0
+    if finished then
+      progress = 1
+    elseif text then
+      local quantity, maxQuantity = text:match('^(%d+)/(%d+)')
+      if quantity and maxQuantity then
+        progress = quantity / maxQuantity
+        --print('GetObjectives', 'calculated objective progress:', quantity, '/', maxQuantity, '=', progress)
+      end
+    end
+
+    print('GetObjectives', format('|cFF88FF88#%d %s %s %s', i, tostring(type), tostring(text), tostring(finished)), '('.. tostring(progress)..')')
+
+
+    objectives[i] = {
+      index = i,
+      type = type,
+      text = text,
+      finished = finished,
+      progress = progress
+    }
+  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