Compare commits
7 Commits
7ecc9016dd
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 30fe4b83a9 | |||
| 26a135a2ca | |||
| cf67ceb474 | |||
| de270a0e8e | |||
| c6019ea823 | |||
| 706fc0b8ad | |||
| c2b1b82b2f |
+43
-5
@@ -1785,16 +1785,36 @@ function QuestieDB.GetQuest(questId, ...) -- /dump QuestieDB.GetQuest(867)
|
|||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
-- Build a minimal live-fallback quest from the quest log so the tracker still works
|
-- Build a minimal live-fallback quest from the quest log so the tracker still works.
|
||||||
|
-- The override that got us here is partial by nature -- a Learner record of a single
|
||||||
|
-- field, a correction -- but whatever it does carry beats guessing, and the quest object
|
||||||
|
-- built here is cached for the session, so anything left blank stays blank.
|
||||||
local logEntry = QuestLogCache.GetQuest(questId)
|
local logEntry = QuestLogCache.GetQuest(questId)
|
||||||
if not logEntry then return nil end
|
if not logEntry then return nil end
|
||||||
|
local function NonEmpty(text)
|
||||||
|
if text and text ~= "" then return text end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local overrideName = NonEmpty(overrideData[QuestieDB.questKeys.name])
|
||||||
|
local overrideLevel = overrideData[QuestieDB.questKeys.questLevel]
|
||||||
|
local overrideZone = overrideData[QuestieDB.questKeys.zoneOrSort]
|
||||||
|
local cachedTitle = NonEmpty(logEntry.title)
|
||||||
|
local liveTitle
|
||||||
|
if (not overrideName) and (not cachedTitle) and GetQuestLogIndexByID and GetQuestLogTitle then
|
||||||
|
-- QuestLogCache is a snapshot and can be missing the title of a quest the client
|
||||||
|
-- has since filled in, which is what leaves a quest showing as its own id.
|
||||||
|
local questLogIndex = GetQuestLogIndexByID(questId)
|
||||||
|
if questLogIndex and questLogIndex > 0 then
|
||||||
|
liveTitle = NonEmpty(GetQuestLogTitle(questLogIndex))
|
||||||
|
end
|
||||||
|
end
|
||||||
local fallback = {
|
local fallback = {
|
||||||
Id = questId,
|
Id = questId,
|
||||||
name = logEntry.title or tostring(questId),
|
name = overrideName or cachedTitle or liveTitle or tostring(questId),
|
||||||
level = logEntry.level or 0,
|
level = overrideLevel or logEntry.level or 0,
|
||||||
questLevel = logEntry.level or 0,
|
questLevel = overrideLevel or logEntry.level or 0,
|
||||||
requiredLevel = 0,
|
requiredLevel = 0,
|
||||||
zoneOrSort = 0,
|
zoneOrSort = overrideZone or 0,
|
||||||
questFlags = 0,
|
questFlags = 0,
|
||||||
specialFlags = 0,
|
specialFlags = 0,
|
||||||
Starts = { CreatureStarts = {}, ObjectStarts = {}, ItemStarts = {} },
|
Starts = { CreatureStarts = {}, ObjectStarts = {}, ItemStarts = {} },
|
||||||
@@ -1916,6 +1936,24 @@ function QuestieDB.GetQuest(questId, ...) -- /dump QuestieDB.GetQuest(867)
|
|||||||
if learnerRecord.objIndex and not QO.objIndex then QO.objIndex = learnerRecord.objIndex end
|
if learnerRecord.objIndex and not QO.objIndex then QO.objIndex = learnerRecord.objIndex end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- A record that never captured a name -- a Learner entry written from an objective update, an
|
||||||
|
-- override carrying a single key -- leaves QO.name nil, and this object is cached for the rest
|
||||||
|
-- of the session, so everything downstream ends up printing the quest id. Ask the client.
|
||||||
|
if ((not QO.name) or QO.name == "") and GetQuestLogIndexByID and GetQuestLogTitle then
|
||||||
|
local questLogIndex = GetQuestLogIndexByID(questId)
|
||||||
|
if questLogIndex and questLogIndex > 0 then
|
||||||
|
local logTitle = GetQuestLogTitle(questLogIndex)
|
||||||
|
if logTitle and logTitle ~= "" then
|
||||||
|
QO.name = logTitle
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Same story for the zone: partial records leave it nil, and callers all treat it as a number
|
||||||
|
-- (`quest.zoneOrSort > 0` errors outright on nil). 0 is the value everything already reads as
|
||||||
|
-- "no zone on file", which sends the tracker to the quest log for one.
|
||||||
|
QO.zoneOrSort = QO.zoneOrSort or 0
|
||||||
|
|
||||||
local questLevel, requiredLevel = QuestieLib.GetTbcLevel(questId)
|
local questLevel, requiredLevel = QuestieLib.GetTbcLevel(questId)
|
||||||
QO.level = questLevel
|
QO.level = questLevel
|
||||||
QO.requiredLevel = requiredLevel
|
QO.requiredLevel = requiredLevel
|
||||||
|
|||||||
@@ -553,6 +553,10 @@ function _QuestEventHandler:QuestLogUpdate()
|
|||||||
doFullQuestLogScan = false
|
doFullQuestLogScan = false
|
||||||
-- Function call updates doFullQuestLogScan. Order matters.
|
-- Function call updates doFullQuestLogScan. Order matters.
|
||||||
_QuestEventHandler:UpdateAllQuests()
|
_QuestEventHandler:UpdateAllQuests()
|
||||||
|
-- Also on this path: UpdateAllQuests only looks at quests still in the log, so a removal
|
||||||
|
-- that fired no event of its own would sit there unnoticed for as long as full scans keep
|
||||||
|
-- being asked for.
|
||||||
|
_QuestEventHandler:CleanupRemovedQuestsFallback()
|
||||||
else
|
else
|
||||||
_QuestEventHandler:CleanupRemovedQuestsFallback()
|
_QuestEventHandler:CleanupRemovedQuestsFallback()
|
||||||
QuestieCombatQueue:Queue(function()
|
QuestieCombatQueue:Queue(function()
|
||||||
@@ -666,7 +670,9 @@ function _QuestEventHandler:CleanupRemovedQuestsFallback()
|
|||||||
if QuestiePlayer and QuestiePlayer.currentQuestlog then
|
if QuestiePlayer and QuestiePlayer.currentQuestlog then
|
||||||
local removedQuestIds = {}
|
local removedQuestIds = {}
|
||||||
for questId in pairs(QuestiePlayer.currentQuestlog) do
|
for questId in pairs(QuestiePlayer.currentQuestlog) do
|
||||||
if questId and questId > 0 and (not gameQuestIds[questId]) then
|
-- Typed check: a stray string key (saved variables have produced them) would other-
|
||||||
|
-- wise error on the comparison and take the whole pass down with it.
|
||||||
|
if type(questId) == "number" and questId > 0 and (not gameQuestIds[questId]) then
|
||||||
removedQuestIds[#removedQuestIds + 1] = questId
|
removedQuestIds[#removedQuestIds + 1] = questId
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -679,7 +685,12 @@ function _QuestEventHandler:CleanupRemovedQuestsFallback()
|
|||||||
local wasTurnedIn = questLog[questId] and questLog[questId].state == QUEST_LOG_STATES.QUEST_TURNED_IN
|
local wasTurnedIn = questLog[questId] and questLog[questId].state == QUEST_LOG_STATES.QUEST_TURNED_IN
|
||||||
local wasAlreadyComplete = Questie.db.char.complete and Questie.db.char.complete[questId]
|
local wasAlreadyComplete = Questie.db.char.complete and Questie.db.char.complete[questId]
|
||||||
local completeAtRemoval = QuestieDB.IsComplete(questId)
|
local completeAtRemoval = QuestieDB.IsComplete(questId)
|
||||||
local shouldComplete = wasTurnedIn or wasAlreadyComplete or completeAtRemoval == 1
|
-- The server's own record, and the only one that knows anything about a quest the
|
||||||
|
-- database has never heard of: QuestieDB.IsComplete cannot answer for those, so an
|
||||||
|
-- Ascension quest the server finished by itself would otherwise be filed as abandoned.
|
||||||
|
local serverFlaggedComplete = IsQuestFlaggedCompleted and IsQuestFlaggedCompleted(questId)
|
||||||
|
local shouldComplete = wasTurnedIn or wasAlreadyComplete or completeAtRemoval == 1 or
|
||||||
|
serverFlaggedComplete
|
||||||
|
|
||||||
QuestLogCache.RemoveQuest(questId)
|
QuestLogCache.RemoveQuest(questId)
|
||||||
QuestieQuest:SetObjectivesDirty(questId)
|
QuestieQuest:SetObjectivesDirty(questId)
|
||||||
|
|||||||
@@ -63,6 +63,22 @@ local function GetWrappedWidth(label)
|
|||||||
return label:GetWidth()
|
return label:GetWidth()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- A quest built from partial data can reach the tracker with no name, or with the id standing in
|
||||||
|
-- for one, and it is cached that way for the session. Ask the quest log before printing a number.
|
||||||
|
local function GetDisplayableQuestName(quest)
|
||||||
|
local questName = quest.name
|
||||||
|
|
||||||
|
if (not questName) or questName == "" or questName == tostring(quest.Id) then
|
||||||
|
questName = TrackerUtils:GetQuestLogTitleById(quest.Id) or questName
|
||||||
|
end
|
||||||
|
|
||||||
|
if (not questName) or questName == "" then
|
||||||
|
questName = tostring(quest.Id)
|
||||||
|
end
|
||||||
|
|
||||||
|
return questName
|
||||||
|
end
|
||||||
|
|
||||||
local LSM30 = LibStub and LibStub("LibSharedMedia-3.0", true)
|
local LSM30 = LibStub and LibStub("LibSharedMedia-3.0", true)
|
||||||
|
|
||||||
-- Local Vars
|
-- Local Vars
|
||||||
@@ -845,11 +861,7 @@ function QuestieTracker:Update()
|
|||||||
-- Setup local QuestieTracker:Update vars
|
-- Setup local QuestieTracker:Update vars
|
||||||
local trackerFontSizeZone = Questie.db.profile.trackerFontSizeZone
|
local trackerFontSizeZone = Questie.db.profile.trackerFontSizeZone
|
||||||
local trackerFontSizeQuest = Questie.db.profile.trackerFontSizeQuest
|
local trackerFontSizeQuest = Questie.db.profile.trackerFontSizeQuest
|
||||||
-- The supertrack button sits at the very left of a quest line, so the whole quest list is
|
local questMarginLeft = (trackerMarginLeft + trackerMarginRight) - (18 - trackerFontSizeQuest)
|
||||||
-- indented past it: otherwise it is clipped by the tracker's left edge and collides with the
|
|
||||||
-- quest item buttons, which share that gutter.
|
|
||||||
local superTrackMarginLeft = TrackerLinePool.GetSuperTrackMarginReserve()
|
|
||||||
local questMarginLeft = (trackerMarginLeft + trackerMarginRight) - (18 - trackerFontSizeQuest) + superTrackMarginLeft
|
|
||||||
local objectiveMarginLeft = questMarginLeft + trackerFontSizeQuest
|
local objectiveMarginLeft = questMarginLeft + trackerFontSizeQuest
|
||||||
local questItemButtonSize = 12 + trackerFontSizeQuest
|
local questItemButtonSize = 12 + trackerFontSizeQuest
|
||||||
local objectiveColor = Questie.db.profile.trackerColorObjectives
|
local objectiveColor = Questie.db.profile.trackerColorObjectives
|
||||||
@@ -899,11 +911,9 @@ function QuestieTracker:Update()
|
|||||||
line.criteriaMark:Hide()
|
line.criteriaMark:Hide()
|
||||||
line.playButton:Hide()
|
line.playButton:Hide()
|
||||||
|
|
||||||
-- Setup Zone Label. Indented like the quest lines below it: the supertrack
|
-- Setup Zone Label
|
||||||
-- buttons overflow their own line at larger sizes, and the zone label is the
|
|
||||||
-- only text that would otherwise share that gutter with them.
|
|
||||||
line.label:ClearAllPoints()
|
line.label:ClearAllPoints()
|
||||||
line.label:SetPoint("TOPLEFT", line, "TOPLEFT", superTrackMarginLeft, 0)
|
line.label:SetPoint("TOPLEFT", line, "TOPLEFT", 0, 0)
|
||||||
|
|
||||||
-- Set Zone Title and default Min/Max states
|
-- Set Zone Title and default Min/Max states
|
||||||
if Questie.db.char.collapsedZones[zoneName] then
|
if Questie.db.char.collapsedZones[zoneName] then
|
||||||
@@ -930,19 +940,19 @@ function QuestieTracker:Update()
|
|||||||
|
|
||||||
-- Check and measure Zone Label text width and update tracker width
|
-- Check and measure Zone Label text width and update tracker width
|
||||||
QuestieTracker:UpdateWidth(line.label:GetStringWidth() + trackerMarginLeft +
|
QuestieTracker:UpdateWidth(line.label:GetStringWidth() + trackerMarginLeft +
|
||||||
superTrackMarginLeft + trackerMarginRight)
|
trackerMarginRight)
|
||||||
|
|
||||||
-- Set Zone Label and Line widths
|
-- Set Zone Label and Line widths
|
||||||
line.label:SetWidth(trackerBaseFrame:GetWidth() - trackerMarginLeft - superTrackMarginLeft - trackerMarginRight)
|
line.label:SetWidth(trackerBaseFrame:GetWidth() - trackerMarginLeft - trackerMarginRight)
|
||||||
line:SetWidth(line.label:GetWidth() + superTrackMarginLeft)
|
line:SetWidth(line.label:GetWidth())
|
||||||
|
|
||||||
-- Compare largest text Label in the tracker with current Label, then save widest width
|
-- Compare largest text Label in the tracker with current Label, then save widest width
|
||||||
trackerLineWidth = math.max(trackerLineWidth,
|
trackerLineWidth = math.max(trackerLineWidth,
|
||||||
line.label:GetStringWidth() + trackerMarginLeft + superTrackMarginLeft)
|
line.label:GetStringWidth() + trackerMarginLeft)
|
||||||
|
|
||||||
-- Setup Min/Max Button
|
-- Setup Min/Max Button
|
||||||
line.expandZone:ClearAllPoints()
|
line.expandZone:ClearAllPoints()
|
||||||
line.expandZone:SetPoint("TOPLEFT", line, "TOPLEFT", superTrackMarginLeft, 0)
|
line.expandZone:SetPoint("TOPLEFT", line, "TOPLEFT", 0, 0)
|
||||||
line.expandZone:SetWidth(line.label:GetWidth())
|
line.expandZone:SetWidth(line.label:GetWidth())
|
||||||
line.expandZone:SetHeight(line.label:GetHeight())
|
line.expandZone:SetHeight(line.label:GetHeight())
|
||||||
line.expandZone:Show()
|
line.expandZone:Show()
|
||||||
@@ -967,6 +977,10 @@ function QuestieTracker:Update()
|
|||||||
-- Safety check - make sure we didn't run over our linePool limit.
|
-- Safety check - make sure we didn't run over our linePool limit.
|
||||||
if not line then return "BREAK" end
|
if not line then return "BREAK" end
|
||||||
|
|
||||||
|
-- Kept so the supertrack button, which lives on the title line, can be
|
||||||
|
-- re-centred over the finished quest block further down.
|
||||||
|
local questTitleLine = line
|
||||||
|
|
||||||
-- Set Line Mode, Types, Clickers
|
-- Set Line Mode, Types, Clickers
|
||||||
line:SetMode("quest")
|
line:SetMode("quest")
|
||||||
line:SetOnClick("quest")
|
line:SetOnClick("quest")
|
||||||
@@ -976,7 +990,7 @@ function QuestieTracker:Update()
|
|||||||
line.criteriaMark:Hide()
|
line.criteriaMark:Hide()
|
||||||
|
|
||||||
-- Set Min/Max Button and default states
|
-- Set Min/Max Button and default states
|
||||||
line.expandQuest:SetPoint("TOPRIGHT", line, "TOPLEFT", questMarginLeft - 8, 1)
|
line.expandQuest:SetPoint("TOPRIGHT", line, "TOPLEFT", questMarginLeft - 4, 1)
|
||||||
line.expandQuest.zoneId = zoneName
|
line.expandQuest.zoneId = zoneName
|
||||||
|
|
||||||
|
|
||||||
@@ -1033,7 +1047,7 @@ function QuestieTracker:Update()
|
|||||||
|
|
||||||
if quest.isFallback or quest._isLogFallback then
|
if quest.isFallback or quest._isLogFallback then
|
||||||
-- Quest not in DB: use the name stored on the fallback object
|
-- Quest not in DB: use the name stored on the fallback object
|
||||||
local questName = quest.name or tostring(quest.Id)
|
local questName = GetDisplayableQuestName(quest)
|
||||||
if Questie.db.profile.trackerShowQuestLevel and quest.level and quest.level > 0 then
|
if Questie.db.profile.trackerShowQuestLevel and quest.level and quest.level > 0 then
|
||||||
questName = "[" .. quest.level .. "] " .. questName
|
questName = "[" .. quest.level .. "] " .. questName
|
||||||
end
|
end
|
||||||
@@ -1067,7 +1081,7 @@ function QuestieTracker:Update()
|
|||||||
-- which queries the static DB only: for a quest with no DB entry it
|
-- which queries the static DB only: for a quest with no DB entry it
|
||||||
-- silently defaults to level 1, and Ascension's scaling then scales
|
-- silently defaults to level 1, and Ascension's scaling then scales
|
||||||
-- that wrong base instead of the quest's real level.
|
-- that wrong base instead of the quest's real level.
|
||||||
local fallbackName = quest.name or tostring(quest.Id)
|
local fallbackName = GetDisplayableQuestName(quest)
|
||||||
if Questie.db.profile.trackerShowQuestLevel and quest.level and quest.level > 0 then
|
if Questie.db.profile.trackerShowQuestLevel and quest.level and quest.level > 0 then
|
||||||
fallbackName = QuestieLib:GetQuestString(quest.Id, fallbackName, quest.level, false)
|
fallbackName = QuestieLib:GetQuestString(quest.Id, fallbackName, quest.level, false)
|
||||||
end
|
end
|
||||||
@@ -1181,7 +1195,8 @@ function QuestieTracker:Update()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Attach button to Quest Title linePool
|
-- Attach button to Quest Title linePool
|
||||||
button:SetPoint("TOPLEFT", button.line, "TOPLEFT", superTrackMarginLeft, 0)
|
button:SetPoint("TOPLEFT", button.line, "TOPLEFT",
|
||||||
|
TrackerLinePool.GetItemButtonOffset(), 0)
|
||||||
button:SetParent(button.line)
|
button:SetParent(button.line)
|
||||||
button:Show()
|
button:Show()
|
||||||
|
|
||||||
@@ -1195,6 +1210,10 @@ function QuestieTracker:Update()
|
|||||||
button:SetParent(UIParent)
|
button:SetParent(UIParent)
|
||||||
button:Hide()
|
button:Hide()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- The quest item button owns this slot, so the marker steps out to
|
||||||
|
-- the left of the line for as long as it is there.
|
||||||
|
button.line.superTrackButton:SetItemButtonShown(button:IsShown())
|
||||||
else
|
else
|
||||||
-- Button failed to get setup for some reason or the quest item is now gone. Hide it and enable the Quest Min/Max button.
|
-- Button failed to get setup for some reason or the quest item is now gone. Hide it and enable the Quest Min/Max button.
|
||||||
-- See previous comment for details on why we're setting this button to UIParent.
|
-- See previous comment for details on why we're setting this button to UIParent.
|
||||||
@@ -1289,7 +1308,7 @@ function QuestieTracker:Update()
|
|||||||
|
|
||||||
-- Attach button to Quest Title linePool
|
-- Attach button to Quest Title linePool
|
||||||
altButton:SetPoint("TOPLEFT", altButton.line, "TOPLEFT",
|
altButton:SetPoint("TOPLEFT", altButton.line, "TOPLEFT",
|
||||||
superTrackMarginLeft + 2 + questItemButtonSize, 0)
|
TrackerLinePool.GetItemButtonOffset() + 2 + questItemButtonSize, 0)
|
||||||
altButton:SetParent(altButton.line)
|
altButton:SetParent(altButton.line)
|
||||||
altButton:Show()
|
altButton:Show()
|
||||||
|
|
||||||
@@ -1590,6 +1609,16 @@ function QuestieTracker:Update()
|
|||||||
|
|
||||||
-- Adds 2 pixels and "Padding Between Quests" setting in Tracker Options
|
-- Adds 2 pixels and "Padding Between Quests" setting in Tracker Options
|
||||||
line:SetHeight(line.label:GetHeight() + (Questie.db.profile.trackerQuestPadding + 2))
|
line:SetHeight(line.label:GetHeight() + (Questie.db.profile.trackerQuestPadding + 2))
|
||||||
|
|
||||||
|
-- Centre the supertrack button on the quest's whole text block now that its
|
||||||
|
-- objective lines are drawn and their heights are final.
|
||||||
|
if questTitleLine.superTrackButton.questId then
|
||||||
|
local blockHeight = TrackerLinePool.GetQuestBlockHeight(questTitleLine, line)
|
||||||
|
|
||||||
|
if blockHeight then
|
||||||
|
questTitleLine.superTrackButton:SetBlockHeight(blockHeight)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
primaryButton = false
|
primaryButton = false
|
||||||
@@ -1648,9 +1677,9 @@ function QuestieTracker:Update()
|
|||||||
line.criteriaMark:Hide()
|
line.criteriaMark:Hide()
|
||||||
line.playButton:Hide()
|
line.playButton:Hide()
|
||||||
|
|
||||||
-- Setup Zone Label (indented past the supertrack button gutter, as above)
|
-- Setup Zone Label
|
||||||
line.label:ClearAllPoints()
|
line.label:ClearAllPoints()
|
||||||
line.label:SetPoint("TOPLEFT", line, "TOPLEFT", superTrackMarginLeft, 0)
|
line.label:SetPoint("TOPLEFT", line, "TOPLEFT", 0, 0)
|
||||||
|
|
||||||
-- Set Zone Title and Min/Max states
|
-- Set Zone Title and Min/Max states
|
||||||
if Questie.db.char.collapsedZones[zoneName] then
|
if Questie.db.char.collapsedZones[zoneName] then
|
||||||
@@ -1679,15 +1708,15 @@ function QuestieTracker:Update()
|
|||||||
|
|
||||||
-- Check and measure Zone Label text width and update tracker width
|
-- Check and measure Zone Label text width and update tracker width
|
||||||
QuestieTracker:UpdateWidth(line.label:GetStringWidth() + trackerMarginLeft +
|
QuestieTracker:UpdateWidth(line.label:GetStringWidth() + trackerMarginLeft +
|
||||||
superTrackMarginLeft + trackerMarginRight)
|
trackerMarginRight)
|
||||||
|
|
||||||
-- Set Zone Label and Line widths
|
-- Set Zone Label and Line widths
|
||||||
line.label:SetWidth(trackerBaseFrame:GetWidth() - trackerMarginLeft - superTrackMarginLeft - trackerMarginRight)
|
line.label:SetWidth(trackerBaseFrame:GetWidth() - trackerMarginLeft - trackerMarginRight)
|
||||||
line:SetWidth(line.label:GetWidth() + superTrackMarginLeft)
|
line:SetWidth(line.label:GetWidth())
|
||||||
|
|
||||||
-- Compare largest text Label in the tracker with current Label, then save widest width
|
-- Compare largest text Label in the tracker with current Label, then save widest width
|
||||||
trackerLineWidth = math.max(trackerLineWidth,
|
trackerLineWidth = math.max(trackerLineWidth,
|
||||||
line.label:GetStringWidth() + trackerMarginLeft + superTrackMarginLeft)
|
line.label:GetStringWidth() + trackerMarginLeft)
|
||||||
|
|
||||||
-- Setup Min/Max Button
|
-- Setup Min/Max Button
|
||||||
line.expandZone:ClearAllPoints()
|
line.expandZone:ClearAllPoints()
|
||||||
@@ -1727,7 +1756,7 @@ function QuestieTracker:Update()
|
|||||||
|
|
||||||
-- Set Min/Max Button and default states
|
-- Set Min/Max Button and default states
|
||||||
line.expandQuest:Show()
|
line.expandQuest:Show()
|
||||||
line.expandQuest:SetPoint("TOPRIGHT", line, "TOPLEFT", questMarginLeft - 8, 1)
|
line.expandQuest:SetPoint("TOPRIGHT", line, "TOPLEFT", questMarginLeft - 4, 1)
|
||||||
line.expandQuest.zoneId = zoneName
|
line.expandQuest.zoneId = zoneName
|
||||||
|
|
||||||
-- The minAllQuestsInZone table is always blank until a player Shift+Clicks the Zone header (MouseDown).
|
-- The minAllQuestsInZone table is always blank until a player Shift+Clicks the Zone header (MouseDown).
|
||||||
|
|||||||
@@ -115,18 +115,20 @@ local linePool = {}
|
|||||||
local buttonPool = {}
|
local buttonPool = {}
|
||||||
local lineMarginLeft = 10
|
local lineMarginLeft = 10
|
||||||
|
|
||||||
-- Gutter given to the supertrack button, which sits flush with the left edge of a quest line. The
|
-- Gap kept between the supertrack button and whatever it is tucked in next to.
|
||||||
-- collapse button, the quest item buttons and the labels are all shifted right by this much, so
|
local superTrackButtonGap = 1
|
||||||
-- nothing lands on top of it and nothing hangs over the tracker's left edge. Fed into
|
|
||||||
-- questMarginLeft, which every width calculation already builds on, so the tracker widens to match.
|
|
||||||
function TrackerLinePool.GetSuperTrackMarginReserve()
|
|
||||||
if (not Questie.db.profile.trackerShowSuperTrackButton) or (not TrackerUtils:IsSuperTrackAvailable()) then
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Reserved even for quests whose button is hidden, so the list does not shift around as quests
|
-- Left edge of a quest line's collapse button. QuestieTracker anchors it at questMarginLeft - 4
|
||||||
-- come in and out of the supertrackable set.
|
-- with a width of trackerFontSizeQuest, and questMarginLeft carries a matching + trackerFontSizeQuest,
|
||||||
return (Questie.db.profile.trackerSuperTrackButtonSize or 25) + 4
|
-- so it lands on a flat 22 whatever the font size is.
|
||||||
|
local superTrackCollapseButtonLeft = 22
|
||||||
|
|
||||||
|
-- Left edge of the quest item buttons. QuestieTracker anchors them there and the marker tucks in
|
||||||
|
-- to the left of them, so the two have to agree on it.
|
||||||
|
local questItemButtonLeft = 6
|
||||||
|
|
||||||
|
function TrackerLinePool.GetItemButtonOffset()
|
||||||
|
return questItemButtonLeft
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param questFrame Frame
|
---@param questFrame Frame
|
||||||
@@ -529,9 +531,62 @@ function TrackerLinePool.Initialize(questFrame)
|
|||||||
|
|
||||||
superTrackButton.SetSuperTrackButton = function(self, questId)
|
superTrackButton.SetSuperTrackButton = function(self, questId)
|
||||||
self.questId = questId
|
self.questId = questId
|
||||||
|
-- Back to the title line on its own: the objective lines below have not been laid out
|
||||||
|
-- yet, so the tracker measures the block and calls SetBlockHeight once they are. Same
|
||||||
|
-- for the quest item button, which is set up further down and owns this slot.
|
||||||
|
self.blockHeight = nil
|
||||||
|
self.itemButtonShown = nil
|
||||||
self:RefreshSuperTrackButton()
|
self:RefreshSuperTrackButton()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Horizontally: tucked in to the left of whatever else owns the head of the line -- the
|
||||||
|
-- quest item button where there is one, the collapse button where there is not. It hangs
|
||||||
|
-- over the line's left edge, and off the tracker entirely at larger sizes; giving the
|
||||||
|
-- marker a column of its own would mean indenting every quest in the tracker for it.
|
||||||
|
-- Vertically: centred on the quest's whole text block, title plus objectives, the height
|
||||||
|
-- the tracker hands us. Without one, centred on the quest title alone, which is all that
|
||||||
|
-- exists at the point the button is first set up.
|
||||||
|
superTrackButton.AnchorSuperTrackButton = function(self)
|
||||||
|
local buttonSize = Questie.db.profile.trackerSuperTrackButtonSize or 25
|
||||||
|
local blockHeight = self.blockHeight or Questie.db.profile.trackerFontSizeQuest
|
||||||
|
local slotLeft = self.itemButtonShown and questItemButtonLeft or superTrackCollapseButtonLeft
|
||||||
|
local offsetX = slotLeft - superTrackButtonGap - buttonSize
|
||||||
|
|
||||||
|
-- The lines live inside the tracker's scroll frame, which clips anything hanging over
|
||||||
|
-- its edge, so a marker that reaches past it is reparented above the clip. It stays
|
||||||
|
-- anchored to its line either way, and every redraw hides it by hand
|
||||||
|
-- (ResetLinesForChange) before deciding whether to show it again.
|
||||||
|
self:SetParent(((offsetX + lineMarginLeft) < 0) and trackerQuestFrame or line)
|
||||||
|
|
||||||
|
self:ClearAllPoints()
|
||||||
|
self:SetPoint("TOPLEFT", line, "TOPLEFT", offsetX, (buttonSize - blockHeight) / 2 + 1)
|
||||||
|
|
||||||
|
-- Has to sit above the tracker backdrop, which is what swallows a frame left at level 0.
|
||||||
|
-- Strata comes from the line rather than from whichever parent it ended up with, so the
|
||||||
|
-- two cases draw the same.
|
||||||
|
self:SetFrameStrata(line:GetFrameStrata())
|
||||||
|
self:SetFrameLevel(line:GetFrameLevel() + 10)
|
||||||
|
end
|
||||||
|
|
||||||
|
superTrackButton.SetBlockHeight = function(self, blockHeight)
|
||||||
|
if self.blockHeight == blockHeight then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
self.blockHeight = blockHeight
|
||||||
|
self:AnchorSuperTrackButton()
|
||||||
|
end
|
||||||
|
|
||||||
|
superTrackButton.SetItemButtonShown = function(self, shown)
|
||||||
|
shown = shown and true or false
|
||||||
|
if (self.itemButtonShown or false) == shown then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
self.itemButtonShown = shown
|
||||||
|
self:AnchorSuperTrackButton()
|
||||||
|
end
|
||||||
|
|
||||||
superTrackButton.RefreshSuperTrackButton = function(self)
|
superTrackButton.RefreshSuperTrackButton = function(self)
|
||||||
-- No map pin means the client has nothing to point the marker at (quest in another zone,
|
-- No map pin means the client has nothing to point the marker at (quest in another zone,
|
||||||
-- or a quest without map coordinates), so there is nothing to offer.
|
-- or a quest without map coordinates), so there is nothing to offer.
|
||||||
@@ -581,12 +636,7 @@ function TrackerLinePool.Initialize(questFrame)
|
|||||||
-- while the mouse is still held down.
|
-- while the mouse is still held down.
|
||||||
self:SetPressedOffset(false)
|
self:SetPressedOffset(false)
|
||||||
|
|
||||||
-- Flush with the left edge of the line, in the gutter GetSuperTrackMarginReserve keeps
|
self:AnchorSuperTrackButton()
|
||||||
-- clear. Anchored to the top rather than centred on the line so the button stays level
|
|
||||||
-- with the first row of a quest title that wraps, matching the collapse button.
|
|
||||||
local fontSizeQuest = Questie.db.profile.trackerFontSizeQuest
|
|
||||||
self:ClearAllPoints()
|
|
||||||
self:SetPoint("TOPLEFT", line, "TOPLEFT", 0, (buttonSize - fontSizeQuest) / 2 + 1)
|
|
||||||
|
|
||||||
-- The icon carries the selected variant itself; the glow is the one part the pin draws
|
-- The icon carries the selected variant itself; the glow is the one part the pin draws
|
||||||
-- as a separate texture.
|
-- as a separate texture.
|
||||||
@@ -596,8 +646,6 @@ function TrackerLinePool.Initialize(questFrame)
|
|||||||
self.glow:Hide()
|
self.glow:Hide()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Has to sit above the tracker backdrop, which is what swallows a frame left at level 0.
|
|
||||||
self:SetFrameLevel(line:GetFrameLevel() + 10)
|
|
||||||
self:Show()
|
self:Show()
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -701,6 +749,9 @@ function TrackerLinePool.Initialize(questFrame)
|
|||||||
|
|
||||||
line.expandQuest = expandQuest
|
line.expandQuest = expandQuest
|
||||||
|
|
||||||
|
-- Its own slot in the pool, so a run of lines drawn for one quest can be walked back over.
|
||||||
|
line.lineIndex = i
|
||||||
|
|
||||||
linePool[i] = line
|
linePool[i] = line
|
||||||
nextFrame = line
|
nextFrame = line
|
||||||
end
|
end
|
||||||
@@ -1060,6 +1111,49 @@ function TrackerLinePool.GetCurrentButton()
|
|||||||
return buttonPool[buttonIndex]
|
return buttonPool[buttonIndex]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Height of the block of lines a single quest was drawn into, title line through last objective.
|
||||||
|
-- Added up from the heights the tracker itself set rather than measured off the frames: on login
|
||||||
|
-- the tracker has not been laid out yet, and GetTop/GetBottom then report positions from a
|
||||||
|
-- half-built frame, which is enough to fling a marker centred on the result off the tracker.
|
||||||
|
---@return number|nil blockHeight
|
||||||
|
function TrackerLinePool.GetQuestBlockHeight(firstLine, lastLine)
|
||||||
|
if (not firstLine) or (not lastLine) or (not firstLine.lineIndex) or (not lastLine.lineIndex) then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if lastLine.lineIndex < firstLine.lineIndex then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local blockHeight = 0
|
||||||
|
for i = firstLine.lineIndex, lastLine.lineIndex do
|
||||||
|
local line = linePool[i]
|
||||||
|
if not line then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
blockHeight = blockHeight + line:GetHeight()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- The last line of a quest carries the padding to the next one. That is empty space below the
|
||||||
|
-- text, so it plays no part in where the block's centre is. A quest collapsed down to its title
|
||||||
|
-- is the exception: that single line is the whole quest, and neither reference reads right on
|
||||||
|
-- its own -- against the text alone the marker rides high over the row's empty half, against
|
||||||
|
-- the whole row it sits low under the title it belongs to -- so it splits the difference.
|
||||||
|
local trailingPadding = Questie.db.profile.trackerQuestPadding + 2
|
||||||
|
if lastLine.lineIndex == firstLine.lineIndex then
|
||||||
|
trailingPadding = trailingPadding / 2
|
||||||
|
end
|
||||||
|
|
||||||
|
blockHeight = blockHeight - trailingPadding
|
||||||
|
|
||||||
|
if blockHeight <= 0 then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return blockHeight
|
||||||
|
end
|
||||||
|
|
||||||
---@return table|nil lineIndex linePool[lineIndex - 1]
|
---@return table|nil lineIndex linePool[lineIndex - 1]
|
||||||
function TrackerLinePool.GetPreviousLine()
|
function TrackerLinePool.GetPreviousLine()
|
||||||
lineIndex = lineIndex - 1
|
lineIndex = lineIndex - 1
|
||||||
@@ -1109,6 +1203,10 @@ function TrackerLinePool.HideUnusedLines()
|
|||||||
line.expandZone.zoneId = nil
|
line.expandZone.zoneId = nil
|
||||||
line.criteriaMark.mode = nil
|
line.criteriaMark.mode = nil
|
||||||
line.playButton.mode = nil
|
line.playButton.mode = nil
|
||||||
|
-- Hidden by hand: a marker that overflows to the left of its line is parented above the
|
||||||
|
-- scroll frame's clip, so hiding the line no longer hides it.
|
||||||
|
line.superTrackButton.questId = nil
|
||||||
|
line.superTrackButton:Hide()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -676,37 +676,162 @@ local function GetAreaIdByZoneName(zoneName)
|
|||||||
return l10n:GetAreaIdByLocalName(zoneName)
|
return l10n:GetAreaIdByLocalName(zoneName)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Walk the quest log to find the zone header for a given questId.
|
-- questId -> the header the client filed it under in the quest log. In 3.3.5 those headers are
|
||||||
-- In 3.3.5, zone names appear as isHeader=true entries above their quests.
|
-- isHeader=true entries sitting above the quests they cover. Built in one pass and kept, because
|
||||||
-- Returns the header title string, or nil if not found.
|
-- the tracker asks per quest and redraws often -- walking the log once per quest is quadratic.
|
||||||
local function GetQuestLogZoneName(questId)
|
local questLogHeaders = {}
|
||||||
local targetIndex = nil
|
|
||||||
local total = GetNumQuestLogEntries and GetNumQuestLogEntries() or 0
|
-- Every questId the log currently holds, headers aside. The tracker draws from currentQuestlog,
|
||||||
for i = 1, total do
|
-- and this is what says whether the player still has a given quest.
|
||||||
|
local questLogQuestIds = {}
|
||||||
|
|
||||||
|
-- questId -> its row in the quest log, so re-reading a quest's leaderboard costs a lookup
|
||||||
|
-- instead of another walk. Verified before use, since the log renumbers on every change.
|
||||||
|
local questLogIndexes = {}
|
||||||
|
|
||||||
|
local function BuildQuestLogHeaders()
|
||||||
|
local headers = {}
|
||||||
|
local questIds = {}
|
||||||
|
local indexes = {}
|
||||||
|
local header
|
||||||
|
|
||||||
|
for i = 1, (GetNumQuestLogEntries and GetNumQuestLogEntries() or 0) do
|
||||||
|
local title, _, _, isHeader, _, _, _, logId = GetQuestLogTitle(i)
|
||||||
|
if isHeader then
|
||||||
|
if title and title ~= "" then
|
||||||
|
header = title
|
||||||
|
end
|
||||||
|
elseif logId then
|
||||||
|
questIds[logId] = true
|
||||||
|
indexes[logId] = i
|
||||||
|
if header then
|
||||||
|
headers[logId] = header
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
questLogHeaders = headers
|
||||||
|
questLogQuestIds = questIds
|
||||||
|
questLogIndexes = indexes
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Returns the quest's current row in the quest log, or nil if the player no longer has it.
|
||||||
|
local function GetQuestLogIndexForQuest(questId)
|
||||||
|
local index = questLogIndexes[questId]
|
||||||
|
if index then
|
||||||
|
local _, _, _, isHeader, _, _, _, logId = GetQuestLogTitle(index)
|
||||||
|
if (not isHeader) and logId == questId then
|
||||||
|
return index
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 1, (GetNumQuestLogEntries and GetNumQuestLogEntries() or 0) do
|
||||||
local _, _, _, isHeader, _, _, _, logId = GetQuestLogTitle(i)
|
local _, _, _, isHeader, _, _, _, logId = GetQuestLogTitle(i)
|
||||||
if not isHeader and logId == questId then
|
if (not isHeader) and logId == questId then
|
||||||
targetIndex = i
|
questLogIndexes[questId] = i
|
||||||
break
|
return i
|
||||||
end
|
|
||||||
end
|
|
||||||
if not targetIndex then return nil end
|
|
||||||
for i = targetIndex, 1, -1 do
|
|
||||||
local title, _, _, isHeader = GetQuestLogTitle(i)
|
|
||||||
if isHeader and title and title ~= "" then
|
|
||||||
return title
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- 3.3.5 hands over a leaderboard line as one string -- "Icefang slain: 3/8" -- while the tracker
|
||||||
|
-- prints the description and the counts separately, so they have to come back apart here.
|
||||||
|
-- Objectives with nothing to count arrive as bare text and stand in as a single 0/1 step.
|
||||||
|
local function ParseLeaderBoardText(text, finished)
|
||||||
|
-- Greedy on purpose: a description of its own may hold a colon, and the counter is the last one.
|
||||||
|
local description, collected, needed = string.match(text, "^(.*):%s*(%d+)%s*/%s*(%d+)%s*$")
|
||||||
|
if not description then
|
||||||
|
-- Servers writing their own objective strings do not always keep the colon.
|
||||||
|
description, collected, needed = string.match(text, "^(.-)%s*(%d+)%s*/%s*(%d+)%s*$")
|
||||||
|
end
|
||||||
|
|
||||||
|
if (not description) or description == "" then
|
||||||
|
return text, (finished and 1 or 0), 1
|
||||||
|
end
|
||||||
|
|
||||||
|
collected = tonumber(collected) or 0
|
||||||
|
needed = tonumber(needed) or 1
|
||||||
|
if needed <= 0 then
|
||||||
|
needed = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
return description, collected, needed
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fills `objectives` from the quest's leaderboard, in place: the tracker hands these very tables
|
||||||
|
-- to the lines it draws, so a redraw has to update them rather than swap in new ones.
|
||||||
|
local function ReadFallbackObjectives(questLogIndex, questId, objectives)
|
||||||
|
local numObjectives = (GetNumQuestLeaderBoards and GetNumQuestLeaderBoards(questLogIndex)) or 0
|
||||||
|
|
||||||
|
for j = 1, numObjectives do
|
||||||
|
local text, objectiveType, finished = GetQuestLogLeaderBoard(j, questLogIndex)
|
||||||
|
if text then
|
||||||
|
local description, collected, needed = ParseLeaderBoardText(text, finished)
|
||||||
|
local objective = objectives[j]
|
||||||
|
if not objective then
|
||||||
|
objective = {}
|
||||||
|
objectives[j] = objective
|
||||||
|
end
|
||||||
|
|
||||||
|
objective.questId = questId
|
||||||
|
objective.Index = j
|
||||||
|
objective.Description = description
|
||||||
|
objective.text = text
|
||||||
|
objective.Collected = collected
|
||||||
|
objective.Needed = needed
|
||||||
|
objective.Completed = (finished or collected >= needed) and true or false
|
||||||
|
objective.baseType = objectiveType
|
||||||
|
-- Nothing here came from the DB, so map/tooltip code has to leave it alone.
|
||||||
|
objective.Type = "fallback"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for j = #objectives, numObjectives + 1, -1 do
|
||||||
|
objectives[j] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return objectives
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Returns the header title string, or nil if the quest is not in the log under one.
|
||||||
|
local function GetQuestLogZoneName(questId)
|
||||||
|
if not questLogQuestIds[questId] then
|
||||||
|
-- Asked about a quest the last pass did not see, so the log has moved on since.
|
||||||
|
BuildQuestLogHeaders()
|
||||||
|
end
|
||||||
|
|
||||||
|
return questLogHeaders[questId]
|
||||||
|
end
|
||||||
|
|
||||||
local function _GetZoneName(zoneOrSort, questId, zoneNameOverride)
|
local function _GetZoneName(zoneOrSort, questId, zoneNameOverride)
|
||||||
if zoneNameOverride and zoneNameOverride ~= "" then
|
if zoneNameOverride and zoneNameOverride ~= "" then
|
||||||
return zoneNameOverride
|
return zoneNameOverride
|
||||||
end
|
end
|
||||||
if not zoneOrSort then return "Unknown Zone" end
|
|
||||||
|
-- A quest assembled from a partial record -- a Learner entry that never captured the field,
|
||||||
|
-- an override carrying a single key -- reaches here with no zoneOrSort at all. That is the
|
||||||
|
-- same "nothing to look up" case as 0, and the quest log below still knows where the client
|
||||||
|
-- files the quest, so it must not short-circuit to Unknown Zone ahead of that.
|
||||||
|
zoneOrSort = zoneOrSort or 0
|
||||||
|
|
||||||
local zoneName
|
local zoneName
|
||||||
local sortObj = Questie.db.profile.trackerSortObjectives
|
local sortObj = Questie.db.profile.trackerSortObjectives
|
||||||
if sortObj == "byZone" or sortObj == "byZoneComplete" or sortObj == "byZoneCompleteReversed" or sortObj == "byZonePlayerProximity" or sortObj == "byZonePlayerProximityReversed" then
|
if sortObj == "byZone" or sortObj == "byZoneComplete" or sortObj == "byZoneCompleteReversed" or sortObj == "byZonePlayerProximity" or sortObj == "byZonePlayerProximityReversed" then
|
||||||
|
-- A server can file quests under categories of its own -- Ascension's "Ascension Main
|
||||||
|
-- Quest" -- and those exist nowhere in the zone tables, so the quest data points at a zone
|
||||||
|
-- instead: for anything the Learner recorded, whichever zone it was picked up in. A header
|
||||||
|
-- that does not resolve to an area is one of those categories, and the client's own
|
||||||
|
-- grouping is the only thing that knows about it.
|
||||||
|
local logHeader = GetQuestLogZoneName(questId)
|
||||||
|
if logHeader and logHeader ~= "" then
|
||||||
|
local headerAreaId = GetAreaIdByZoneName(logHeader)
|
||||||
|
if (not headerAreaId) or headerAreaId == 0 then
|
||||||
|
return logHeader
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if (zoneOrSort) > 0 then
|
if (zoneOrSort) > 0 then
|
||||||
zoneName = TrackerUtils:GetZoneNameByID(zoneOrSort)
|
zoneName = TrackerUtils:GetZoneNameByID(zoneOrSort)
|
||||||
if not zoneName or zoneName == "Unknown Zone" then
|
if not zoneName or zoneName == "Unknown Zone" then
|
||||||
@@ -746,73 +871,119 @@ local function _GetZoneName(zoneOrSort, questId, zoneNameOverride)
|
|||||||
return zoneName
|
return zoneName
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- The client's own title for a quest, or nil if the player does not have it. Quest objects can
|
||||||
|
-- reach the tracker without a usable name -- a questDataOverrides entry that carries no name
|
||||||
|
-- field, a QuestLogCache row read before the client had filled the title in -- and since those
|
||||||
|
-- objects are cached for the session, the name never repairs itself. The log always knows.
|
||||||
|
function TrackerUtils:GetQuestLogTitleById(questId)
|
||||||
|
local questLogIndex = GetQuestLogIndexForQuest(questId)
|
||||||
|
if not questLogIndex then return nil end
|
||||||
|
|
||||||
|
local title = GetQuestLogTitle(questLogIndex)
|
||||||
|
if title and title ~= "" then
|
||||||
|
return title
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- IsComplete must be a method (called as quest:IsComplete()), and it reads the state the last
|
||||||
|
-- refresh stored rather than closing over the state at build time -- these quests outlive many
|
||||||
|
-- redraws, and a captured value would still claim the quest is unfinished after it is turned in.
|
||||||
|
local function FallbackQuestIsComplete(self)
|
||||||
|
if self.logIsComplete == 1 or self.logIsComplete == true then
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if QuestiePlayer.currentQuestlog[self.Id] and IsQuestFlaggedCompleted and IsQuestFlaggedCompleted(self.Id) then
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Re-reads everything the quest log owns: title, level, completion and the objectives. The
|
||||||
|
-- tracker keeps fallback quests between draws, so without this they stay frozen at whatever
|
||||||
|
-- the log said when the quest was first seen.
|
||||||
|
---@return boolean @false if the player no longer has the quest
|
||||||
|
function TrackerUtils:RefreshFallbackQuest(quest)
|
||||||
|
if not quest then return false end
|
||||||
|
|
||||||
|
local questLogIndex = GetQuestLogIndexForQuest(quest.Id)
|
||||||
|
if not questLogIndex then return false end
|
||||||
|
|
||||||
|
local title, level, _, _, _, isComplete = GetQuestLogTitle(questLogIndex)
|
||||||
|
if title and title ~= "" then
|
||||||
|
quest.name = title
|
||||||
|
end
|
||||||
|
if level and level > 0 then
|
||||||
|
quest.level = level
|
||||||
|
end
|
||||||
|
|
||||||
|
quest.logIsComplete = isComplete
|
||||||
|
quest.isComplete = (isComplete == 1)
|
||||||
|
|
||||||
|
ReadFallbackObjectives(questLogIndex, quest.Id, quest.Objectives)
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
-- Returns nil if the quest is not currently in the quest log.
|
-- Returns nil if the quest is not currently in the quest log.
|
||||||
function TrackerUtils:BuildFallbackQuest(questId)
|
function TrackerUtils:BuildFallbackQuest(questId)
|
||||||
for i = 1, GetNumQuestLogEntries() do
|
local questLogIndex = GetQuestLogIndexForQuest(questId)
|
||||||
local title, level, _, isHeader, _, isComplete, _, logQuestId = GetQuestLogTitle(i)
|
if not questLogIndex then return nil end
|
||||||
if not isHeader and logQuestId == questId then
|
|
||||||
-- Parse objectives from the leaderboard
|
|
||||||
local objectives = {}
|
|
||||||
local numObj = GetNumQuestLeaderBoards and GetNumQuestLeaderBoards(i) or 0
|
|
||||||
for j = 1, numObj do
|
|
||||||
local text, _, finished = GetQuestLogLeaderBoard(j, i)
|
|
||||||
if text then
|
|
||||||
-- Parse "Description: X/Y" or just "Description"
|
|
||||||
local collected, needed = string.match(text, ":.-(%d+)/(%d+)%s*$")
|
|
||||||
collected = tonumber(collected) or (finished and 1 or 0)
|
|
||||||
needed = tonumber(needed) or 1
|
|
||||||
objectives[j] = {
|
|
||||||
text = text,
|
|
||||||
Needed = needed,
|
|
||||||
Collected = collected,
|
|
||||||
Finished = finished or (collected >= needed),
|
|
||||||
Type = "fallback",
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Walk backwards from i in the quest log to find the zone header.
|
-- Walk backwards from the quest in the log to find the zone header.
|
||||||
-- This is the canonical 3.3.5 method: zone headers sit above their quests.
|
-- This is the canonical 3.3.5 method: zone headers sit above their quests.
|
||||||
local zoneText = nil
|
local zoneText = nil
|
||||||
for h = i, 1, -1 do
|
for h = questLogIndex, 1, -1 do
|
||||||
local hTitle, _, _, hIsHeader = GetQuestLogTitle(h)
|
local hTitle, _, _, hIsHeader = GetQuestLogTitle(h)
|
||||||
if hIsHeader and hTitle and hTitle ~= "" then
|
if hIsHeader and hTitle and hTitle ~= "" then
|
||||||
zoneText = hTitle
|
zoneText = hTitle
|
||||||
break
|
break
|
||||||
end
|
|
||||||
end
|
|
||||||
local zoneId = (zoneText and GetAreaIdByZoneName(zoneText)) or 0
|
|
||||||
local zoneNameOverride = nil
|
|
||||||
if zoneText and (not zoneId or zoneId == 0) then
|
|
||||||
zoneNameOverride = zoneText
|
|
||||||
end
|
|
||||||
|
|
||||||
local quest = {
|
|
||||||
Id = questId,
|
|
||||||
name = title or ("Quest " .. questId),
|
|
||||||
level = level or 0,
|
|
||||||
zoneOrSort = zoneId,
|
|
||||||
zoneName = zoneText,
|
|
||||||
zoneNameOverride = zoneNameOverride,
|
|
||||||
Objectives = objectives,
|
|
||||||
SpecialObjectives = {},
|
|
||||||
isFallback = true,
|
|
||||||
}
|
|
||||||
-- IsComplete must be a method (called as quest:IsComplete())
|
|
||||||
quest.IsComplete = function(self)
|
|
||||||
return (isComplete == 1 or (QuestiePlayer.currentQuestlog[questId] and IsQuestFlaggedCompleted and IsQuestFlaggedCompleted(questId))) and 1 or 0
|
|
||||||
end
|
|
||||||
|
|
||||||
return quest
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return nil
|
local zoneId = (zoneText and GetAreaIdByZoneName(zoneText)) or 0
|
||||||
|
local zoneNameOverride = nil
|
||||||
|
if zoneText and (not zoneId or zoneId == 0) then
|
||||||
|
zoneNameOverride = zoneText
|
||||||
|
end
|
||||||
|
|
||||||
|
local quest = {
|
||||||
|
Id = questId,
|
||||||
|
name = "Quest " .. questId,
|
||||||
|
level = 0,
|
||||||
|
zoneOrSort = zoneId,
|
||||||
|
zoneName = zoneText,
|
||||||
|
zoneNameOverride = zoneNameOverride,
|
||||||
|
Objectives = {},
|
||||||
|
SpecialObjectives = {},
|
||||||
|
isFallback = true,
|
||||||
|
IsComplete = FallbackQuestIsComplete,
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Same read the tracker does on every later draw, so a quest built here and one refreshed
|
||||||
|
-- from the cache carry exactly the same fields.
|
||||||
|
TrackerUtils:RefreshFallbackQuest(quest)
|
||||||
|
|
||||||
|
return quest
|
||||||
end
|
end
|
||||||
|
|
||||||
function TrackerUtils:GetSortedQuestIds()
|
function TrackerUtils:GetSortedQuestIds()
|
||||||
local sortedQuestIds = {}
|
local sortedQuestIds = {}
|
||||||
local questDetails = {}
|
local questDetails = {}
|
||||||
local sortObj = Questie.db.profile.trackerSortObjectives
|
local sortObj = Questie.db.profile.trackerSortObjectives
|
||||||
|
|
||||||
|
-- One walk of the quest log for the whole draw, so the per-quest lookups below are reads.
|
||||||
|
BuildQuestLogHeaders()
|
||||||
|
|
||||||
|
-- currentQuestlog is only as good as the removal events that maintain it, and a quest the
|
||||||
|
-- server finishes on its own -- Ascension's auto-complete quests -- can leave the log without
|
||||||
|
-- any of them landing, which strands the quest in the tracker for the rest of the session. The
|
||||||
|
-- log is the authority on what the player still has, so anything missing from it is skipped.
|
||||||
|
-- Skipped rather than pruned: a redraw that catches the log mid-refresh would otherwise throw
|
||||||
|
-- away state Questie is about to want back.
|
||||||
|
local questLogIsReadable = next(questLogQuestIds) ~= nil
|
||||||
-- Update quest objectives
|
-- Update quest objectives
|
||||||
|
|
||||||
for questId, quest in pairs(QuestiePlayer.currentQuestlog) do
|
for questId, quest in pairs(QuestiePlayer.currentQuestlog) do
|
||||||
@@ -878,6 +1049,11 @@ function TrackerUtils:GetSortedQuestIds()
|
|||||||
if fallback then
|
if fallback then
|
||||||
TrackerUtils._fallbackQuests[qid] = fallback
|
TrackerUtils._fallbackQuests[qid] = fallback
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
-- Cached by an earlier draw, so its objectives and completion state are as
|
||||||
|
-- old as the cache. Nothing else updates them -- these quests have no DB
|
||||||
|
-- entry, so QuestieQuest's populate path skips them entirely.
|
||||||
|
TrackerUtils:RefreshFallbackQuest(fallback)
|
||||||
end
|
end
|
||||||
if fallback then
|
if fallback then
|
||||||
quest = fallback
|
quest = fallback
|
||||||
@@ -885,7 +1061,14 @@ function TrackerUtils:GetSortedQuestIds()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if type(quest) == "table" and quest.IsComplete and quest.Objectives then
|
local isInQuestLog = (not questLogIsReadable) or (questLogQuestIds[qid] == true)
|
||||||
|
if not isInQuestLog then
|
||||||
|
-- Left over from a removal nothing told the tracker about, so the object built for it
|
||||||
|
-- goes too -- otherwise it would still be here to serve the next draw.
|
||||||
|
TrackerUtils._fallbackQuests[qid] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if isInQuestLog and type(quest) == "table" and quest.IsComplete and quest.Objectives then
|
||||||
-- Insert Quest Ids into sortedQuestIds table
|
-- Insert Quest Ids into sortedQuestIds table
|
||||||
tinsert(sortedQuestIds, qid)
|
tinsert(sortedQuestIds, qid)
|
||||||
|
|
||||||
|
|||||||
@@ -11,10 +11,13 @@ Made for and tested on CoA, but it should also work on other 3.3.5 clients.
|
|||||||
- fixed custom quests having no name in the tracker.
|
- fixed custom quests having no name in the tracker.
|
||||||
- fixed custom quests not showing (complete) in the tracker.
|
- fixed custom quests not showing (complete) in the tracker.
|
||||||
- fixed custom quest items not showing.
|
- fixed custom quest items not showing.
|
||||||
|
- fixed quest tags (group, elite, dungeon...) being wrong on quest ids Ascension reuses for its own content.
|
||||||
|
- fixed Ascension "main quests" not having the correct category and not clearing on complete.
|
||||||
|
|
||||||
## Changes
|
## Changes
|
||||||
|
|
||||||
- added "By Zone + %% Completed" and "By Zone + %% Completed (Reversed)" tracker sorting options.
|
- implemented Ascension's backported supertracker buttons to the tracker.
|
||||||
|
- added "By Zone + % Completed" and "By Zone + % Completed (Reversed)" tracker sorting options.
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user