From 29be25fb06a29f99b53c77127ab38d2177946795 Mon Sep 17 00:00:00 2001 From: James Whitehead II Date: Sun, 9 Jan 2011 16:08:09 +0000 Subject: [PATCH] Refactoring of corpse and objective code Waypoints should be provided by a waypoint source, which will eventually be filtered. Priorities should be used to deteremine which waypoints should be shown, allowing the user to control this themselves. --- AddonCore.lua | 20 +++++ DatabaseDefaults.lua | 2 + MapUtils.lua | 64 ++++++++++++---- TomTomLite.lua | 169 ++++++++++++++++--------------------------- TomTomLite.toc | 4 + sources/Corpse.lua | 133 ++++++++++++++++++++++++++++++++++ sources/QuestObjectives.lua | 134 ++++++++++++++++++++++++++++++++++ 7 files changed, 406 insertions(+), 120 deletions(-) create mode 100644 sources/ArchaeologyDigSites.lua create mode 100644 sources/Corpse.lua create mode 100644 sources/QuestObjectives.lua diff --git a/AddonCore.lua b/AddonCore.lua index a765133..aa37464 100644 --- a/AddonCore.lua +++ b/AddonCore.lua @@ -127,6 +127,16 @@ end -- Setup Initialize/Enable support -------------------------------------------------------------------------]]-- +local initFuncs = {} +function addon:RunAtInitialize(func) + initFuncs[#initFuncs + 1] = func +end + +local enableFuncs = {} +function addon:RunAtEnable(func) + enableFuncs[#enableFuncs + 1] = func +end + addon:RegisterEvent("PLAYER_LOGIN", "Enable") addon:RegisterEvent("ADDON_LOADED", function(event, ...) if ... == addonName then @@ -135,10 +145,20 @@ addon:RegisterEvent("ADDON_LOADED", function(event, ...) addon["Initialize"](addon) end + -- Run any registered init functions + for idx, func in ipairs(initFuncs) do + func() + end + -- If this addon was loaded-on-demand, trigger 'Enable' as well if IsLoggedIn() and type(addon["Enable"]) == "function" then addon["Enable"](addon) end + + -- Run any registered enable functions + for idx, func in ipairs(enableFuncs) do + func() + end end end) diff --git a/DatabaseDefaults.lua b/DatabaseDefaults.lua index 20c08a0..d9b3eaf 100644 --- a/DatabaseDefaults.lua +++ b/DatabaseDefaults.lua @@ -10,6 +10,8 @@ addon.defaults = { showMapIconsZone = false, showMapIconsContinent = false, + corpseArrow = true, + goodcolor = {0, 1, 0}, badcolor = {1, 0, 0}, middlecolor = {1, 1, 0}, diff --git a/MapUtils.lua b/MapUtils.lua index 1edecb5..cc68db7 100644 --- a/MapUtils.lua +++ b/MapUtils.lua @@ -34,7 +34,7 @@ end -- return nil function addon:GetVector(sm, sf, sx, sy, dm, df, dx, dy) - if smap == dmap and sfloor == dfloor then + if sm == dm and sf == df then -- The waypoints are on the same map, so calculate directly using map data if mapdata then @@ -74,17 +74,45 @@ function addon:GetVector(sm, sf, sx, sy, dm, df, dx, dy) end end +-- Returns the player's current map, floor and position. This information isn't +-- directly available if the world map is open and the player has navigated to +-- another map. + +function addon:GetPlayerPosition() + -- Attempt to get the player's position on the current map + local x, y = GetPlayerMapPosition("player") + if x and y and x > 0 and y > 0 then + local map, floor = GetCurrentMapAreaID() + return map, floor, x, y + end + + -- At this point, we were unable to get the position information + if WorldMapFrame:IsVisible() then + -- The map is open and we cannot change the map being displayed + return + end + + -- Flip the map to the current zone and get the position. We do not change + -- the map zoom back at the end of this function to avoid getting into any + -- nasty race conditions, it's bad enough that we need to set it currently. + SetMapToCurrentZone() + local x, y = GetPlayerMapPosition("player") + + if x <= 0 and y <= 0 then + -- Coordinate information not available for wherever the player is + return + end + + -- Fetch the map and floor and return the information that we have + local map, floor = GetCurrentMapAreaID() + return map, floor, x, y +end + -- Get the distance (in yards) and angle (in radians) from the player's current -- position to a position on the map. function addon:GetVectorFromCurrent(map, floor, x, y) - - -- First we need to obtain the player's current position. Attempt, at all - -- costs to do this without changing the map zoom, as it shouldn't be - -- necessary. - - local cmap, cfloor = GetCurrentMapAreaID() - local cx, cy = GetPlayerMapPosition("player") + local cmap, cfloor, cx, cy = self:GetPlayerPosition() if map and floor and x and y then return self:GetVector(cmap, cfloor, cx, cy, map, floor, x, y) @@ -115,11 +143,11 @@ local continents = { [751] = 5, -- Maelstrom (5) -- Map from continentIndex to mapId - [1] = 13, - [2] = 14, - [3] = 466, - [4] = 485, - [5] = 751, + [1] = 13, -- Kalimdor (13) + [2] = 14, -- Eastern Kingdoms (14) + [3] = 466, -- Outland (466) + [4] = 485, -- Northrend (485) + [5] = 751, -- Maelstrom (751) } -- Returns if a given map file is a continent map @@ -137,3 +165,13 @@ function addon:GetMapContinentMap(map) elseif astrolabe then end end + +function addon:GetNumMapFloors(map) + if mapdata then + local floors = mapdata:MapFloors(map) == 0 and 0 or 1 + return floors + elseif astrolabe then + local floors = astrolabe:GetNumFloors(map) == 0 and 0 or 1 + return floors + end +end diff --git a/TomTomLite.lua b/TomTomLite.lua index 110d7fa..1939222 100644 --- a/TomTomLite.lua +++ b/TomTomLite.lua @@ -57,15 +57,8 @@ function addon:Initialize() self.arrow:SetPoint("CENTER", 0, 0) self.arrow:Hide() - -- 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:FireMessage("OBJECTIVES_CHANGED") - end) - self:RegisterMessage("TOMTOMLITE_WAYPOINT_ADDED") + self:RegisterMessage("TOMTOMLITE_WAYPOINTS_CHANGED") -- Events for world map overlays self:RegisterEvent("WORLD_MAP_UPDATE") @@ -76,10 +69,15 @@ function addon:CreateCrazyArrow(name, parent) local frame = CreateFrame("Button", name, parent) frame:SetSize(128, 128) - frame.arrow = frame:CreateTexture("OVERLAY") + frame.arrow = frame:CreateTexture(name .. "Icon", "BACKGROUND") frame.arrow:SetAllPoints() frame.arrow:SetTexture("Interface\\Addons\\TomTomLite\\images\\BevArrow") + frame.glow = frame:CreateTexture(name .. "Glow", "OVERLAY") + frame.glow:SetAllPoints() + frame.glow:SetTexture("Interface\\Addons\\TomTomLite\\images\\BevArrowGlow") + frame.glow:SetVertexColor(1.0, 0.8, 0.0) + frame.title = frame:CreateFontString("OVERLAY", name .. "Title", "GameFontHighlight") frame.info = frame:CreateFontString("OVERLAY", name .. "Info", "GameFontHighlight") frame.subtitle = frame:CreateFontString("OVERLAY", name .. "Subtitle", "GameFontHighlight") @@ -109,6 +107,31 @@ function addon:CreateCrazyArrow(name, parent) self.arrow:SetVertexColor(r,g,b) self.arrow:SetRotation(faceangle) + -- This code is not quite correct, needs to be 'fixed' + + local lowlimit = 20.0 + local highlimit = 360.0 - lowlimit + + local angle = math.abs(deg(faceangle)) % 360 + + if angle <= lowlimit then + -- Determine what alpha to show + local perc = angle / lowlimit + + self.glow:Show() + self.glow:SetRotation(faceangle) + self.glow:SetAlpha(1.0 - perc) + elseif angle >= highlimit then + -- Determine what alpha to show + local perc = angle / (360 - lowlimit) + + self.glow:Show() + self.glow:SetRotation(faceangle) + self.glow:SetAlpha(1.0 - perc) + else + self.glow:Hide() + end + self.subtitle:SetFormattedText("%.1f yards", distance) end) @@ -127,10 +150,14 @@ end function addon:AddWaypoint(map, floor, x, y, opt) assert(type(map) == "number") - assert(type(floor) == "number") + assert(type(floor) == "number" or floor == nil) assert(type(x) == "number") assert(type(y) == "number") + if floor == nil then + floor = addon:GetNumMapFloors(map) + end + local waypoint = {map, floor, x, y} if type(opt) == "table" then for k, v in pairs(opt) do @@ -146,10 +173,10 @@ function addon:AddWaypoint(map, floor, x, y, opt) return waypoint end -function addon:DeleteWaypoint(waypoint) +function addon:RemoveWaypoint(waypoint) for idx, entry in ipairs(self.waypoints) do if entry == waypoint then - table.remove(self.waypoints, waypoint) + table.remove(self.waypoints, idx) break end end @@ -161,42 +188,37 @@ end -- Private implementation -------------------------------------------------------------------------]]-- function addon:TOMTOMLITE_WAYPOINT_ADDED(msg, waypoint, ...) - self:UpdateArrow() + self:FireMessage("TOMTOMLITE_WAYPOINTS_CHANGED") + self:Printf("Waypoint '%s' added at %.2f, %.2f", waypoint.title, waypoint[3], waypoint[4]) +end + +function addon:TOMTOMLITE_WAYPOINT_DELETED(msg, waypoint, ...) + self:FireMessage("TOMTOMLITE_WAYPOINTS_CHANGED") + self:Printf("Waypoint '%s' REMOVED at %.2f, %.2f", waypoint.title, waypoint[3], waypoint[4]) end -function addon:OBJECTIVES_CHANGED() - self:UpdateQuestObjectives() +function addon:TOMTOMLITE_WAYPOINTS_CHANGED(msg, ...) self:UpdateArrow() end 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) + -- This naive sort function will sort all waypoints so the highest + -- priority waypoint is first. This is the waypoint that will be + -- displayed on the arrow. + + table.sort(self.waypoints, function(a, b) + local apri = a.priority or 0 + local bpri = b.priority or 0 + return bpri < apri + end) + + local highest = self.waypoints[1] + if highest then + local zone, floor, x, y = unpack(highest) local lzone = self:GetMapDisplayName(zone) - self.arrow.waypoint = closest - self.arrow.title:SetText(closest.title or L["Unknown waypoint"]) + self.arrow.waypoint = highest + self.arrow.title:SetText(highest.title or L["Unknown waypoint"]) self.arrow.info:SetFormattedText("%.2f, %.2f - %s", x * 100, y * 100, lzone) self.arrow:Show() else @@ -205,70 +227,6 @@ function addon:UpdateArrow() 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: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 - end - - 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 - local questIndex = GetQuestIndexForWatch(watchIndex) - if not questIndex then - break - end - - local title = GetQuestLogTitle(questIndex) - local qid = select(9, GetQuestLogTitle(questIndex)) - local completed, x, y, objective = QuestPOIGetIconInfo(qid) - - if x and y then - -- 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 - - 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 - end - - watchIndex = watchIndex + 1 - end - - self.arrow.waypoint = closest - - SetCVar("questPOI", cvar and 1 or 0) -end - --[[------------------------------------------------------------------------- -- World map support, displaying waypoint overlays -------------------------------------------------------------------------]]-- @@ -323,9 +281,6 @@ function addon:WORLD_MAP_UPDATE() return end - -- TODO: Handle 'showMapIconsContinent' option - - local continent = GetCurrentMapContinent() local map, floor = GetCurrentMapAreaID() local waypoints = addon.waypointsByMap[map] diff --git a/TomTomLite.toc b/TomTomLite.toc index ae036a0..7449561 100755 --- a/TomTomLite.toc +++ b/TomTomLite.toc @@ -22,3 +22,7 @@ Utils.lua DatabaseDefaults.lua TomTomLite.lua + +sources\ArchaeologyDigSites.lua +sources\Corpse.lua +sources\QuestObjectives.lua diff --git a/sources/ArchaeologyDigSites.lua b/sources/ArchaeologyDigSites.lua new file mode 100644 index 0000000..e69de29 diff --git a/sources/Corpse.lua b/sources/Corpse.lua new file mode 100644 index 0000000..3d28b02 --- /dev/null +++ b/sources/Corpse.lua @@ -0,0 +1,133 @@ +local addonName, addon = ... +local L = addon.L + +--[[------------------------------------------------------------------- +-- Corpse arrow module for TomTomLite, example reference module +-------------------------------------------------------------------]]-- + +local eventFrame = CreateFrame("Frame") +eventFrame:RegisterEvent("ADDON_LOADED") +eventFrame:RegisterEvent("PLAYER_ALIVE") +eventFrame:RegisterEvent("PLAYER_DEAD") +eventFrame:RegisterEvent("PLAYER_UNGHOST") +eventFrame:RegisterEvent("PLAYER_ENTERING_WORLD") +eventFrame:Hide() + +local waypoint +local map, floor, x, y + +local function GetCorpseLocation() + if map and x and y then + return map, floor, x, y + end + + local oldmap, oldfloor = GetCurrentMapAreaID() + + local cont + -- Scan the all of the continent maps in order to find the corpse arrow + for i=1, select("#", GetMapContinents()) do + SetMapZoom(i) + local cx, cy = GetCorpseMapPosition() + if cx ~= 0 and cy ~= 0 then + cont = i + break + end + end + + -- If we found the corpse on a continent, find out which zone it is in + if cont and cont ~= -1 then + for i = 1, select("#", GetMapZones(cont)) do + SetMapZoom(cont, i) + local cx, cy = GetCorpseMapPosition() + if cx > 0 and cy > 0 then + map, floor = GetCurrentMapAreaID() + x, y = cx, cy + break + end + end + end + + -- Restore the map to its previous map + SetMapByID(oldmap, oldfloor) + + if map and x and y then + return map, floor, x, y + else + -- Now handle the case where the corpse is on the current map + local cx, cy = GetCorpseMapPosition() + if cx ~= 0 and cy ~= 0 then + map, floor = oldmap, oldfloor + x, y = cx, cy + return map, floor, x, y + end + end +end + +local function SetCorpseArrow() + if map and x and y and x > 0 and y > 0 then + waypoint = addon:AddWaypoint(map, floor, x, y, { + title = "Your corpse", + priority = 100, + }) + return waypoint + end +end + +local function StartCorpseSearch() + if not IsInInstance() then + eventFrame:Show() + end +end + +local function ClearCorpseArrow() + if waypoint then + addon:RemoveWaypoint(waypoint) + + waypoint = nil + map, floor, x, y = nil, nil, nil, nil + end +end + +local counter, throttle = 0, 0.5 +eventFrame:SetScript("OnUpdate", function(self, elapsed) + counter = counter + elapsed + if counter < throttle then + return + else + counter = 0 + if addon.db.profile.corpseArrow then + if GetCorpseLocation() then + if SetCorpseArrow() then + self:Hide() + end + end + else + self:Hide() + end + end +end) + +eventFrame:SetScript("OnEvent", function(self, event, arg1, ...) + if event == "ADDON_LOADED" and arg1 == addonName then + self:UnregisterEvent("ADDON_LOADED") + if UnitIsDeadOrGhost("player") then + StartCorpseSearch() + end + end + + if event == "PLAYER_ALIVE" or event == "PLAYER_ENTERING_WORLD" then + if UnitIsDeadOrGhost("player") then + StartCorpseSearch() + else + ClearCorpseArrow() + end + elseif event == "PLAYER_DEAD" then + StartCorpseSearch() + elseif event == "PLAYER_UNGHOST" then + ClearCorpseArrow() + end +end) + +if IsLoggedIn() then + eventFrame:GetScript("OnEvent")(eventFrame, "ADDON_LOADED", addonName) +end diff --git a/sources/QuestObjectives.lua b/sources/QuestObjectives.lua new file mode 100644 index 0000000..f670c71 --- /dev/null +++ b/sources/QuestObjectives.lua @@ -0,0 +1,134 @@ +local addonName, addon = ... +local L = addon.L + +--[[------------------------------------------------------------------- +-- Quest objective waypoint source for TomTomLite +-- +-- Anytime the quest log or objective POI information changes, the +-- objective tracker is scanned for all objectives and waypoints are +-- created for each of them. The priority of the first waypoint, i.e. +-- the first one in the objectives tracker, is set higher than the +-- rest. This of course can be overridden by the user +-------------------------------------------------------------------]]-- + +local PRI_FIRST = 15 +local PRI_OTHER = 0 + +local eventFrame = CreateFrame("Frame") +eventFrame:RegisterEvent("QUEST_POI_UPDATE") +eventFrame:RegisterEvent("QUEST_LOG_UPDATE") +hooksecurefunc("WatchFrame_Update", function(self) + addon:OBJECTIVES_CHANGED() +end) + +eventFrame:SetScript("OnEvent", function(self, event, ...) + if event == "QUEST_POI_UPDATE" then + addon:OBJECTIVES_CHANGED() + elseif event == "QUEST_LOG_UPDATE" and (...) == "player" then + addon:OBJECTIVES_CHANGED() + end +end) + +-- This function scans the current set of objectives, adding and removing +-- waypoints to reflect the current state of the tracker. + +local waypoints = {} +function addon:OBJECTIVES_CHANGED() + local map, floor = GetCurrentMapAreaID() + floor = floor or 0 + + -- Do not set any NEW waypoints if we're on the continent map + if self: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 + end + + local newWaypoints = {} + local changed = false + + -- This function relies on the above CVar being set, and updates the icon + -- position information so it can be queries via the API + 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 watchIndex = 1 + while true do + local questIndex = GetQuestIndexForWatch(watchIndex) + if not questIndex then + break + end + + local title = GetQuestLogTitle(questIndex) + local qid = select(9, GetQuestLogTitle(questIndex)) + local completed, x, y, objective = QuestPOIGetIconInfo(qid) + + if x and y then + -- Try to uniquely identify objective waypoints using qid, x, y along + -- with map and floor. This may not always work, but its the best we + -- can do right now. This allows us to ensure we don't double-set a + -- waypoint. + + if completed then + title = "Turn in: " .. title + end + + local key = title .. tostring(qid + (x * 100) * (y * 100) * map * (floor + 1)) + + if waypoints[key] then + -- This waypoint already exists, no need to do anything, except + -- possibly change the priority + + local waypoint = waypoints[key] + local newpri = (watchIndex == 1) and PRI_FIRST or PRI_OTHER + if waypoint.priority ~= newpri then + changed = true + waypoint.priority = newpri + end + + newWaypoints[key] = waypoint + else + -- Create the waypoint, setting priority + local waypoint = self:AddWaypoint(map, floor, x, y, { + title = title, + priority = (watchIndex == 1) and PRI_FIRST or PRI_OTHER, + }) + + newWaypoints[key] = waypoint + end + end + + watchIndex = watchIndex + 1 + end + + SetCVar("questPOI", cvar and 1 or 0) + + -- Check to see if there are any waypoints that are in 'waypoints' but + -- not in 'newWaypoints' so we can remove them. Additionally, we may have + -- changed a waypoint's priority during the scan, so trigger an update in + -- that case. + + for k, waypoint in pairs(newWaypoints) do + waypoints[k] = nil + end + + for k, waypoint in pairs(waypoints) do + -- This waypoint is no longer being tracked + addon:RemoveWaypoint(waypoint) + end + + -- Swap the arrays + waypoints = newWaypoints + + if changed then + addon:FireMessage("TOMTOMLITE_WAYPOINTS_CHANGED") + end +end -- 1.7.9.5