--[[-------------------------------------------------------------------
-- TomTomLite - Copyright 2010 - James N. Whitehead II
-------------------------------------------------------------------]]--
local addonName, addon = ...
local L = addon.L
addon.callbacks = LibStub("CallbackHandler-1.0"):New(addon)
addon.libwindow = LibStub("LibWindow-1.1")
-- Set up some tables to track waypoints
do
local cache = {}
addon.waypoints = setmetatable({}, {
__newindex = function(t, k, v)
if type(v) == "nil" then
-- A waypoint is being removed
local oldwaypoint = rawget(t, k)
rawset(t, k, nil)
local map = oldwaypoint[1]
if map then
cache[map] = nil
end
else
-- A waypoint is being added
rawset(t, k, v)
local map = v[1]
if map then
cache[map] = nil
end
end
end,
})
addon.waypointsByMap = setmetatable({}, {
__index = function(t, k)
local cached = cache[k]
if cached then
return cached
end
-- Look up all the waypoints for a given mapId
local set = {}
for idx, waypoint in ipairs(addon.waypoints) do
if waypoint[1] == k then
table.insert(set, waypoint)
end
end
rawset(cache, k, set)
return set
end
})
addon.sources = {}
end
function addon:Initialize()
self.db = LibStub("AceDB-3.0"):New("TomTomLiteDB", self.defaults)
self.arrow = self:CreateCrazyArrow("TomTomLiteArrow")
self.arrow:SetPoint("CENTER", 0, 0)
self.arrow:Hide()
self:RegisterMessage("TOMTOMLITE_WAYPOINT_ADDED")
self:RegisterMessage("TOMTOMLITE_WAYPOINTS_CHANGED")
-- Events for world map overlays
self:RegisterEvent("WORLD_MAP_UPDATE")
end
function addon:CreateCrazyArrow(name, parent)
parent = parent or UIParent
local frame = CreateFrame("Button", name, parent)
frame:SetSize(128, 128)
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")
frame.title:SetPoint("TOP", frame, "BOTTOM", 0, 0)
frame.info:SetPoint("TOP", frame.title, "BOTTOM", 0, 0)
frame.subtitle:SetPoint("TOP", frame.info, "BOTTOM", 0, 0)
frame:Hide()
local PI2 = math.pi * 2
-- Set up the OnUpdate handler
frame:SetScript("OnUpdate", function(self, elapsed)
local map, floor, x, y = unpack(self.waypoint)
local distance, angle = addon:GetVectorFromCurrent(map, floor, x, y)
local facing = GetPlayerFacing()
local faceangle = angle - facing
local perc = math.abs((math.pi - math.abs(faceangle)) / math.pi)
local gr,gg,gb = unpack(addon.db.profile.goodcolor)
local mr,mg,mb = unpack(addon.db.profile.middlecolor)
local br,bg,bb = unpack(addon.db.profile.badcolor)
local r,g,b = addon:ColorGradient(perc, br, bg, bb, mr, mg, mb, gr, gg, gb)
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)
self.libwindow.RegisterConfig(frame, self.db.profile.positions)
self.libwindow.RestorePosition(frame)
self.libwindow.MakeDraggable(frame)
self.libwindow.EnableMouseOnAlt(frame)
self.libwindow.EnableMouseWheelScaling(frame)
return frame
end
--[[-------------------------------------------------------------------------
-- External API
-------------------------------------------------------------------------]]--
-- Register a new waypoint source for use with TomTomLite. The build-in sources
-- are 'objective' and 'corpse'. These can just be used in the user interface
-- to allow the user to filter/mask different sources and to set priority
-- modifiers.
--
-- Arguments:
-- stype - A short 'type' string used to identify the source of a waypoint
-- name - The localized name of the source, for use in the user interface
-- desc - Localized long description of the source type
-- opt - A table containing any other options, for future-use
function addon:RegisterSource(stype, name, desc, opt)
local sources = {
type = stype,
name = name,
desc = desc,
}
if opt then
for k,v in pairs(opt) do
if sources[k] then
local err = string.format(L["Source '%s' registered with invalid option '%s'"], name, k)
error(err)
else
sources[k] = v
end
end
end
table.insert(self.sources, source)
end
-- Add a new waypoint to TomTomLite. This may not cause the waypoint to be
-- immediately displayed, just simply adds it to the collection of waypoints
-- that TomTomLite knows about. In general, a waypoint will be added and then
-- between user options and waypoint priorities one or more may be chosen
-- to be displayed.
--
-- Arguments:
-- map - The numeric map ID for the given waypoint. These map ids are
-- unique for a given map.
-- floor - The floor for the given waypoint on the specified map. This
-- argument may be nil, indicating that the 'default' floor should
-- be used, and TomTomLite will attempt to choose a sane default.
-- x - The x coordinate of the waypoint, specified as a number between 0
-- and 1. If the number specified is greater than 1, it will be
-- divided by 100 before being used by TomTomLite.
-- y - The y coordinate of the waypoint, following the same format as 'x'
-- opt - A table containing other options for the specified waypoint.
-- Currently the following options are supported:
--
-- priority - A number indicating the priority of the waypoint.
-- The default priority is 0 and the greatest should be
-- 100, indicating something that should always be
-- displayed, for example the Corpse arrow.
--
-- Returns:
-- waypoint - A table containing the information about the given waypoint and
-- serving as a unique identifier for the waypoint within TomTomLite.
function addon:AddWaypoint(map, floor, x, y, opt)
assert(type(map) == "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
if type(k) ~= "number" then
waypoint[k] = v
end
end
end
table.insert(self.waypoints, waypoint)
self:FireMessage("TOMTOMLITE_WAYPOINT_ADDED", waypoint)
return waypoint
end
-- Removes a waypoint entirely from TomTomLite.
--
-- Arguments:
-- waypoint - The unique waypoint table that was returned by 'AddWaypoint'
function addon:RemoveWaypoint(waypoint)
for idx, entry in ipairs(self.waypoints) do
if entry == waypoint then
table.remove(self.waypoints, idx)
break
end
end
self:FireMessage("TOMTOMLITE_WAYPOINT_DELETED", waypoint)
end
--[[-------------------------------------------------------------------------
-- Private implementation
-------------------------------------------------------------------------]]--
function addon:TOMTOMLITE_WAYPOINT_ADDED(msg, waypoint, ...)
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:TOMTOMLITE_WAYPOINTS_CHANGED(msg, ...)
self:UpdateArrow()
end
function addon:UpdateArrow()
-- 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 = 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
self.arrow.waypoint = nil
self.arrow:Hide()
end
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
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
--[[-------------------------------------------------------------------------
-- Slash command registration
-------------------------------------------------------------------------]]--
SLASH_TOMTOMLITE1 = "/ttl"
SLASH_TOMTOMLITE2 = "/tomtomlite"
SLASH_TOMTOMLITE3 = "/tt"
local wrongseparator = "(%d)" .. (tonumber("1.1") and "," or ".") .. "(%d)"
local rightseparator = "%1" .. (tonumber("1.1") and "." or ",") .. "%2"
SlashCmdList["TOMTOMLITE"] = function(msg, editbox)
-- Attempt to fix any pairs of coordinates in any form so they work. This
-- should correctly handle cases where the user uses a number format that
-- is different than their current locale, i.e. someone using 34,45 when
-- their numeric locale expects 34.45 (for the number 35 and 45 hundreths).
-- Additionally, if the two numbers are separaed by a comma, i.e.
-- 45.34, 54.13 then this comma will be removed.
--
-- Thanks to Phanx for working out the best way to do this
local msgfix = msg:gsub("(%d)[%.,] (%d)", "%1 %2"):gsub(wrongseparator, rightseparator)
local tokens = {}
for token in msgfix:gmatch("%S+") do
table.insert(tokens, token)
end
local verb = tokens[1] and tokens[1]:lower()
if verb == "set" then
if not tonumber(tokens[2]) then
-- A zone has been specified as the first argument so find the boundary
local zoneEnd = 2
for idx, token in ipairs(tokens) do
if tonumber(token) then
zoneEnd = idx - 1
break
end
end
local zone = table.concat(tokens, " ", 2, zoneEnd)
local x, y, desc = unpack(tokens, zoneEnd + 1)
-- The description may be multiple tokens as well
if desc then
desc = table.concat(tokens, " ", zoneEnd + 3)
end
-- TODO: Try to find a match for the zone/map name
else
self:Printf(L["Usage for /ttl:"])
self:Printf(L[" * set [zone] <x> <y> [desc] - sets a waypoint"])
end
end
end