diff --git a/Map/Arrow.lua b/Map/Arrow.lua index cda6cbd..4fc63b7 100644 --- a/Map/Arrow.lua +++ b/Map/Arrow.lua @@ -1,9 +1,11 @@ --- Quest arrow that points toward the nearest active quest objective in the --- current zone, adapted from pfQuest's route arrow. +-- Quest guidance overlays adapted from pfQuest's route.lua: a screen arrow that +-- points at the nearest active objective, and a dotted route across the map and +-- minimap that walks the current zone's objectives in nearest-first order. QuestieOcto.Arrow = QuestieOcto.Arrow or {} local A = QuestieOcto.Arrow local ARROW_TEXTURE="Interface\\AddOns\\Questie-Octo\\UI\\Icons\\arrow" +local ROUTE_TEXTURE="Interface\\AddOns\\Questie-Octo\\UI\\Icons\\route" local atan2=atan2 or math.atan2 local function Settings() @@ -54,32 +56,53 @@ local function IsArrowRole(role) or role=="objectiveItemSource" or role=="turnin" end --- Pull the current map's prepared plan and keep only objective/turn-in slots. +-- Pull the current map's prepared plan and keep only objective/turn-in slots, +-- dropping any the quest menu has hidden and recording each node's objective +-- index so focus can filter by objective. local function CollectTargets(mapID) local targets={} local plan=mapID and QuestieOcto.PreparedMap and QuestieOcto.PreparedMap:Get(mapID) or nil if not plan then return targets end + local menu=QuestieOcto.QuestMenu for _,desc in pairs(plan) do - local x,y,questID + local node if desc.type=="nodeSlot" then for _,entry in pairs(desc.entries or {}) do - local node=entry.node - if node and IsArrowRole(node.role) then - x,y,questID=tonumber(desc.x),tonumber(desc.y),node.questID - break - end + local n=entry.node + if n and IsArrowRole(n.role) then node=n; break end end elseif desc.type=="node" and desc.node and IsArrowRole(desc.node.role) then - x,y,questID=tonumber(desc.x),tonumber(desc.y),desc.node.questID + node=desc.node end - if x and y then table.insert(targets,{x,y,questID}) end + if node and (not menu or not menu:IsNodeHidden(node)) then + local x,y=tonumber(desc.x),tonumber(desc.y) + if x and y then table.insert(targets,{x,y,node.questID,tonumber(node.objectiveIndex)}) end + end end return targets end +-- When an objective is focused, restrict to its nodes (falling back to all when +-- the focused objective has no node in this zone). +local function FocusTargets(targets) + if not QuestieOcto.QuestMenu then return targets end + local fq,fo=QuestieOcto.QuestMenu:GetFocus() + if not fq then return targets end + local filtered={} + local i=1 + local total=table.getn(targets) + while i<=total do + local t=targets[i] + if tonumber(t[3])==fq and tonumber(t[4])==fo then table.insert(filtered,t) end + i=i+1 + end + if table.getn(filtered)>0 then return filtered end + return targets +end + local function NearestTarget(targets,px,py) local best,bestDist for _,t in pairs(targets or {}) do @@ -90,6 +113,232 @@ local function NearestTarget(targets,px,py) return best,bestDist end +-- Greedy nearest-neighbor ordering starting from the objective closest to the +-- player, the same heuristic pfQuest uses to approximate the shortest route. +local function BuildRoute(targets,px,py,forceFirst) + local n=table.getn(targets) + if n==0 then return {} end + local used={} + local route={} + local cx,cy=px,py + if forceFirst then + local fi=1 + while fi<=n do + if targets[fi]==forceFirst then + used[fi]=true + route[1]=forceFirst + cx,cy=forceFirst[1],forceFirst[2] + break + end + fi=fi+1 + end + end + local placed=table.getn(route) + while placedGetTime() then return end + self.routeThrottle=GetTime()+0.1 + + EnsureRouteFrames() + + if not Settings():Get("showQuestRoute") then + self:ClearRoute() + return + end + + local px,py + if GetPlayerMapPosition then px,py=GetPlayerMapPosition("player") end + px,py=(px or 0)*100,(py or 0)*100 + if px==0 and py==0 then self:ClearRoute(); return end + + local mapID=CurrentMapID() + if mapID~=self.routeMapID or (self.routeRefresh or 0)