mirror of
https://github.com/rivi-s/pfQuest.git
synced 2026-11-10 20:17:36 +00:00
Improve tracker, routes, and tooltips
This commit is contained in:
+45
-2
@@ -701,6 +701,18 @@ end
|
||||
|
||||
-- ShowExtendedTooltip
|
||||
-- Draws quest informations into a tooltip
|
||||
local MAX_TOOLTIP_DESCRIPTION = 500
|
||||
local function GetTooltipDescription(text)
|
||||
local formatted = pfDatabase:FormatQuestText(text)
|
||||
if strlen(formatted) <= MAX_TOOLTIP_DESCRIPTION then
|
||||
return formatted
|
||||
end
|
||||
|
||||
-- Extended descriptions are useful context, but a few custom quests contain
|
||||
-- several full paragraphs. Keep tooltips readable without hiding objectives.
|
||||
return strsub(formatted, 1, MAX_TOOLTIP_DESCRIPTION) .. "..."
|
||||
end
|
||||
|
||||
function pfDatabase:ShowExtendedTooltip(id, tooltip, parent, anchor, offx, offy)
|
||||
local tooltip = tooltip or GameTooltip
|
||||
local parent = parent or this
|
||||
@@ -770,7 +782,7 @@ function pfDatabase:ShowExtendedTooltip(id, tooltip, parent, anchor, offx, offy)
|
||||
-- details
|
||||
if locales["D"] and locales["D"] ~= "" then
|
||||
tooltip:AddLine(" ")
|
||||
tooltip:AddLine(pfDatabase:FormatQuestText(locales["D"]), 0.6, 0.6, 0.6, true)
|
||||
tooltip:AddLine(GetTooltipDescription(locales["D"]), 0.6, 0.6, 0.6, true)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2020,7 +2032,9 @@ function pfDatabase:GetQuestIDs(qid)
|
||||
if header or not title then
|
||||
return
|
||||
end
|
||||
local identifier = title .. ":" .. (level or "") .. ":" .. (objective or "") .. ":" .. (text or "")
|
||||
-- Version this key when resolver rules change so stale same-title matches
|
||||
-- do not keep bypassing the improved live-objective disambiguation.
|
||||
local identifier = "objective-v2:" .. title .. ":" .. (level or "") .. ":" .. (objective or "") .. ":" .. (text or "")
|
||||
|
||||
-- always make sure the quest-cache exists
|
||||
pfQuest_questcache = pfQuest_questcache or {}
|
||||
@@ -2037,6 +2051,24 @@ function pfDatabase:GetQuestIDs(qid)
|
||||
local best = 0
|
||||
local results = {}
|
||||
|
||||
-- Several quest chains reuse both title and level (The Defias Brotherhood
|
||||
-- is a prominent example). The description fallback can tie on Turtle's
|
||||
-- altered text, so collect actual item objectives from the quest log and
|
||||
-- use them as a definitive stage discriminator below.
|
||||
local objectiveItems = {}
|
||||
local boardCount = GetNumQuestLeaderBoards(qid) or 0
|
||||
for board = 1, boardCount do
|
||||
local boardText, boardType = GetQuestLogLeaderBoard(board, qid)
|
||||
if boardType == "item" and boardText then
|
||||
local _, _, itemName = strfind(boardText, "^(.-):")
|
||||
if itemName then
|
||||
for itemId in pairs(pfDatabase:GetIDByName(itemName, "items")) do
|
||||
objectiveItems[itemId] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local tcount = 0
|
||||
-- check if multiple quests share the same name
|
||||
for id, data in pairs(pfDB["quests"]["loc"]) do
|
||||
@@ -2105,6 +2137,17 @@ function pfDatabase:GetQuestIDs(qid)
|
||||
|
||||
-- check description and calculate score
|
||||
score = score + max(24 - lev(pfDatabase:FormatQuestText(pfDB.quests.loc[id]["D"]), text, 24), 0)
|
||||
|
||||
-- A matching live item objective distinguishes same-name stages even
|
||||
-- when their title, level, and Turtle quest text are otherwise equal.
|
||||
if quests[id]["obj"] and quests[id]["obj"]["I"] then
|
||||
for _, itemId in pairs(quests[id]["obj"]["I"]) do
|
||||
if objectiveItems[itemId] then
|
||||
score = score + 64
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if score > best then
|
||||
|
||||
@@ -1081,21 +1081,31 @@ function pfMap:UpdateNodes()
|
||||
-- reset tracker
|
||||
pfQuest.tracker.Reset()
|
||||
|
||||
-- reset route
|
||||
pfQuest.route:Reset()
|
||||
|
||||
-- Continent and world views do not resolve to a zone map ID. Extensions can
|
||||
-- render their own pins there, but the core zone-node renderer must not use
|
||||
-- nil as a dirty-map key.
|
||||
if not map then
|
||||
pfQuest.route:Clear()
|
||||
pfMap.lastRouteMap = nil
|
||||
for _, pin in pairs(pfMap.pins) do
|
||||
pin:Hide()
|
||||
end
|
||||
pfMap.lastUpdateZone = nil
|
||||
pfMap.mapJustOpened = nil
|
||||
if pfQuest.tracker and pfQuest.tracker.DoLayout then
|
||||
pfQuest.tracker.DoLayout()
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- A tracker/UI refresh can call UpdateNodes without changing any map node.
|
||||
-- Keep the existing route in that case; resetting it redraws the path every
|
||||
-- couple of seconds even though its inputs are unchanged.
|
||||
if pfMap.lastRouteMap ~= map or pfMap.dirtyMaps[map] then
|
||||
pfQuest.route:Reset()
|
||||
pfMap.lastRouteMap = map
|
||||
end
|
||||
|
||||
-- refresh all nodes
|
||||
local n_pins, n_skipped = 0, 0
|
||||
-- hoist map dimensions: same for every pin this call, and if the map
|
||||
@@ -1141,6 +1151,13 @@ function pfMap:UpdateNodes()
|
||||
pfQuest.route:AddPoint({ x, y, pfMap.pins[i] })
|
||||
end
|
||||
|
||||
-- Populate the tracker even when the matching map pin is hidden by a
|
||||
-- display preference. Hidden objective spawns are still active quests
|
||||
-- and must remain visible in Current Zone Only mode.
|
||||
for title, node in pairs(pfMap.pins[i].node) do
|
||||
pfQuest.tracker.ButtonAdd(title, node)
|
||||
end
|
||||
|
||||
-- hide cluster nodes if set
|
||||
if pfQuest_config["showcluster"] == "0" and pfMap.pins[i].cluster then
|
||||
pfMap.pins[i]:Hide()
|
||||
@@ -1148,11 +1165,6 @@ function pfMap:UpdateNodes()
|
||||
elseif pfQuest_config["showspawn"] == "0" and addon == "PFQUEST" and not pfMap.pins[i].texture then
|
||||
pfMap.pins[i]:Hide()
|
||||
else
|
||||
-- populate quest list on map
|
||||
for title, node in pairs(pfMap.pins[i].node) do
|
||||
pfQuest.tracker.ButtonAdd(title, node)
|
||||
end
|
||||
|
||||
local px = x / 100 * mapW
|
||||
local py = y / 100 * mapH
|
||||
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
## Author: Shagu, txtsd
|
||||
## Notes: A Questhelper and Database
|
||||
## Notes-ruRU: Облегченный помощник заданий и база данных
|
||||
## Version: 8.0.1
|
||||
## Version: 8.0.2
|
||||
## OptionalDeps: pfUI
|
||||
## SavedVariables: pfQuest_questcache
|
||||
## SavedVariablesPerCharacter: pfQuest_config, pfBrowser_fav, pfQuest_history, pfQuest_colors, pfQuest_server, pfQuest_track
|
||||
|
||||
@@ -262,6 +262,10 @@ pfQuest:SetScript("OnUpdate", function()
|
||||
local meta = { ["addon"] = "PFQUEST", ["qlogid"] = entry[3] }
|
||||
local t1 = GetTime()
|
||||
pfDatabase:SearchQuestID(entry[2], meta)
|
||||
-- SearchQuestID marks map nodes dirty, but does not itself request a
|
||||
-- render. Queue one after the quest batch settles so the tracker
|
||||
-- receives newly found same-zone objectives immediately.
|
||||
pfMap.queue_update = GetTime()
|
||||
pfQuest:Debug(format("|cffff8800TIMER SearchQuestID: %.4fs", GetTime() - t1))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -34,7 +34,16 @@ local function GetNearest(xstart, ystart, db, blacklist)
|
||||
-- Use squared distance for comparison (avoid sqrt)
|
||||
local distSq = x * x + y * y
|
||||
|
||||
if not nearest or distSq < nearest then
|
||||
local replace = not nearest or distSq < nearest
|
||||
if distSq == nearest and best then
|
||||
local previous = db[best]
|
||||
-- Keep equal-distance choices stable even when db was rebuilt through
|
||||
-- pairs(). Coordinates are sufficient; the index is a final tie-break.
|
||||
replace = data[1] < previous[1]
|
||||
or (data[1] == previous[1] and (data[2] < previous[2] or (data[2] == previous[2] and id < best)))
|
||||
end
|
||||
|
||||
if replace then
|
||||
nearest = distSq
|
||||
best = id
|
||||
end
|
||||
@@ -157,14 +166,26 @@ pfQuest.route.coords = {}
|
||||
pfQuest.route.Reset = function(self)
|
||||
self.coords = {}
|
||||
self.firstnode = nil
|
||||
self.recalculate = true
|
||||
self.lastDrawX = nil
|
||||
self.lastDrawY = nil
|
||||
self.lastDrawNode = nil
|
||||
end
|
||||
|
||||
pfQuest.route.Clear = function(self)
|
||||
self:Reset()
|
||||
ClearPath(objectivepath)
|
||||
ClearPath(playerpath)
|
||||
ClearPath(mplayerpath)
|
||||
if self.arrow then
|
||||
self.arrow:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
pfQuest.route.AddPoint = function(self, tbl)
|
||||
table.insert(self.coords, tbl)
|
||||
self.firstnode = nil
|
||||
self.recalculate = true
|
||||
end
|
||||
|
||||
local targetTitle, targetCluster, targetLayer, targetTexture = nil, nil, nil, nil
|
||||
@@ -185,6 +206,7 @@ pfQuest.route.SetTarget = function(node, default)
|
||||
targetCluster = node and node.cluster or nil
|
||||
targetLayer = node and node.layer or nil
|
||||
targetTexture = node and node.texture or nil
|
||||
pfQuest.route.recalculate = true
|
||||
end
|
||||
|
||||
pfQuest.route.IsTarget = function(node)
|
||||
@@ -203,8 +225,29 @@ pfQuest.route.IsTarget = function(node)
|
||||
end
|
||||
|
||||
local lastpos, completed = 0, 0
|
||||
local automaticTargetKey = nil
|
||||
local function TargetKey(data)
|
||||
if not data then return nil end
|
||||
local node = data[3]
|
||||
return tostring(data[1]) .. ":" .. tostring(data[2]) .. ":" .. tostring(node and (node.title or node.spawn) or "")
|
||||
end
|
||||
local function sortfunc(a, b)
|
||||
return a[4] < b[4]
|
||||
-- Distances are rounded to two decimals, so ties are common. UpdateNodes
|
||||
-- rebuilds its list through pairs(), whose order is undefined; without a
|
||||
-- deterministic tie-breaker the first route target can flip every refresh.
|
||||
if a[4] ~= b[4] then
|
||||
return a[4] < b[4]
|
||||
end
|
||||
if a[1] ~= b[1] then
|
||||
return a[1] < b[1]
|
||||
end
|
||||
if a[2] ~= b[2] then
|
||||
return a[2] < b[2]
|
||||
end
|
||||
|
||||
local an = a[3] and (a[3].title or a[3].spawn or "") or ""
|
||||
local bn = b[3] and (b[3].title or b[3].spawn or "") or ""
|
||||
return tostring(an) < tostring(bn)
|
||||
end
|
||||
pfQuest.route:SetScript("OnUpdate", function()
|
||||
local xplayer, yplayer = GetPlayerMapPosition("player")
|
||||
@@ -235,10 +278,32 @@ pfQuest.route:SetScript("OnUpdate", function()
|
||||
this.coords[id][4] = ceil(math.sqrt(x * x + y * y) * 100) / 100
|
||||
end
|
||||
end
|
||||
-- sort all coords by distance only once per second
|
||||
if not this.recalculate or this.recalculate < GetTime() then
|
||||
-- Reorder only when the available route nodes or an explicit target change.
|
||||
-- Re-sorting every second while the player moves causes route flicker and
|
||||
-- expensive map redraws without improving the selected objective.
|
||||
if this.recalculate then
|
||||
table.sort(this.coords, sortfunc)
|
||||
|
||||
-- Map-position updates have slight jitter. Keep the current automatic
|
||||
-- target while it remains close to the best candidate, otherwise two
|
||||
-- nearby objectives can continuously trade places and redraw the route.
|
||||
if not targetTitle and automaticTargetKey and this.coords[1] then
|
||||
local previousIndex
|
||||
for id, data in ipairs(this.coords) do
|
||||
if TargetKey(data) == automaticTargetKey then
|
||||
previousIndex = id
|
||||
break
|
||||
end
|
||||
end
|
||||
if previousIndex and previousIndex > 1 then
|
||||
local previous = this.coords[previousIndex]
|
||||
if previous[4] <= this.coords[1][4] + 0.5 then
|
||||
table.remove(this.coords, previousIndex)
|
||||
table.insert(this.coords, 1, previous)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- order list on custom targets
|
||||
if targetTitle and this.coords[1] and not pfQuest.route.IsTarget(this.coords[1][3]) then
|
||||
local target = nil
|
||||
@@ -266,7 +331,9 @@ pfQuest.route:SetScript("OnUpdate", function()
|
||||
end
|
||||
end
|
||||
|
||||
this.recalculate = GetTime() + 1
|
||||
automaticTargetKey = not targetTitle and TargetKey(this.coords[1]) or nil
|
||||
|
||||
this.recalculate = nil
|
||||
end
|
||||
|
||||
-- show arrow when route exists and is stable
|
||||
|
||||
Reference in New Issue
Block a user