Quantcast

Refactoring and adding world map icon display code

James Whitehead II [12-02-10 - 17:29]
Refactoring and adding world map icon display code
Filename
DatabaseDefaults.lua
TomTomLite.lua
images/MapPointer.tga
images/MapPointerGlow.tga
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