You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

774 lines
27 KiB

4 years ago
-- HereBeDragons-Pins is a library to show pins/icons on the world map and minimap
local MAJOR, MINOR = "HereBeDragons-Pins-2.0", 12
4 years ago
assert(LibStub, MAJOR .. " requires LibStub")
local pins, _oldversion = LibStub:NewLibrary(MAJOR, MINOR)
if not pins then return end
local HBD = LibStub("HereBeDragons-2.0")
local MinimapRadiusAPI = C_Minimap and C_Minimap.GetViewRadius
pins.updateFrame = pins.updateFrame or CreateFrame("Frame")
-- storage for minimap pins
pins.minimapPins = pins.minimapPins or {}
pins.activeMinimapPins = pins.activeMinimapPins or {}
pins.minimapPinRegistry = pins.minimapPinRegistry or {}
-- and worldmap pins
pins.worldmapPins = pins.worldmapPins or {}
pins.worldmapPinRegistry = pins.worldmapPinRegistry or {}
pins.worldmapPinsPool = pins.worldmapPinsPool or CreateFramePool("FRAME")
pins.worldmapProvider = pins.worldmapProvider or CreateFromMixins(MapCanvasDataProviderMixin)
pins.worldmapProviderPin = pins.worldmapProviderPin or CreateFromMixins(MapCanvasPinMixin)
-- store a reference to the active minimap object
pins.Minimap = pins.Minimap or Minimap
-- Data Constants
local WORLD_MAP_ID = 947
-- upvalue lua api
local cos, sin, max = math.cos, math.sin, math.max
local type, pairs = type, pairs
-- upvalue wow api
local GetPlayerFacing = GetPlayerFacing
-- upvalue data tables
local minimapPins = pins.minimapPins
local activeMinimapPins = pins.activeMinimapPins
local minimapPinRegistry = pins.minimapPinRegistry
local worldmapPins = pins.worldmapPins
local worldmapPinRegistry = pins.worldmapPinRegistry
local worldmapPinsPool = pins.worldmapPinsPool
local worldmapProvider = pins.worldmapProvider
local worldmapProviderPin = pins.worldmapProviderPin
local minimap_size = {
indoor = {
[0] = 300, -- scale
[1] = 240, -- 1.25
[2] = 180, -- 5/3
[3] = 120, -- 2.5
[4] = 80, -- 3.75
[5] = 50, -- 6
},
outdoor = {
[0] = 466 + 2/3, -- scale
[1] = 400, -- 7/6
[2] = 333 + 1/3, -- 1.4
[3] = 266 + 2/6, -- 1.75
[4] = 200, -- 7/3
[5] = 133 + 1/3, -- 3.5
},
}
local minimap_shapes = {
-- { upper-left, lower-left, upper-right, lower-right }
["SQUARE"] = { false, false, false, false },
["CORNER-TOPLEFT"] = { true, false, false, false },
["CORNER-TOPRIGHT"] = { false, false, true, false },
["CORNER-BOTTOMLEFT"] = { false, true, false, false },
["CORNER-BOTTOMRIGHT"] = { false, false, false, true },
["SIDE-LEFT"] = { true, true, false, false },
["SIDE-RIGHT"] = { false, false, true, true },
["SIDE-TOP"] = { true, false, true, false },
["SIDE-BOTTOM"] = { false, true, false, true },
["TRICORNER-TOPLEFT"] = { true, true, true, false },
["TRICORNER-TOPRIGHT"] = { true, false, true, true },
["TRICORNER-BOTTOMLEFT"] = { true, true, false, true },
["TRICORNER-BOTTOMRIGHT"] = { false, true, true, true },
}
local tableCache = setmetatable({}, {__mode='k'})
local function newCachedTable()
local t = next(tableCache)
if t then
tableCache[t] = nil
else
t = {}
end
return t
end
local function recycle(t)
tableCache[t] = true
end
-------------------------------------------------------------------------------------------
-- Minimap pin position logic
-- minimap rotation
local rotateMinimap = GetCVar("rotateMinimap") == "1"
-- is the minimap indoors or outdoors
local indoors = GetCVar("minimapZoom")+0 == pins.Minimap:GetZoom() and "outdoor" or "indoor"
local minimapPinCount, queueFullUpdate = 0, false
local minimapScale, minimapShape, mapRadius, minimapWidth, minimapHeight, mapSin, mapCos
local lastZoom, lastFacing, lastXY, lastYY
local function drawMinimapPin(pin, data)
local xDist, yDist = lastXY - data.x, lastYY - data.y
-- handle rotation
if rotateMinimap then
local dx, dy = xDist, yDist
xDist = dx*mapCos - dy*mapSin
yDist = dx*mapSin + dy*mapCos
end
-- adapt delta position to the map radius
local diffX = xDist / mapRadius
local diffY = yDist / mapRadius
-- different minimap shapes
local isRound = true
if minimapShape and not (xDist == 0 or yDist == 0) then
isRound = (xDist < 0) and 1 or 3
if yDist < 0 then
isRound = minimapShape[isRound]
else
isRound = minimapShape[isRound + 1]
end
end
-- calculate distance from the center of the map
local dist
if isRound then
dist = (diffX*diffX + diffY*diffY) / 0.9^2
else
dist = max(diffX*diffX, diffY*diffY) / 0.9^2
end
-- if distance > 1, then adapt node position to slide on the border
if dist > 1 and data.floatOnEdge then
dist = dist^0.5
diffX = diffX/dist
diffY = diffY/dist
end
if dist <= 1 or data.floatOnEdge then
pin:Show()
pin:ClearAllPoints()
pin:SetPoint("CENTER", pins.Minimap, "CENTER", diffX * minimapWidth, -diffY * minimapHeight)
data.onEdge = (dist > 1)
else
pin:Hide()
data.onEdge = nil
pin.keep = nil
end
end
local function IsParentMap(originMapId, toCheckMapId)
local parentMapID = HBD.mapData[originMapId].parent
while parentMapID and HBD.mapData[parentMapID] do
local mapType = HBD.mapData[parentMapID].mapType
if mapType ~= Enum.UIMapType.Zone and mapType ~= Enum.UIMapType.Dungeon and mapType ~= Enum.UIMapType.Micro then
return false
end
if parentMapID == toCheckMapId then
return true
end
parentMapID = HBD.mapData[parentMapID].parent
end
return false
end
local function UpdateMinimapPins(force)
-- get the current player position
local x, y, instanceID = HBD:GetPlayerWorldPosition()
local mapID = HBD:GetPlayerZone()
-- get data from the API for calculations
local zoom = pins.Minimap:GetZoom()
local diffZoom = zoom ~= lastZoom
-- for rotating minimap support
local facing
if rotateMinimap then
facing = GetPlayerFacing()
else
facing = lastFacing
end
-- check for all values to be available (starting with 7.1.0, instances don't report coordinates)
if not x or not y or (rotateMinimap and not facing) then
minimapPinCount = 0
for pin in pairs(activeMinimapPins) do
pin:Hide()
activeMinimapPins[pin] = nil
end
return
end
local newScale = pins.Minimap:GetScale()
if minimapScale ~= newScale then
minimapScale = newScale
force = true
end
if x ~= lastXY or y ~= lastYY or diffZoom or facing ~= lastFacing or force then
-- minimap information
minimapShape = GetMinimapShape and minimap_shapes[GetMinimapShape() or "ROUND"]
minimapWidth = pins.Minimap:GetWidth() / 2
minimapHeight = pins.Minimap:GetHeight() / 2
if MinimapRadiusAPI then
mapRadius = C_Minimap.GetViewRadius()
else
mapRadius = minimap_size[indoors][zoom] / 2
end
-- update upvalues for icon placement
lastZoom = zoom
lastFacing = facing
lastXY, lastYY = x, y
if rotateMinimap then
mapSin = sin(facing)
mapCos = cos(facing)
end
for pin, data in pairs(minimapPins) do
if data.instanceID == instanceID and (not data.uiMapID or data.uiMapID == mapID or (data.showInParentZone and IsParentMap(data.uiMapID, mapID))) then
activeMinimapPins[pin] = data
data.keep = true
-- draw the pin (this may reset data.keep if outside of the map)
drawMinimapPin(pin, data)
end
end
minimapPinCount = 0
for pin, data in pairs(activeMinimapPins) do
if not data.keep then
pin:Hide()
activeMinimapPins[pin] = nil
else
minimapPinCount = minimapPinCount + 1
data.keep = nil
end
end
end
end
local function UpdateMinimapIconPosition()
-- get the current map zoom
local zoom = pins.Minimap:GetZoom()
local diffZoom = zoom ~= lastZoom
-- if the map zoom changed, run a full update sweep
if diffZoom then
UpdateMinimapPins()
return
end
-- we have no active minimap pins, just return early
if minimapPinCount == 0 then return end
local x, y = HBD:GetPlayerWorldPosition()
-- for rotating minimap support
local facing
if rotateMinimap then
facing = GetPlayerFacing()
else
facing = lastFacing
end
-- check for all values to be available (starting with 7.1.0, instances don't report coordinates)
if not x or not y or (rotateMinimap and not facing) then
UpdateMinimapPins()
return
end
local refresh
local newScale = pins.Minimap:GetScale()
if minimapScale ~= newScale then
minimapScale = newScale
refresh = true
end
if x ~= lastXY or y ~= lastYY or facing ~= lastFacing or refresh then
-- update radius of the map
if MinimapRadiusAPI then
mapRadius = C_Minimap.GetViewRadius()
else
mapRadius = minimap_size[indoors][zoom] / 2
end
-- update upvalues for icon placement
lastXY, lastYY = x, y
lastFacing = facing
if rotateMinimap then
mapSin = sin(facing)
mapCos = cos(facing)
end
-- iterate all nodes and check if they are still in range of our minimap display
for pin, data in pairs(activeMinimapPins) do
-- update the position of the node
drawMinimapPin(pin, data)
end
end
end
local function UpdateMinimapZoom()
if not MinimapRadiusAPI then
local zoom = pins.Minimap:GetZoom()
if GetCVar("minimapZoom") == GetCVar("minimapInsideZoom") then
pins.Minimap:SetZoom(zoom < 2 and zoom + 1 or zoom - 1)
end
indoors = GetCVar("minimapZoom")+0 == pins.Minimap:GetZoom() and "outdoor" or "indoor"
pins.Minimap:SetZoom(zoom)
end
end
-------------------------------------------------------------------------------------------
-- WorldMap data provider
-- setup pin pool
worldmapPinsPool.parent = WorldMapFrame:GetCanvas()
worldmapPinsPool.creationFunc = function(framePool)
local frame = CreateFrame(framePool.frameType, nil, framePool.parent)
frame:SetSize(1, 1)
return Mixin(frame, worldmapProviderPin)
end
worldmapPinsPool.resetterFunc = function(pinPool, pin)
FramePool_HideAndClearAnchors(pinPool, pin)
pin:OnReleased()
pin.pinTemplate = nil
pin.owningMap = nil
end
-- register pin pool with the world map
WorldMapFrame.pinPools["HereBeDragonsPinsTemplate"] = worldmapPinsPool
-- provider base API
function worldmapProvider:RemoveAllData()
self:GetMap():RemoveAllPinsByTemplate("HereBeDragonsPinsTemplate")
end
function worldmapProvider:RemovePinByIcon(icon)
for pin in self:GetMap():EnumeratePinsByTemplate("HereBeDragonsPinsTemplate") do
if pin.icon == icon then
self:GetMap():RemovePin(pin)
end
end
end
function worldmapProvider:RemovePinsByRef(ref)
for pin in self:GetMap():EnumeratePinsByTemplate("HereBeDragonsPinsTemplate") do
if pin.icon and worldmapPinRegistry[ref][pin.icon] then
self:GetMap():RemovePin(pin)
end
end
end
function worldmapProvider:RefreshAllData(fromOnShow)
self:RemoveAllData()
for icon, data in pairs(worldmapPins) do
self:HandlePin(icon, data)
end
end
function worldmapProvider:HandlePin(icon, data)
local uiMapID = self:GetMap():GetMapID()
-- check for a valid map
if not uiMapID then return end
local x, y
if uiMapID == WORLD_MAP_ID then
-- should this pin show on the world map?
if uiMapID ~= data.uiMapID and data.worldMapShowFlag ~= HBD_PINS_WORLDMAP_SHOW_WORLD then return end
-- translate to the world map
x, y = HBD:GetAzerothWorldMapCoordinatesFromWorld(data.x, data.y, data.instanceID)
else
-- check that it matches the instance
if not HBD.mapData[uiMapID] or HBD.mapData[uiMapID].instance ~= data.instanceID then return end
if uiMapID ~= data.uiMapID then
local mapType = HBD.mapData[uiMapID].mapType
if not data.uiMapID then
if mapType == Enum.UIMapType.Continent and data.worldMapShowFlag >= HBD_PINS_WORLDMAP_SHOW_CONTINENT then
--pass
elseif mapType ~= Enum.UIMapType.Zone and mapType ~= Enum.UIMapType.Dungeon and mapType ~= Enum.UIMapType.Micro then
-- fail
return
end
else
local show = false
local parentMapID = HBD.mapData[data.uiMapID].parent
while parentMapID and HBD.mapData[parentMapID] do
if parentMapID == uiMapID then
local parentMapType = HBD.mapData[parentMapID].mapType
-- show on any parent zones if they are normal zones
if data.worldMapShowFlag >= HBD_PINS_WORLDMAP_SHOW_PARENT and
(parentMapType == Enum.UIMapType.Zone or parentMapType == Enum.UIMapType.Dungeon or parentMapType == Enum.UIMapType.Micro) then
show = true
-- show on the continent
elseif data.worldMapShowFlag >= HBD_PINS_WORLDMAP_SHOW_CONTINENT and
parentMapType == Enum.UIMapType.Continent then
show = true
end
break
-- worldmap is handled above already
else
parentMapID = HBD.mapData[parentMapID].parent
end
end
if not show then return end
end
end
-- translate coordinates
x, y = HBD:GetZoneCoordinatesFromWorld(data.x, data.y, uiMapID)
end
if x and y then
self:GetMap():AcquirePin("HereBeDragonsPinsTemplate", icon, x, y, data.frameLevelType)
end
end
-- map pin base API
function worldmapProviderPin:OnLoad()
self:UseFrameLevelType("PIN_FRAME_LEVEL_AREA_POI")
self:SetScalingLimits(1, 1.0, 1.2)
end
function worldmapProviderPin:OnAcquired(icon, x, y, frameLevelType)
self:UseFrameLevelType(frameLevelType or "PIN_FRAME_LEVEL_AREA_POI")
self:SetPosition(x, y)
self.icon = icon
icon:SetParent(self)
icon:ClearAllPoints()
icon:SetPoint("CENTER", self, "CENTER")
icon:Show()
end
function worldmapProviderPin:OnReleased()
if self.icon then
self.icon:Hide()
self.icon:SetParent(UIParent)
self.icon:ClearAllPoints()
self.icon = nil
end
end
-- hack to avoid in-combat error on 10.1.5
worldmapProviderPin.SetPassThroughButtons = function() end
4 years ago
-- register with the world map
WorldMapFrame:AddDataProvider(worldmapProvider)
-- map event handling
local function UpdateMinimap()
UpdateMinimapZoom()
UpdateMinimapPins()
end
local function UpdateWorldMap()
worldmapProvider:RefreshAllData()
end
local last_update = 0
local function OnUpdateHandler(frame, elapsed)
last_update = last_update + elapsed
if last_update > 1 or queueFullUpdate then
UpdateMinimapPins(queueFullUpdate)
last_update = 0
queueFullUpdate = false
else
UpdateMinimapIconPosition()
end
end
pins.updateFrame:SetScript("OnUpdate", OnUpdateHandler)
local function OnEventHandler(frame, event, ...)
if event == "CVAR_UPDATE" then
local cvar, value = ...
if cvar == "rotateMinimap" or cvar == "ROTATE_MINIMAP" then
4 years ago
rotateMinimap = (value == "1")
queueFullUpdate = true
end
elseif event == "MINIMAP_UPDATE_ZOOM" then
UpdateMinimap()
elseif event == "PLAYER_LOGIN" then
-- recheck cvars after login
rotateMinimap = GetCVar("rotateMinimap") == "1"
elseif event == "PLAYER_ENTERING_WORLD" then
UpdateMinimap()
UpdateWorldMap()
end
end
pins.updateFrame:SetScript("OnEvent", OnEventHandler)
pins.updateFrame:UnregisterAllEvents()
pins.updateFrame:RegisterEvent("CVAR_UPDATE")
pins.updateFrame:RegisterEvent("MINIMAP_UPDATE_ZOOM")
pins.updateFrame:RegisterEvent("PLAYER_LOGIN")
pins.updateFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
HBD.RegisterCallback(pins, "PlayerZoneChanged", UpdateMinimap)
--- Add a icon to the minimap (x/y world coordinate version)
-- Note: This API does not let you specify a map to limit the pin to, it'll be shown on all maps these coordinates are valid for.
-- @param ref Reference to your addon to track the icon under (ie. your "self" or string identifier)
-- @param icon Icon Frame
-- @param instanceID Instance ID of the map to add the icon to
-- @param x X position in world coordinates
-- @param y Y position in world coordinates
-- @param floatOnEdge flag if the icon should float on the edge of the minimap when going out of range, or hide immediately (default false)
function pins:AddMinimapIconWorld(ref, icon, instanceID, x, y, floatOnEdge)
if not ref then
error(MAJOR..": AddMinimapIconWorld: 'ref' must not be nil", 2)
end
if type(icon) ~= "table" or not icon.SetPoint then
error(MAJOR..": AddMinimapIconWorld: 'icon' must be a frame", 2)
end
if type(instanceID) ~= "number" or type(x) ~= "number" or type(y) ~= "number" then
error(MAJOR..": AddMinimapIconWorld: 'instanceID', 'x' and 'y' must be numbers", 2)
end
if not minimapPinRegistry[ref] then
minimapPinRegistry[ref] = {}
end
minimapPinRegistry[ref][icon] = true
local t = minimapPins[icon] or newCachedTable()
t.instanceID = instanceID
t.x = x
t.y = y
t.floatOnEdge = floatOnEdge
t.uiMapID = nil
t.showInParentZone = nil
minimapPins[icon] = t
queueFullUpdate = true
icon:SetParent(pins.Minimap)
end
--- Add a icon to the minimap (UiMapID zone coordinate version)
-- The pin will only be shown on the map specified, or optionally its parent map if specified
-- @param ref Reference to your addon to track the icon under (ie. your "self" or string identifier)
-- @param icon Icon Frame
-- @param uiMapID uiMapID of the map to place the icon on
-- @param x X position in local/point coordinates (0-1), relative to the zone
-- @param y Y position in local/point coordinates (0-1), relative to the zone
-- @param showInParentZone flag if the icon should be shown in its parent zone - ie. an icon in a microdungeon in the outdoor zone itself (default false)
-- @param floatOnEdge flag if the icon should float on the edge of the minimap when going out of range, or hide immediately (default false)
function pins:AddMinimapIconMap(ref, icon, uiMapID, x, y, showInParentZone, floatOnEdge)
if not ref then
error(MAJOR..": AddMinimapIconMap: 'ref' must not be nil", 2)
end
if type(icon) ~= "table" or not icon.SetPoint then
error(MAJOR..": AddMinimapIconMap: 'icon' must be a frame", 2)
end
if type(uiMapID) ~= "number" or type(x) ~= "number" or type(y) ~= "number" then
error(MAJOR..": AddMinimapIconMap: 'uiMapID', 'x' and 'y' must be numbers", 2)
end
-- convert to world coordinates and use our known adding function
local xCoord, yCoord, instanceID = HBD:GetWorldCoordinatesFromZone(x, y, uiMapID)
if not xCoord then return end
self:AddMinimapIconWorld(ref, icon, instanceID, xCoord, yCoord, floatOnEdge)
-- store extra information
minimapPins[icon].uiMapID = uiMapID
minimapPins[icon].showInParentZone = showInParentZone
end
--- Check if a floating minimap icon is on the edge of the map
-- @param icon the minimap icon
function pins:IsMinimapIconOnEdge(icon)
if not icon then return false end
local data = minimapPins[icon]
if not data then return nil end
return data.onEdge
end
--- Remove a minimap icon
-- @param ref Reference to your addon to track the icon under (ie. your "self" or string identifier)
-- @param icon Icon Frame
function pins:RemoveMinimapIcon(ref, icon)
if not ref or not icon or not minimapPinRegistry[ref] then return end
minimapPinRegistry[ref][icon] = nil
if minimapPins[icon] then
recycle(minimapPins[icon])
minimapPins[icon] = nil
activeMinimapPins[icon] = nil
end
icon:Hide()
end
--- Remove all minimap icons belonging to your addon (as tracked by "ref")
-- @param ref Reference to your addon to track the icon under (ie. your "self" or string identifier)
function pins:RemoveAllMinimapIcons(ref)
if not ref or not minimapPinRegistry[ref] then return end
for icon in pairs(minimapPinRegistry[ref]) do
recycle(minimapPins[icon])
minimapPins[icon] = nil
activeMinimapPins[icon] = nil
icon:Hide()
end
wipe(minimapPinRegistry[ref])
end
--- Set the minimap object to position the pins on. Needs to support the usual functions a Minimap-type object exposes.
-- @param minimapObject The new minimap object, or nil to restore the default
function pins:SetMinimapObject(minimapObject)
pins.Minimap = minimapObject or Minimap
for pin in pairs(minimapPins) do
pin:SetParent(pins.Minimap)
end
UpdateMinimapPins(true)
end
-- world map constants
-- show worldmap pin on its parent zone map (if any)
HBD_PINS_WORLDMAP_SHOW_PARENT = 1
-- show worldmap pin on the continent map
HBD_PINS_WORLDMAP_SHOW_CONTINENT = 2
-- show worldmap pin on the continent and world map
HBD_PINS_WORLDMAP_SHOW_WORLD = 3
--- Add a icon to the world map (x/y world coordinate version)
-- Note: This API does not let you specify a map to limit the pin to, it'll be shown on all maps these coordinates are valid for.
-- @param ref Reference to your addon to track the icon under (ie. your "self" or string identifier)
-- @param icon Icon Frame
-- @param instanceID Instance ID of the map to add the icon to
-- @param x X position in world coordinates
-- @param y Y position in world coordinates
-- @param showFlag Flag to control on which maps this pin will be shown
-- @param frameLevel Optional Frame Level type registered with the WorldMapFrame, defaults to PIN_FRAME_LEVEL_AREA_POI
function pins:AddWorldMapIconWorld(ref, icon, instanceID, x, y, showFlag, frameLevel)
if not ref then
error(MAJOR..": AddWorldMapIconWorld: 'ref' must not be nil", 2)
end
if type(icon) ~= "table" or not icon.SetPoint then
error(MAJOR..": AddWorldMapIconWorld: 'icon' must be a frame", 2)
end
if type(instanceID) ~= "number" or type(x) ~= "number" or type(y) ~= "number" then
error(MAJOR..": AddWorldMapIconWorld: 'instanceID', 'x' and 'y' must be numbers", 2)
end
if showFlag ~= nil and type(showFlag) ~= "number" then
error(MAJOR..": AddWorldMapIconWorld: 'showFlag' must be a number (or nil)", 2)
end
if not worldmapPinRegistry[ref] then
worldmapPinRegistry[ref] = {}
end
worldmapPinRegistry[ref][icon] = true
local t = worldmapPins[icon] or newCachedTable()
t.instanceID = instanceID
t.x = x
t.y = y
t.uiMapID = nil
t.worldMapShowFlag = showFlag or 0
t.frameLevelType = frameLevel
worldmapPins[icon] = t
worldmapProvider:HandlePin(icon, t)
end
--- Add a icon to the world map (uiMapID zone coordinate version)
-- @param ref Reference to your addon to track the icon under (ie. your "self" or string identifier)
-- @param icon Icon Frame
-- @param uiMapID uiMapID of the map to place the icon on
-- @param x X position in local/point coordinates (0-1), relative to the zone
-- @param y Y position in local/point coordinates (0-1), relative to the zone
-- @param showFlag Flag to control on which maps this pin will be shown
-- @param frameLevel Optional Frame Level type registered with the WorldMapFrame, defaults to PIN_FRAME_LEVEL_AREA_POI
function pins:AddWorldMapIconMap(ref, icon, uiMapID, x, y, showFlag, frameLevel)
if not ref then
error(MAJOR..": AddWorldMapIconMap: 'ref' must not be nil", 2)
end
if type(icon) ~= "table" or not icon.SetPoint then
error(MAJOR..": AddWorldMapIconMap: 'icon' must be a frame", 2)
end
if type(uiMapID) ~= "number" or type(x) ~= "number" or type(y) ~= "number" then
error(MAJOR..": AddWorldMapIconMap: 'uiMapID', 'x' and 'y' must be numbers", 2)
end
if showFlag ~= nil and type(showFlag) ~= "number" then
error(MAJOR..": AddWorldMapIconMap: 'showFlag' must be a number (or nil)", 2)
end
-- convert to world coordinates
local xCoord, yCoord, instanceID = HBD:GetWorldCoordinatesFromZone(x, y, uiMapID)
if not xCoord then return end
if not worldmapPinRegistry[ref] then
worldmapPinRegistry[ref] = {}
end
worldmapPinRegistry[ref][icon] = true
local t = worldmapPins[icon] or newCachedTable()
t.instanceID = instanceID
t.x = xCoord
t.y = yCoord
t.uiMapID = uiMapID
t.worldMapShowFlag = showFlag or 0
t.frameLevelType = frameLevel
worldmapPins[icon] = t
worldmapProvider:HandlePin(icon, t)
end
--- Remove a worldmap icon
-- @param ref Reference to your addon to track the icon under (ie. your "self" or string identifier)
-- @param icon Icon Frame
function pins:RemoveWorldMapIcon(ref, icon)
if not ref or not icon or not worldmapPinRegistry[ref] then return end
worldmapPinRegistry[ref][icon] = nil
if worldmapPins[icon] then
recycle(worldmapPins[icon])
worldmapPins[icon] = nil
end
worldmapProvider:RemovePinByIcon(icon)
end
--- Remove all worldmap icons belonging to your addon (as tracked by "ref")
-- @param ref Reference to your addon to track the icon under (ie. your "self" or string identifier)
function pins:RemoveAllWorldMapIcons(ref)
if not ref or not worldmapPinRegistry[ref] then return end
for icon in pairs(worldmapPinRegistry[ref]) do
recycle(worldmapPins[icon])
worldmapPins[icon] = nil
end
worldmapProvider:RemovePinsByRef(ref)
wipe(worldmapPinRegistry[ref])
end
--- Return the angle and distance from the player to the specified pin
-- @param icon icon object (minimap or worldmap)
-- @return angle, distance where angle is in radians and distance in yards
function pins:GetVectorToIcon(icon)
if not icon then return nil, nil end
local data = minimapPins[icon] or worldmapPins[icon]
if not data then return nil, nil end
local x, y, instance = HBD:GetPlayerWorldPosition()
if not x or not y or instance ~= data.instanceID then return nil end
return HBD:GetWorldVector(instance, x, y, data.x, data.y)
end