diff --git a/DatabaseDefaults.lua b/DatabaseDefaults.lua index f237e96..20c08a0 100644 --- a/DatabaseDefaults.lua +++ b/DatabaseDefaults.lua @@ -7,6 +7,8 @@ addon.defaults = { positions = {}, trackQuestObjectives = true, + showMapIconsZone = false, + showMapIconsContinent = false, goodcolor = {0, 1, 0}, badcolor = {1, 0, 0}, diff --git a/TomTomLite.lua b/TomTomLite.lua index 1b51a4c..7b6b7ee 100644 --- a/TomTomLite.lua +++ b/TomTomLite.lua @@ -58,12 +58,18 @@ function addon:Initialize() self.arrow:SetPoint("CENTER", 0, 0) self.arrow:Hide() - self:RegisterEvent("QUEST_POI_UPDATE") - self:RegisterEvent("QUEST_LOG_UPDATE") - self:RegisterMessage("TOMTOMLITE_WAYPOINT_ADDED") + -- Events for objective tracking + self:RegisterEvent("QUEST_POI_UPDATE", "OBJECTIVES_CHANGED") + self:RegisterEvent("QUEST_LOG_UPDATE", "OBJECTIVES_CHANGED") + self:RegisterMessage("OBJECTIVES_CHANGED") hooksecurefunc("WatchFrame_Update", function(self) - addon:UpdateQuestObjectives() + addon:FireMessage("OBJECTIVES_CHANGED") end) + + self:RegisterMessage("TOMTOMLITE_WAYPOINT_ADDED") + + -- Events for world map overlays + self:RegisterEvent("WORLD_MAP_UPDATE") end function addon:CreateCrazyArrow(name, parent) @@ -131,6 +137,10 @@ function addon:CreateCrazyArrow(name, parent) return frame end +--[[------------------------------------------------------------------------- +-- External API +-------------------------------------------------------------------------]]-- + function addon:AddWaypoint(map, floor, x, y, opt) assert(type(map) == "number") assert(type(floor) == "number") @@ -148,57 +158,82 @@ function addon:AddWaypoint(map, floor, x, y, opt) table.insert(self.waypoints, waypoint) self:FireMessage("TOMTOMLITE_WAYPOINT_ADDED", waypoint) -end -function addon:TOMTOMLITE_WAYPOINT_ADDED(msg, waypoint, ...) - local zone, floor, x, y = unpack(waypoint) - local lzone = self.mapdata:MapLocalize(zone) - --self:Printf("Added a waypoint at (%.2f, %.2f) in %s", x * 100, y * 100, lzone) - - self.arrow.waypoint = waypoint - self.arrow.title:SetText(waypoint.title or L["Unknown waypoint"]) - self.arrow.info:SetFormattedText("%.2f, %.2f - %s", x * 100, y * 100, lzone) - self.arrow:Show() + return waypoint end -local function POIAnchorToCoord(poiframe) - local point, relto, relpoint, x, y = poiframe:GetPoint() - local frame = WorldMapDetailFrame - local width = frame:GetWidth() - local height = frame:GetHeight() - local scale = frame:GetScale() / poiframe:GetScale() - local cx = (x / scale) / width - local cy = (-y / scale) / height - - if cx < 0 or cx > 1 or cy < 0 or cy > 1 then - return nil, nil +function addon:DeleteWaypoint(waypoint) + for idx, entry in ipairs(self.waypoints) do + if entry == waypoint then + table.remove(self.waypoints, waypoint) + break + end end - local map, floor = GetCurrentMapAreaID() - return map, floor or 0, cx, cy + self:FireMessage("TOMTOMLITE_WAYPOINT_DELETED", waypoint) end -local function findQuestFrameFromQuestIndex(index) - -- Try to find the correct quest frame - for i = 1, MAX_NUM_QUESTS do - local questFrame = _G["WorldMapQuestFrame" .. i]; - if not questFrame then - break - elseif questFrame.questLogIndex == index then - return questFrame - end - end +--[[------------------------------------------------------------------------- +-- Private implementation +-------------------------------------------------------------------------]]-- +function addon:TOMTOMLITE_WAYPOINT_ADDED(msg, waypoint, ...) + self:UpdateArrow() end -function addon:QUEST_POI_UPDATE() +function addon:OBJECTIVES_CHANGED() self:UpdateQuestObjectives() + self:UpdateArrow() end -function addon:QUEST_LOG_UPDATE() - self:UpdateQuestObjectives() +function addon:UpdateArrow() + -- local cmap, cfloor = GetCurrentMapAreaID() + -- local cx, cy = GetPlayerMapPosition("player") + + -- -- Scan the current waypoints and determine which one is closest, regardless + -- -- of which zone the waypoint is in. This could be altered to only consider + -- -- waypoints in the current zone, quite easily. + -- local mindist = math.huge + -- local closest + + -- for idx, waypoint in ipairs(self.waypoints) do + -- local map, floor, x, y = unpack(waypoint) + -- local distance = addon.mapdata:DistanceWithinContinent(cmap, cfloor or 0, cx, cy, map, floor, x, y) + -- if distance < mindist then + -- mindist = distance + -- closest = waypoint + -- end + -- end + + local closest = self.arrow.waypoint + + if closest then + -- Set the crazy arrow to display this waypoint + local zone, floor, x, y = unpack(closest) + local lzone = self.mapdata:MapLocalize(zone) + + self.arrow.waypoint = closest + self.arrow.title:SetText(closest.title or L["Unknown waypoint"]) + self.arrow.info:SetFormattedText("%.2f, %.2f - %s", x * 100, y * 100, lzone) + self.arrow:Show() + else + self.arrow.waypoint = nil + self.arrow:Hide() + end end +local objectiveWaypoints = {} function addon:UpdateQuestObjectives() + local map, floor = GetCurrentMapAreaID() + floor = floor or 0 + + -- Do not set any NEW waypoints if we're on the continent map + if self.mapdata:IsContinentMap(map) then + return + end + + local cvar = GetCVarBool("questPOI") + SetCVar("questPOI", 1) + -- Only do an objective scan if the option is enabled if not self.db.profile.trackQuestObjectives then return @@ -206,35 +241,140 @@ function addon:UpdateQuestObjectives() QuestPOIUpdateIcons() + -- Scan through every quest that is being tracked, and create a waypoint + -- for each of the objectives that are being tracked. These waypoints will + -- be unordered, and will be sorted or ordered by the user/algorithm + local closest local watchIndex = 1 while true do - -- Get the first quest item in the watch frame local questIndex = GetQuestIndexForWatch(watchIndex) if not questIndex then - self.arrow:Hide() - return + break end local title = GetQuestLogTitle(questIndex) local qid = select(9, GetQuestLogTitle(questIndex)) - local completed, x, y, objective = QuestPOIGetIconInfo(qid) - -- Check to see if there was coordinate information for the current point if x and y then - local map, floor = GetCurrentMapAreaID() - - if not floor then floor = 0 end - if completed then - title = "Turn in: " .. title - end + -- For two waypoints to be equal, their map, floor, x, y should all be + -- the same, as well as the title and completion set. + local key = qid + (x * 100) * (y * 100) * map * (floor + 1) + if not objectiveWaypoints[key] then + if completed then + title = "Turn in: " .. title + end - if x and y and map and floor then - self:AddWaypoint(map, floor, x, y, {title = title}) + local waypoint = self:AddWaypoint(map, floor, x, y, {title = title}) + objectiveWaypoints[key] = waypoint + if not closest then + closest = objectiveWaypoints[key] + end + else + if not closest then + closest = objectiveWaypoints[key] + end end - return end watchIndex = watchIndex + 1 end + + self.arrow.waypoint = closest + + SetCVar("questPOI", cvar and 1 or 0) +end + +--[[------------------------------------------------------------------------- +-- World map support, displaying waypoint overlays +-------------------------------------------------------------------------]]-- + +-- Create an overlay that we can use to parent our world map icons +addon.overlay = CreateFrame("Frame", addonName .. "MapOverlay", WorldMapButton) +addon.overlay:SetAllPoints() +addon.overlay:Show() + +-- Metatable that stores world map icons indexed by number, in array form +local worldmapIcons = setmetatable({}, { + __index = function(t, k) + local name = addonName .. "MapIcon" .. tostring(k) + local button = CreateFrame("Button", name, addon.overlay) + button:SetSize(64, 64) + button:SetHitRectInsets(12, 12, 5, 2) + + button.icon = button:CreateTexture("BACKGROUND") + button.icon:SetTexture("Interface\\AddOns\\TomTomLite\\images\\MapPointer") + button.icon:SetVertexColor(0.3, 1.0, 0.3) + button.icon:SetAllPoints() + button.glow = button:CreateTexture("BACKGROUND") + button.glow:SetTexture("Interface\\AddOns\\TomTomLite\\images\\MapPointerGlow") + button.glow:SetAllPoints() + button.glow:Hide() + button.number = button:CreateTexture("OVERLAY", name .. "Number") + button.number:SetSize(50, 50) + button.number:SetTexture("Interface\\WorldMap\\UI-QuestPoi-NumberIcons") + button.number:SetPoint("CENTER", button, "CENTER", 0, 8) + button.number:SetDrawLayer("OVERLAY", 7) + + rawset(t, k, button) + return button + end, +}) + +function addon:WORLD_MAP_UPDATE() + -- Display any waypoints overlaid on the world map. Specifically, if + -- the map zoom is set to a continent or cosmic map, then display all + -- waypoints, otherwise display only the waypoints for the currently + -- displayed zone. If there are waypoints on a floor other than the + -- one the player is currently on, they will be displayed as well, and + -- will be distinguishable from waypoints on the current floor. + + -- If the map isn't shown, do nothing + if not addon.overlay:IsVisible() then + return + end + + -- Check the options to see what should be displayed + if not self.db.profile.showMapIconsZone then + return + end + + -- TODO: Handle 'showMapIconsContinent' option + + local continent = GetCurrentMapContinent() + local map, floor = GetCurrentMapAreaID() + local waypoints = addon.waypointsByMap[map] + + for idx = 1, math.max(#waypoints, #worldmapIcons), 1 do + local icon = worldmapIcons[idx] + local waypoint = waypoints[idx] + + if waypoint then + local width, height = addon.overlay:GetSize() + + icon:ClearAllPoints() + + local x = waypoint[3] * width + local y = waypoint[4] * height + + -- Set the number to be displayed + local buttonIndex = idx - 1 + local yOffset = 0.5 + math.floor(buttonIndex / QUEST_POI_ICONS_PER_ROW) * QUEST_POI_ICON_SIZE; + local xOffset = mod(buttonIndex, QUEST_POI_ICONS_PER_ROW) * QUEST_POI_ICON_SIZE + icon.number:SetTexCoord(xOffset, xOffset + QUEST_POI_ICON_SIZE, yOffset, yOffset + QUEST_POI_ICON_SIZE) + + -- Nudge position so arrow appears centered on POI + icon:SetPoint("BOTTOM", addon.overlay, "TOPLEFT", x + 1, -y - 5) + + if (floor or 0) == waypoint[2] then + icon:SetAlpha(1.0) + else + icon:SetAlpha(0.6) + end + + icon:Show() + else + icon:Hide() + end + end end diff --git a/images/MapPointer.tga b/images/MapPointer.tga new file mode 100644 index 0000000..8df1177 Binary files /dev/null and b/images/MapPointer.tga differ diff --git a/images/MapPointerGlow.tga b/images/MapPointerGlow.tga new file mode 100644 index 0000000..38cdfc7 Binary files /dev/null and b/images/MapPointerGlow.tga differ