Quantcast

Update HereBeDragons to 2.01-release, resolves #6

Ludovicus [03-11-19 - 23:23]
Update HereBeDragons to 2.01-release, resolves #6
Filename
libs/HereBeDragons/CHANGES.txt
libs/HereBeDragons/HereBeDragons-1.0.lua
libs/HereBeDragons/HereBeDragons-2.0.lua
libs/HereBeDragons/HereBeDragons-Migrate.lua
libs/HereBeDragons/HereBeDragons-Pins-1.0.lua
libs/HereBeDragons/HereBeDragons-Pins-2.0.lua
diff --git a/libs/HereBeDragons/CHANGES.txt b/libs/HereBeDragons/CHANGES.txt
index 99c6647..b59a35d 100755
--- a/libs/HereBeDragons/CHANGES.txt
+++ b/libs/HereBeDragons/CHANGES.txt
@@ -1,58 +1,41 @@
-tag cd12ccfd13832690f1422c5795a3921c452298a7 1.92-beta
+tag 529cf963909b0c048d607d9fb35e549e2867a61c 2.01-release
 Author:	Hendrik Leppkes <h.leppkes@gmail.com>
-Date:	Sat Jun 23 20:03:11 2018 +0200
+Date:	Thu Jan 24 10:17:51 2019 +0100

-Tag as 1.92-beta
+Tag as 2.01-release

-commit f1b0f720ccce59c8139ae5e0cbfac21100c24874
+commit 6df387471e91795b8152b6271ce7a8fa15c9d151
 Author: Hendrik Leppkes <h.leppkes@gmail.com>
-Date:   Sat Jun 23 18:53:33 2018 +0200
+Date:   Thu Jan 24 10:17:20 2019 +0100

-    Fix minimap drawing zone limitation
+    Remove load guards

-commit d087939c3b4734ac18a010420b12a4a3810ab5a2
+commit 601d62a39f98f533d81027473efdfe5f52672df5
 Author: Hendrik Leppkes <h.leppkes@gmail.com>
-Date:   Sat Jun 23 18:53:18 2018 +0200
+Date:   Thu Jan 24 10:06:55 2019 +0100

-    Use HBD parent map data for pin drawing
+    Add support for specifying the frame level category for world map pins

-commit 70c7ea002a3d702dd482958d11b7660f6ad36b7c
+commit 41e7284588afe1f43f987df7efd4669c5467ebb4
 Author: Hendrik Leppkes <h.leppkes@gmail.com>
-Date:   Sat Jun 23 18:52:40 2018 +0200
+Date:   Tue Jan 22 13:13:53 2019 +0100

-    Store the maps parent in the data table
-
-commit 3ecf06773358df59c80f66ae91de16da88b4264e
-Author: Hendrik Leppkes <h.leppkes@gmail.com>
-Date:   Sat Jun 23 18:27:36 2018 +0200
-
-    Fix show flag handling in world map drawing for instance-based coordinates
-
-commit ec1e0d064b7a02ac97980fc5b7fc10adda044390
-Author: Hendrik Leppkes <h.leppkes@gmail.com>
-Date:   Wed Jun 20 20:19:26 2018 +0200
-
-    Set a scaling limit for the pins so they show in a consistent size on all maps
+    Remove HereBeDragons-1.0 from the source

-    Fixes WoWAce Issue #12
+    1.0 was developed for Legion, and has not functioned in WoW 8.0

-commit e8e542646986796906d14abe6a8cba716e2f58c6
+commit 3ee227ed2b69d15ffeab4c9b06f06141dc879f78
 Author: Hendrik Leppkes <h.leppkes@gmail.com>
-Date:   Wed Jun 20 20:19:01 2018 +0200
+Date:   Mon Sep 10 11:09:08 2018 +0200

-    Properly reset existing icons when re-using pins
-
-    Fixes GitHub Issue #4
+    Use the parent parameter instead of accessing the table, it holds the same value

-commit 61383763a1ce7b87379d50b534150693e2d17ab7
+commit 6f03f6f9297155fba47b03f16c29dabd7445081d
 Author: Hendrik Leppkes <h.leppkes@gmail.com>
-Date:   Wed Jun 13 19:50:42 2018 +0200
-
-    Fix drawing pins on the world map
+Date:   Mon Sep 10 11:07:18 2018 +0200

-commit af699f6637a2d07ba3000273534116aefaffc679
-Author: Hendrik Leppkes <h.leppkes@gmail.com>
-Date:   Sat May 26 19:15:44 2018 +0200
-
-    Optimize transform storage for faster lookups
+    Improve map discovery to handle improperly flagged dungeon floor maps
+
+    These maps sometimes do not show up as a child, and can only be detected
+    by checking the map group.

diff --git a/libs/HereBeDragons/HereBeDragons-1.0.lua b/libs/HereBeDragons/HereBeDragons-1.0.lua
deleted file mode 100755
index 78faf34..0000000
--- a/libs/HereBeDragons/HereBeDragons-1.0.lua
+++ /dev/null
@@ -1,776 +0,0 @@
--- HereBeDragons is a data API for the World of Warcraft mapping system
-
--- HereBeDragons-1.0 is not supported on WoW 8.0
-if select(4, GetBuildInfo()) >= 80000 then
-	return
-end
-
-local MAJOR, MINOR = "HereBeDragons-1.0", 33
-assert(LibStub, MAJOR .. " requires LibStub")
-
-local HereBeDragons, oldversion = LibStub:NewLibrary(MAJOR, MINOR)
-if not HereBeDragons then return end
-
-local CBH = LibStub("CallbackHandler-1.0")
-
-HereBeDragons.eventFrame       = HereBeDragons.eventFrame or CreateFrame("Frame")
-
-HereBeDragons.mapData          = HereBeDragons.mapData or {}
-HereBeDragons.continentZoneMap = HereBeDragons.continentZoneMap or { [-1] = { [0] = WORLDMAP_COSMIC_ID }, [0] = { [0] = WORLDMAP_AZEROTH_ID }}
-HereBeDragons.mapToID          = HereBeDragons.mapToID or { Cosmic = WORLDMAP_COSMIC_ID, World = WORLDMAP_AZEROTH_ID }
-HereBeDragons.microDungeons    = HereBeDragons.microDungeons or {}
-HereBeDragons.transforms       = HereBeDragons.transforms or {}
-
-HereBeDragons.callbacks        = HereBeDragons.callbacks or CBH:New(HereBeDragons, nil, nil, false)
-
--- constants
-local TERRAIN_MATCH = "_terrain%d+$"
-
--- Lua upvalues
-local PI2 = math.pi * 2
-local atan2 = math.atan2
-local pairs, ipairs = pairs, ipairs
-local type = type
-local band = bit.band
-
--- WoW API upvalues
-local UnitPosition = UnitPosition
-
--- data table upvalues
-local mapData          = HereBeDragons.mapData -- table { width, height, left, top }
-local continentZoneMap = HereBeDragons.continentZoneMap
-local mapToID          = HereBeDragons.mapToID
-local microDungeons    = HereBeDragons.microDungeons
-local transforms       = HereBeDragons.transforms
-
-local currentPlayerZoneMapID, currentPlayerLevel, currentMapFile, currentMapIsMicroDungeon
-
--- Override instance ids for phased content
-local instanceIDOverrides = {
-    -- Draenor
-    [1152] = 1116, -- Horde Garrison 1
-    [1330] = 1116, -- Horde Garrison 2
-    [1153] = 1116, -- Horde Garrison 3
-    [1154] = 1116, -- Horde Garrison 4 (unused)
-    [1158] = 1116, -- Alliance Garrison 1
-    [1331] = 1116, -- Alliance Garrison 2
-    [1159] = 1116, -- Alliance Garrison 3
-    [1160] = 1116, -- Alliance Garrison 4 (unused)
-    [1191] = 1116, -- Ashran PvP Zone
-    [1203] = 1116, -- Frostfire Finale Scenario
-    [1207] = 1116, -- Talador Finale Scenario
-    [1277] = 1116, -- Defense of Karabor Scenario (SMV)
-    [1402] = 1116, -- Gorgrond Finale Scenario
-    [1464] = 1116, -- Tanaan
-    [1465] = 1116, -- Tanaan
-    -- Legion
-    [1478] = 1220, -- Temple of Elune Scenario (Val'Sharah)
-    [1495] = 1220, -- Protection Paladin Artifact Scenario (Stormheim)
-    [1498] = 1220, -- Havoc Demon Hunter Artifact Scenario (Suramar)
-    [1502] = 1220, -- Dalaran Underbelly
-    [1533] = 0,    -- Karazhan Artifact Scenario
-    [1612] = 1220, -- Feral Druid Artifact Scenario (Suramar)
-    [1626] = 1220, -- Suramar Withered Scenario
-    [1662] = 1220, -- Suramar Invasion Scenario
-}
-
--- unregister and store all WORLD_MAP_UPDATE registrants, to avoid excess processing when
--- retrieving info from stateful map APIs
-local wmuRegistry
-local function UnregisterWMU()
-    wmuRegistry = {GetFramesRegisteredForEvent("WORLD_MAP_UPDATE")}
-    for _, frame in ipairs(wmuRegistry) do
-        frame:UnregisterEvent("WORLD_MAP_UPDATE")
-    end
-end
-
--- restore WORLD_MAP_UPDATE to all frames in the registry
-local function RestoreWMU()
-    assert(wmuRegistry)
-    for _, frame in ipairs(wmuRegistry) do
-        frame:RegisterEvent("WORLD_MAP_UPDATE")
-    end
-    wmuRegistry = nil
-end
-
--- gather map info, but only if this isn't an upgrade (or the upgrade version forces a re-map)
-if not oldversion or oldversion < 33 then
-    -- wipe old data, if required, otherwise the upgrade path isn't triggered
-    if oldversion then
-        wipe(mapData)
-        wipe(microDungeons)
-    end
-
-    local MAPS_TO_REMAP = {
-         -- alliance garrison
-        [973] = 971,
-        [974] = 971,
-        [975] = 971,
-        [991] = 971,
-        -- horde garrison
-        [980] = 976,
-        [981] = 976,
-        [982] = 976,
-        [990] = 976,
-    }
-
-    -- some zones will remap initially, but have a fixup later
-    local REMAP_FIXUP_EXEMPT = {
-        -- main draenor garrison maps
-        [971] = true,
-        [976] = true,
-
-        -- legion class halls
-        [1072] = { Z = 10, mapFile = "TrueshotLodge" }, -- true shot lodge
-        [1077] = { Z = 7,  mapFile = "TheDreamgrove" }, -- dreamgrove
-    }
-
-    local function processTransforms()
-        wipe(transforms)
-        for _, tID in ipairs(GetWorldMapTransforms()) do
-            local terrainMapID, newTerrainMapID, _, _, transformMinY, transformMaxY, transformMinX, transformMaxX, offsetY, offsetX, flags = GetWorldMapTransformInfo(tID)
-            -- flag 4 indicates the transform is only for the flight map
-            if band(flags, 4) ~= 4 and (offsetY ~= 0 or offsetX ~= 0) then
-                local transform = {
-                    instanceID = terrainMapID,
-                    newInstanceID = newTerrainMapID,
-                    minY = transformMinY,
-                    maxY = transformMaxY,
-                    minX = transformMinX,
-                    maxX = transformMaxX,
-                    offsetY = offsetY,
-                    offsetX = offsetX
-                }
-                table.insert(transforms, transform)
-            end
-        end
-    end
-
-    local function applyMapTransforms(instanceID, left, right, top, bottom)
-        for _, transformData in ipairs(transforms) do
-            if transformData.instanceID == instanceID then
-                if left < transformData.maxX and right > transformData.minX and top < transformData.maxY and bottom > transformData.minY then
-                    instanceID = transformData.newInstanceID
-                    left   = left   + transformData.offsetX
-                    right  = right  + transformData.offsetX
-                    top    = top    + transformData.offsetY
-                    bottom = bottom + transformData.offsetY
-                    break
-                end
-            end
-        end
-        return instanceID, left, right, top, bottom
-    end
-
-    -- gather the data of one zone (by mapID)
-    local function processZone(id)
-        if not id or mapData[id] then return end
-
-        -- set the map and verify it could be set
-        local success = SetMapByID(id)
-        if not success then
-            return
-        elseif id ~= GetCurrentMapAreaID() and not REMAP_FIXUP_EXEMPT[id] then
-            -- this is an alias zone (phasing terrain changes), just skip it and remap it later
-            if not MAPS_TO_REMAP[id] then
-                MAPS_TO_REMAP[id] = GetCurrentMapAreaID()
-            end
-            return
-        end
-
-        -- dimensions of the map
-        local originalInstanceID, _, _, left, right, top, bottom = GetAreaMapInfo(id)
-        local instanceID = originalInstanceID
-        if (left and top and right and bottom and (left ~= 0 or top ~= 0 or right ~= 0 or bottom ~= 0)) then
-            instanceID, left, right, top, bottom = applyMapTransforms(originalInstanceID, left, right, top, bottom)
-            mapData[id] = { left - right, top - bottom, left, top }
-        else
-            mapData[id] = { 0, 0, 0, 0 }
-        end
-
-        mapData[id].instance = instanceID
-        mapData[id].name = GetMapNameByID(id)
-
-        -- store the original instance id (ie. not remapped for map transforms) for micro dungeons
-        mapData[id].originalInstance = originalInstanceID
-
-        local mapFile = type(REMAP_FIXUP_EXEMPT[id]) == "table" and REMAP_FIXUP_EXEMPT[id].mapFile or GetMapInfo()
-        if mapFile then
-            -- remove phased terrain from the map names
-            mapFile = mapFile:gsub(TERRAIN_MATCH, "")
-
-            if not mapToID[mapFile] then mapToID[mapFile] = id end
-            mapData[id].mapFile = mapFile
-        end
-
-        local C, Z = GetCurrentMapContinent(), GetCurrentMapZone()
-
-        -- maps that remap generally have wrong C/Z info, so allow the fixup table to override it
-        if type(REMAP_FIXUP_EXEMPT[id]) == "table" then
-            C = REMAP_FIXUP_EXEMPT[id].C or C
-            Z = REMAP_FIXUP_EXEMPT[id].Z or Z
-        end
-
-        mapData[id].C = C or -100
-        mapData[id].Z = Z or -100
-
-        if mapData[id].C > 0 and mapData[id].Z >= 0 then
-            -- store C/Z lookup table
-            if not continentZoneMap[C] then
-                continentZoneMap[C] = {}
-            end
-            if not continentZoneMap[C][Z] then
-                continentZoneMap[C][Z] = id
-            end
-        end
-
-        -- retrieve floors
-        local floors = { GetNumDungeonMapLevels() }
-
-        -- offset floors for terrain map
-        if DungeonUsesTerrainMap() then
-            for i = 1, #floors do
-                floors[i] = floors[i] + 1
-            end
-        end
-
-        -- check for fake floors
-        if #floors == 0 and GetCurrentMapDungeonLevel() > 0 then
-            floors[1] = GetCurrentMapDungeonLevel()
-            mapData[id].fakefloor = GetCurrentMapDungeonLevel()
-        end
-
-        mapData[id].floors = {}
-        mapData[id].numFloors = #floors
-        for i = 1, mapData[id].numFloors do
-            local f = floors[i]
-            SetDungeonMapLevel(f)
-            local _, right, bottom, left, top = GetCurrentMapDungeonLevel()
-            if left and top and right and bottom then
-                instanceID, left, right, top, bottom = applyMapTransforms(originalInstanceID, left, right, top, bottom)
-                mapData[id].floors[f] = { left - right, top - bottom, left, top }
-                mapData[id].floors[f].instance = mapData[id].instance
-            elseif f == 1 and DungeonUsesTerrainMap() then
-                mapData[id].floors[f] = { mapData[id][1], mapData[id][2], mapData[id][3], mapData[id][4] }
-                mapData[id].floors[f].instance = mapData[id].instance
-            end
-        end
-
-        -- setup microdungeon storage if the its a zone map or has no floors of its own
-        if (mapData[id].C > 0 and mapData[id].Z > 0) or mapData[id].numFloors == 0 then
-            if not microDungeons[originalInstanceID] then
-                microDungeons[originalInstanceID] = { global = {} }
-            end
-        end
-    end
-
-    local function processMicroDungeons()
-        for _, dID in ipairs(GetDungeonMaps()) do
-            local floorIndex, minX, maxX, minY, maxY, terrainMapID, parentWorldMapID, flags = GetDungeonMapInfo(dID)
-
-            -- apply transform
-            local originalTerrainMapID = terrainMapID
-            terrainMapID, maxX, minX, maxY, minY = applyMapTransforms(terrainMapID, maxX, minX, maxY, minY)
-
-            -- check if this zone can have microdungeons
-            if microDungeons[originalTerrainMapID] then
-                -- store per-zone info
-                if not microDungeons[originalTerrainMapID][parentWorldMapID] then
-                    microDungeons[originalTerrainMapID][parentWorldMapID] = {}
-                end
-
-                microDungeons[originalTerrainMapID][parentWorldMapID][floorIndex] = { maxX - minX, maxY - minY, maxX, maxY }
-                microDungeons[originalTerrainMapID][parentWorldMapID][floorIndex].instance = terrainMapID
-
-                -- store global info, as some microdungeon are associated to the wrong zone when phasing is involved (garrison, and more)
-                -- but only store the first, since there can be overlap on the same continent otherwise
-                if not microDungeons[originalTerrainMapID].global[floorIndex] then
-                    microDungeons[originalTerrainMapID].global[floorIndex] = microDungeons[originalTerrainMapID][parentWorldMapID][floorIndex]
-                end
-            end
-        end
-    end
-
-    local function fixupZones()
-        -- fake cosmic map
-        mapData[WORLDMAP_COSMIC_ID] = {0, 0, 0, 0}
-        mapData[WORLDMAP_COSMIC_ID].instance = -1
-        mapData[WORLDMAP_COSMIC_ID].mapFile = "Cosmic"
-        mapData[WORLDMAP_COSMIC_ID].floors = {}
-        mapData[WORLDMAP_COSMIC_ID].C = -1
-        mapData[WORLDMAP_COSMIC_ID].Z = 0
-        mapData[WORLDMAP_COSMIC_ID].name = WORLD_MAP
-
-        -- fake azeroth world map
-        -- the world map has one "floor" per continent it contains, which allows
-        -- using these floors to translate coordinates from and to the world map.
-        -- note: due to artistic differences in the drawn azeroth maps, the values
-        -- used for the continents are estimates and not perfectly accurate
-        mapData[WORLDMAP_AZEROTH_ID] = { 63570, 42382, 53730, 19600 } -- Eastern Kingdoms, or floor 0
-        mapData[WORLDMAP_AZEROTH_ID].floors = {
-            -- Kalimdor
-            [1] =    { 65700, 43795, 11900, 23760, instance = 1    },
-            -- Northrend
-            [571] =  { 65700, 43795, 33440, 11960, instance = 571  },
-            -- Pandaria
-            [870] =  { 58520, 39015, 29070, 34410, instance = 870  },
-            -- Broken Isles
-            [1220] = { 96710, 64476, 63100, 29960, instance = 1220 },
-        }
-        mapData[WORLDMAP_AZEROTH_ID].instance = 0
-        mapData[WORLDMAP_AZEROTH_ID].mapFile = "World"
-        mapData[WORLDMAP_AZEROTH_ID].C = 0
-        mapData[WORLDMAP_AZEROTH_ID].Z = 0
-        mapData[WORLDMAP_AZEROTH_ID].name = WORLD_MAP
-
-        -- alliance draenor garrison
-        if mapData[971] then
-            mapData[971].Z = 5
-
-            mapToID["garrisonsmvalliance_tier1"] = 971
-            mapToID["garrisonsmvalliance_tier2"] = 971
-            mapToID["garrisonsmvalliance_tier3"] = 971
-        end
-
-        -- horde draenor garrison
-        if mapData[976] then
-            mapData[976].Z = 3
-
-            mapToID["garrisonffhorde_tier1"] = 976
-            mapToID["garrisonffhorde_tier2"] = 976
-            mapToID["garrisonffhorde_tier3"] = 976
-        end
-
-        -- remap zones with alias IDs
-        for remapID, validMapID in pairs(MAPS_TO_REMAP) do
-            if mapData[validMapID] then
-                mapData[remapID] = mapData[validMapID]
-            end
-        end
-    end
-
-    local function gatherMapData()
-        -- unregister WMU to reduce the processing burden
-        UnregisterWMU()
-
-        -- load transforms
-        processTransforms()
-
-        -- load the main zones
-        -- these should be processed first so they take precedence in the mapFile lookup table
-        local continents = {GetMapContinents()}
-        for i = 1, #continents, 2 do
-            processZone(continents[i])
-            local zones = {GetMapZones((i + 1) / 2)}
-            for z = 1, #zones, 2 do
-                processZone(zones[z])
-            end
-        end
-
-        -- process all other zones, this includes dungeons and more
-        local areas = GetAreaMaps()
-        for idx, zoneID in pairs(areas) do
-            processZone(zoneID)
-        end
-
-        -- fix a few zones with data lookup problems
-        fixupZones()
-
-        -- and finally, the microdungeons
-        processMicroDungeons()
-
-        -- restore WMU
-        RestoreWMU()
-    end
-
-    gatherMapData()
-end
-
--- Transform a set of coordinates based on the defined map transformations
-local function applyCoordinateTransforms(x, y, instanceID)
-    for _, transformData in ipairs(transforms) do
-        if transformData.instanceID == instanceID then
-            if transformData.minX <= x and transformData.maxX >= x and transformData.minY <= y and transformData.maxY >= y then
-                instanceID = transformData.newInstanceID
-                x = x + transformData.offsetX
-                y = y + transformData.offsetY
-                break
-            end
-        end
-    end
-    if instanceIDOverrides[instanceID] then
-        instanceID = instanceIDOverrides[instanceID]
-    end
-    return x, y, instanceID
-end
-
--- get the data table for a map and its level (floor)
-local function getMapDataTable(mapID, level)
-    if not mapID then return nil end
-    if type(mapID) == "string" then
-        mapID = mapID:gsub(TERRAIN_MATCH, "")
-        mapID = mapToID[mapID]
-    end
-    local data = mapData[mapID]
-    if not data then return nil end
-
-    if (type(level) ~= "number" or level == 0) and data.fakefloor then
-        level = data.fakefloor
-    end
-
-    if type(level) == "number" and level > 0 then
-        if data.floors[level] then
-            return data.floors[level]
-        elseif data.originalInstance and microDungeons[data.originalInstance] then
-            if microDungeons[data.originalInstance][mapID] and microDungeons[data.originalInstance][mapID][level] then
-                return microDungeons[data.originalInstance][mapID][level]
-            elseif microDungeons[data.originalInstance].global[level] then
-                return microDungeons[data.originalInstance].global[level]
-            end
-        end
-    else
-        return data
-    end
-end
-
-local StartUpdateTimer
-local function UpdateCurrentPosition()
-    UnregisterWMU()
-
-    -- save active map and level
-    local prevContinent
-    local prevMapID, prevLevel = GetCurrentMapAreaID(), GetCurrentMapDungeonLevel()
-
-    -- handle continent maps (751 is the maelstrom continent, which fails with SetMapByID)
-    if not prevMapID or prevMapID < 0 or prevMapID == 751 then
-        prevContinent = GetCurrentMapContinent()
-    end
-
-    -- set current map
-    SetMapToCurrentZone()
-
-    -- retrieve active values
-    local newMapID, newLevel = GetCurrentMapAreaID(), GetCurrentMapDungeonLevel()
-    local mapFile, _, _, isMicroDungeon, microFile = GetMapInfo()
-
-    -- we want to ignore any terrain phasings
-    if mapFile then
-        mapFile = mapFile:gsub(TERRAIN_MATCH, "")
-    end
-
-    -- hack to update the mapfile for the garrison map (as it changes when the player updates his garrison)
-    -- its not ideal to only update it when the player is in the garrison, but updates should only really happen then
-    if (newMapID == 971 or newMapID == 976) and mapData[newMapID] and mapFile ~= mapData[newMapID].mapFile then
-        mapData[newMapID].mapFile = mapFile
-    end
-
-    -- restore previous map
-    if prevContinent then
-        SetMapZoom(prevContinent)
-    else
-        -- reset map if it changed, or we need to go back to level 0
-        if prevMapID and (prevMapID ~= newMapID or (prevLevel ~= newLevel and prevLevel == 0)) then
-            SetMapByID(prevMapID)
-        end
-        if prevLevel and prevLevel > 0 then
-            SetDungeonMapLevel(prevLevel)
-        end
-    end
-
-    RestoreWMU()
-
-    if newMapID ~= currentPlayerZoneMapID or newLevel ~= currentPlayerLevel then
-        -- store micro dungeon map lookup, if available
-        if microFile and not mapToID[microFile] then mapToID[microFile] = newMapID end
-
-        -- update upvalues and signal callback
-        currentPlayerZoneMapID, currentPlayerLevel, currentMapFile, currentMapIsMicroDungeon = newMapID, newLevel, microFile or mapFile, isMicroDungeon
-        HereBeDragons.callbacks:Fire("PlayerZoneChanged", currentPlayerZoneMapID, currentPlayerLevel, currentMapFile, currentMapIsMicroDungeon)
-    end
-
-    -- start a timer to update in micro dungeons since multi-level micro dungeons do not reliably fire events
-    if isMicroDungeon then
-        StartUpdateTimer()
-    end
-end
-
--- upgradeable timer callback, don't want to keep calling the old function if the library is upgraded
-HereBeDragons.UpdateCurrentPosition = UpdateCurrentPosition
-local function UpdateTimerCallback()
-    -- signal that the timer ran
-    HereBeDragons.updateTimerActive = nil
-
-    -- run update now
-    HereBeDragons.UpdateCurrentPosition()
-end
-
-function StartUpdateTimer()
-    if not HereBeDragons.updateTimerActive then
-        -- prevent running multiple timers
-        HereBeDragons.updateTimerActive = true
-
-        -- and queue an update
-        C_Timer.After(1, UpdateTimerCallback)
-    end
-end
-
-local function OnEvent(frame, event, ...)
-    UpdateCurrentPosition()
-end
-
-HereBeDragons.eventFrame:SetScript("OnEvent", OnEvent)
-HereBeDragons.eventFrame:UnregisterAllEvents()
-HereBeDragons.eventFrame:RegisterEvent("ZONE_CHANGED_NEW_AREA")
-HereBeDragons.eventFrame:RegisterEvent("ZONE_CHANGED")
-HereBeDragons.eventFrame:RegisterEvent("ZONE_CHANGED_INDOORS")
-HereBeDragons.eventFrame:RegisterEvent("NEW_WMO_CHUNK")
-HereBeDragons.eventFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
-
--- if we're loading after entering the world (ie. on demand), update position now
-if IsLoggedIn() then
-    UpdateCurrentPosition()
-end
-
---- Return the localized zone name for a given mapID or mapFile
--- @param mapID numeric mapID or mapFile
-function HereBeDragons:GetLocalizedMap(mapID)
-    if type(mapID) == "string" then
-        mapID = mapID:gsub(TERRAIN_MATCH, "")
-        mapID = mapToID[mapID]
-    end
-    return mapData[mapID] and mapData[mapID].name or nil
-end
-
---- Return the map id to a mapFile
--- @param mapFile Map File
-function HereBeDragons:GetMapIDFromFile(mapFile)
-    if mapFile then
-        mapFile = mapFile:gsub(TERRAIN_MATCH, "")
-        return mapToID[mapFile]
-    end
-    return nil
-end
-
---- Return the mapFile to a map ID
--- @param mapID Map ID
-function HereBeDragons:GetMapFileFromID(mapID)
-    return mapData[mapID] and mapData[mapID].mapFile or nil
-end
-
---- Lookup the map ID for a Continent / Zone index combination
--- @param C continent index from GetCurrentMapContinent
--- @param Z zone index from GetCurrentMapZone
-function HereBeDragons:GetMapIDFromCZ(C, Z)
-    if C and continentZoneMap[C] then
-        return Z and continentZoneMap[C][Z]
-    end
-    return nil
-end
-
---- Lookup the C/Z values for map
--- @param mapID the MapID
-function HereBeDragons:GetCZFromMapID(mapID)
-    if mapData[mapID] then
-        return mapData[mapID].C, mapData[mapID].Z
-    end
-    return nil, nil
-end
-
---- Get the size of the zone
--- @param mapID Map ID or MapFile of the zone
--- @param level Optional map level
--- @return width, height of the zone, in yards
-function HereBeDragons:GetZoneSize(mapID, level)
-    local data = getMapDataTable(mapID, level)
-    if not data then return 0, 0 end
-
-    return data[1], data[2]
-end
-
---- Get the number of floors for a map
--- @param mapID map ID or mapFile of the zone
-function HereBeDragons:GetNumFloors(mapID)
-    if not mapID then return 0 end
-    if type(mapID) == "string" then
-        mapID = mapID:gsub(TERRAIN_MATCH, "")
-        mapID = mapToID[mapID]
-    end
-
-    if not mapData[mapID] or not mapData[mapID].numFloors then return 0 end
-
-    return mapData[mapID].numFloors
-end
-
---- Get a list of all map IDs
--- @return array-style table with all known/valid map IDs
-function HereBeDragons:GetAllMapIDs()
-    local t = {}
-    for id in pairs(mapData) do
-        table.insert(t, id)
-    end
-    return t
-end
-
---- Convert local/point coordinates to world coordinates in yards
--- @param x X position in 0-1 point coordinates
--- @param y Y position in 0-1 point coordinates
--- @param zone MapID or MapFile of the zone
--- @param level Optional level of the zone
-function HereBeDragons:GetWorldCoordinatesFromZone(x, y, zone, level)
-    local data = getMapDataTable(zone, level)
-    if not data or data[1] == 0 or data[2] == 0 then return nil, nil, nil end
-    if not x or not y then return nil, nil, nil end
-
-    local width, height, left, top = data[1], data[2], data[3], data[4]
-    x, y = left - width * x, top - height * y
-
-    return x, y, data.instance
-end
-
---- Convert world coordinates to local/point zone coordinates
--- @param x Global X position
--- @param y Global Y position
--- @param zone MapID or MapFile of the zone
--- @param level Optional level of the zone
--- @param allowOutOfBounds Allow coordinates to go beyond the current map (ie. outside of the 0-1 range), otherwise nil will be returned
-function HereBeDragons:GetZoneCoordinatesFromWorld(x, y, zone, level, allowOutOfBounds)
-    local data = getMapDataTable(zone, level)
-    if not data or data[1] == 0 or data[2] == 0 then return nil, nil end
-    if not x or not y then return nil, nil end
-
-    local width, height, left, top = data[1], data[2], data[3], data[4]
-    x, y = (left - x) / width, (top - y) / height
-
-    -- verify the coordinates fall into the zone
-    if not allowOutOfBounds and (x < 0 or x > 1 or y < 0 or y > 1) then return nil, nil end
-
-    return x, y
-end
-
---- Translate zone coordinates from one zone to another
--- @param x X position in 0-1 point coordinates, relative to the origin zone
--- @param y Y position in 0-1 point coordinates, relative to the origin zone
--- @param oZone Origin Zone, mapID or mapFile
--- @param oLevel Origin Zone Level
--- @param dZone Destination Zone, mapID or mapFile
--- @param dLevel Destination Zone Level
--- @param allowOutOfBounds Allow coordinates to go beyond the current map (ie. outside of the 0-1 range), otherwise nil will be returned
-function HereBeDragons:TranslateZoneCoordinates(x, y, oZone, oLevel, dZone, dLevel, allowOutOfBounds)
-    local xCoord, yCoord, instance = self:GetWorldCoordinatesFromZone(x, y, oZone, oLevel)
-    if not xCoord then return nil, nil end
-
-    local data = getMapDataTable(dZone, dLevel)
-    if not data or data.instance ~= instance then return nil, nil end
-
-    return self:GetZoneCoordinatesFromWorld(xCoord, yCoord, dZone, dLevel, allowOutOfBounds)
-end
-
---- Return the distance from an origin position to a destination position in the same instance/continent.
--- @param instanceID instance ID
--- @param oX origin X
--- @param oY origin Y
--- @param dX destination X
--- @param dY destination Y
--- @return distance, deltaX, deltaY
-function HereBeDragons:GetWorldDistance(instanceID, oX, oY, dX, dY)
-    if not oX or not oY or not dX or not dY then return nil, nil, nil end
-    local deltaX, deltaY = dX - oX, dY - oY
-    return (deltaX * deltaX + deltaY * deltaY)^0.5, deltaX, deltaY
-end
-
---- Return the distance between two points on the same continent
--- @param oZone origin zone map id or mapfile
--- @param oLevel optional origin zone level (floor)
--- @param oX origin X, in local zone/point coordinates
--- @param oY origin Y, in local zone/point coordinates
--- @param dZone destination zone map id or mapfile
--- @param dLevel optional destination zone level (floor)
--- @param dX destination X, in local zone/point coordinates
--- @param dY destination Y, in local zone/point coordinates
--- @return distance, deltaX, deltaY in yards
-function HereBeDragons:GetZoneDistance(oZone, oLevel, oX, oY, dZone, dLevel, dX, dY)
-    local oX, oY, oInstance = self:GetWorldCoordinatesFromZone(oX, oY, oZone, oLevel)
-    if not oX then return nil, nil, nil end
-
-    -- translate dX, dY to the origin zone
-    local dX, dY, dInstance = self:GetWorldCoordinatesFromZone(dX, dY, dZone, dLevel)
-    if not dX then return nil, nil, nil end
-
-    if oInstance ~= dInstance then return nil, nil, nil end
-
-    return self:GetWorldDistance(oInstance, oX, oY, dX, dY)
-end
-
---- Return the angle and distance from an origin position to a destination position in the same instance/continent.
--- @param instanceID instance ID
--- @param oX origin X
--- @param oY origin Y
--- @param dX destination X
--- @param dY destination Y
--- @return angle, distance where angle is in radians and distance in yards
-function HereBeDragons:GetWorldVector(instanceID, oX, oY, dX, dY)
-    local distance, deltaX, deltaY = self:GetWorldDistance(instanceID, oX, oY, dX, dY)
-    if not distance then return nil, nil end
-
-    -- calculate the angle from deltaY and deltaX
-    local angle = atan2(-deltaX, deltaY)
-
-    -- normalize the angle
-    if angle > 0 then
-        angle = PI2 - angle
-    else
-        angle = -angle
-    end
-
-    return angle, distance
-end
-
---- Get the current world position of the specified unit
--- The position is transformed to the current continent, if applicable
--- NOTE: The same restrictions as for the UnitPosition() API apply,
--- which means a very limited set of unit ids will actually work.
--- @param unitId Unit Id
--- @return x, y, instanceID
-function HereBeDragons:GetUnitWorldPosition(unitId)
-    -- get the current position
-    local y, x, z, instanceID = UnitPosition(unitId)
-    if not x or not y then return nil, nil, instanceIDOverrides[instanceID] or instanceID end
-
-    -- return transformed coordinates
-    return applyCoordinateTransforms(x, y, instanceID)
-end
-
---- Get the current world position of the player
--- The position is transformed to the current continent, if applicable
--- @return x, y, instanceID
-function HereBeDragons:GetPlayerWorldPosition()
-    -- get the current position
-    local y, x, z, instanceID = UnitPosition("player")
-    if not x or not y then return nil, nil, instanceIDOverrides[instanceID] or instanceID end
-
-    -- return transformed coordinates
-    return applyCoordinateTransforms(x, y, instanceID)
-end
-
---- Get the current zone and level of the player
--- The returned mapFile can represent a micro dungeon, if the player currently is inside one.
--- @return mapID, level, mapFile, isMicroDungeon
-function HereBeDragons:GetPlayerZone()
-    return currentPlayerZoneMapID, currentPlayerLevel, currentMapFile, currentMapIsMicroDungeon
-end
-
---- Get the current position of the player on a zone level
--- The returned values are local point coordinates, 0-1. The mapFile can represent a micro dungeon.
--- @param allowOutOfBounds Allow coordinates to go beyond the current map (ie. outside of the 0-1 range), otherwise nil will be returned
--- @return x, y, mapID, level, mapFile, isMicroDungeon
-function HereBeDragons:GetPlayerZonePosition(allowOutOfBounds)
-    if not currentPlayerZoneMapID then return nil, nil, nil, nil end
-    local x, y, instanceID = self:GetPlayerWorldPosition()
-    if not x or not y then return nil, nil, nil, nil end
-
-    x, y = self:GetZoneCoordinatesFromWorld(x, y, currentPlayerZoneMapID, currentPlayerLevel, allowOutOfBounds)
-    if x and y then
-        return x, y, currentPlayerZoneMapID, currentPlayerLevel, currentMapFile, currentMapIsMicroDungeon
-    end
-    return nil, nil, nil, nil
-end
diff --git a/libs/HereBeDragons/HereBeDragons-2.0.lua b/libs/HereBeDragons/HereBeDragons-2.0.lua
index 4231a8a..ff36303 100755
--- a/libs/HereBeDragons/HereBeDragons-2.0.lua
+++ b/libs/HereBeDragons/HereBeDragons-2.0.lua
@@ -1,11 +1,6 @@
 -- HereBeDragons is a data API for the World of Warcraft mapping system

--- HereBeDragons-2.0 is not supported on WoW 7.x or earlier
-if select(4, GetBuildInfo()) < 80000 then
-    return
-end
-
-local MAJOR, MINOR = "HereBeDragons-2.0", 5
+local MAJOR, MINOR = "HereBeDragons-2.0", 7
 assert(LibStub, MAJOR .. " requires LibStub")

 local HereBeDragons, oldversion = LibStub:NewLibrary(MAJOR, MINOR)
@@ -72,7 +67,7 @@ local instanceIDOverrides = {
 }

 -- gather map info, but only if this isn't an upgrade (or the upgrade version forces a re-map)
-if not oldversion or oldversion < 3 then
+if not oldversion or oldversion < 7 then
     -- wipe old data, if required, otherwise the upgrade path isn't triggered
     if oldversion then
         wipe(mapData)
@@ -83,9 +78,9 @@ if not oldversion or oldversion < 3 then
     -- map transform data extracted from UIMapAssignment.db2 (see HereBeDragons-Scripts on GitHub)
     -- format: instanceID, newInstanceID, minY, maxY, minX, maxX, offsetY, offsetX
     local transformData = {
-        { 530, 1, -6933.33, 533.33, -16000, -8000, 10133.3, 17600 },
         { 530, 0, 4800, 16000, -10133.3, -2666.67, -2400, 2400 },
-        { 732, 0, -20000, 20000, -20000, 20000, -1600, 2800 },
+        { 530, 1, -6933.33, 533.33, -16000, -8000, 10133.3, 17600 },
+        { 732, 0, -3200, 533.3, -533.3, 2666.7, -611.8, 3904.3 },
         { 1064, 870, 5391, 8148, 3518, 7655, -2134.2, -2286.6 },
         { 1208, 1116, -2666, -2133, -2133, -1600, 10210, 2410 },
         { 1460, 1220, -1066.7, 2133.3, 0, 3200, -2333.9, 966.7 },
@@ -119,8 +114,14 @@ if not oldversion or oldversion < 3 then

     local vector00, vector05 = CreateVector2D(0, 0), CreateVector2D(0.5, 0.5)
     -- gather the data of one map (by uiMapID)
-    local function processMap(id, data)
-        if not id or mapData[id] then return end
+    local function processMap(id, data, parent)
+        if not id or not data or mapData[id] then return end
+
+        if data.parentMapID and data.parentMapID ~= 0 then
+            parent = data.parentMapID
+        elseif not parent then
+            parent = 0
+        end

         -- get two positions from the map, we use 0/0 and 0.5/0.5 to avoid issues on some maps where 1/1 is translated inaccurately
         local instance, topLeft = C_Map.GetWorldPosFromMapPos(id, vector00)
@@ -132,20 +133,36 @@ if not oldversion or oldversion < 3 then
             right = left + (right - left) * 2

             instance, left, right, top, bottom = applyMapTransforms(instance, left, right, top, bottom)
-            mapData[id] = {left - right, top - bottom, left, top, instance = instance, name = data.name, mapType = data.mapType, parent = data.parentMapID}
+            mapData[id] = {left - right, top - bottom, left, top, instance = instance, name = data.name, mapType = data.mapType, parent = parent }
         else
-            mapData[id] = {0, 0, 0, 0, instance = instance or -1, name = data.name, mapType = data.mapType, parent = data.parentMapID }
+            mapData[id] = {0, 0, 0, 0, instance = instance or -1, name = data.name, mapType = data.mapType, parent = parent }
         end
     end

-    local function processMapChildrenRecursive(id)
-        local children = C_Map.GetMapChildrenInfo(id)
+    local function processMapChildrenRecursive(parent)
+        local children = C_Map.GetMapChildrenInfo(parent)
         if children and #children > 0 then
             for i = 1, #children do
                 local id = children[i].mapID
                 if id and not mapData[id] then
-                    processMap(id, children[i])
+                    processMap(id, children[i], parent)
                     processMapChildrenRecursive(id)
+
+                    -- process sibling maps (in the same group)
+                    -- in some cases these are not discovered by GetMapChildrenInfo above
+                    local groupID = C_Map.GetMapGroupID(id)
+                    if groupID then
+                        local groupMembers = C_Map.GetMapGroupMembersInfo(groupID)
+                        if groupMembers then
+                            for k = 1, #groupMembers do
+                                local memberId = groupMembers[k].mapID
+                                if memberId and not mapData[memberId] then
+                                    processMap(memberId, C_Map.GetMapInfo(memberId), parent)
+                                    processMapChildrenRecursive(memberId)
+                                end
+                            end
+                        end
+                    end
                 end
             end
         end
diff --git a/libs/HereBeDragons/HereBeDragons-Migrate.lua b/libs/HereBeDragons/HereBeDragons-Migrate.lua
index b6efc1b..537b9cb 100755
--- a/libs/HereBeDragons/HereBeDragons-Migrate.lua
+++ b/libs/HereBeDragons/HereBeDragons-Migrate.lua
@@ -1,8 +1,3 @@
--- HereBeDragons-Migrate is not supported on WoW 7.x or earlier
-if select(4, GetBuildInfo()) < 80000 then
-    return
-end
-
 local MAJOR, MINOR = "HereBeDragons-Migrate", 2
 assert(LibStub, MAJOR .. " requires LibStub")

diff --git a/libs/HereBeDragons/HereBeDragons-Pins-1.0.lua b/libs/HereBeDragons/HereBeDragons-Pins-1.0.lua
deleted file mode 100755
index d0a79c5..0000000
--- a/libs/HereBeDragons/HereBeDragons-Pins-1.0.lua
+++ /dev/null
@@ -1,651 +0,0 @@
--- HereBeDragons-Pins is a library to show pins/icons on the world map and minimap
-
--- HereBeDragons-Pins-1.0 is not supported on WoW 8.0
-if select(4, GetBuildInfo()) >= 80000 then
-	return
-end
-
-
-local MAJOR, MINOR = "HereBeDragons-Pins-1.0", 16
-assert(LibStub, MAJOR .. " requires LibStub")
-
-local pins, oldversion = LibStub:NewLibrary(MAJOR, MINOR)
-if not pins then return end
-
-local HBD = LibStub("HereBeDragons-1.0")
-
-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 {}
-
--- store a reference to the active minimap object
-pins.Minimap = pins.Minimap or Minimap
-
--- 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 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 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 worldmapWidth, worldmapHeight = WorldMapButton:GetWidth(), WorldMapButton:GetHeight()
-
-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 UpdateMinimapPins(force)
-    -- get the current player position
-    local x, y, instanceID = HBD:GetPlayerWorldPosition()
-    local mapID, mapFloor = 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, data 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"]
-        mapRadius = minimap_size[indoors][zoom] / 2
-        minimapWidth = pins.Minimap:GetWidth() / 2
-        minimapHeight = pins.Minimap:GetHeight() / 2
-
-        -- 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.floor or (data.floor == mapFloor and (data.floor == 0 or data.mapID == 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
-        mapRadius = minimap_size[indoors][zoom] / 2
-        -- 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()
-    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
-
-local function PositionWorldMapIcon(icon, data, currentMapID, currentMapFloor)
-    -- special handling for the azeroth world map
-    -- translating coordinates to the azeroth map requires passing the instance ID
-    -- of the origin continent, so the appropriate coordinates can be calculated
-    if currentMapID == WORLDMAP_AZEROTH_ID then
-        currentMapFloor = data.instanceID
-    end
-
-    local x, y = HBD:GetZoneCoordinatesFromWorld(data.x, data.y, currentMapID, currentMapFloor)
-    if x and y then
-        icon:ClearAllPoints()
-        icon:SetPoint("CENTER", WorldMapButton, "TOPLEFT", x * worldmapWidth, -y * worldmapHeight)
-        icon:Show()
-    else
-        icon:Hide()
-    end
-end
-
-local function GetWorldMapLocation()
-    local mapID, mapFloor = GetCurrentMapAreaID(), GetCurrentMapDungeonLevel()
-
-    -- override the mapID for the azeroth world map
-    if mapID == -1 and GetCurrentMapContinent() == 0 and GetCurrentMapZone() == 0 then
-        mapID = WORLDMAP_AZEROTH_ID
-        mapFloor = 0
-    end
-
-    return mapID, mapFloor
-end
-
-local function UpdateWorldMap()
-    if not WorldMapButton:IsVisible() then return end
-
-    local mapID, mapFloor = GetWorldMapLocation()
-
-    -- not viewing a valid map
-    if not mapID or mapID == -1 then
-        for icon in pairs(worldmapPins) do
-            icon:Hide()
-        end
-        return
-    end
-
-    local instanceID = HBD.mapData[mapID] and HBD.mapData[mapID].instance or -1
-
-    worldmapWidth  = WorldMapButton:GetWidth()
-    worldmapHeight = WorldMapButton:GetHeight()
-
-    for icon, data in pairs(worldmapPins) do
-        if (instanceID == data.instanceID or mapID == WORLDMAP_AZEROTH_ID) and (not data.floor or (data.floor == mapFloor and (data.floor == 0 or data.mapID == mapID))) then
-            PositionWorldMapIcon(icon, data, mapID, mapFloor)
-        else
-            icon:Hide()
-        end
-    end
-end
-
-local function UpdateMaps()
-    UpdateMinimapZoom()
-    UpdateMinimapPins()
-    UpdateWorldMap()
-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 == "ROTATE_MINIMAP" then
-            rotateMinimap = (value == "1")
-            queueFullUpdate = true
-        end
-    elseif event == "MINIMAP_UPDATE_ZOOM" then
-        UpdateMinimapZoom()
-        UpdateMinimapPins()
-    elseif event == "PLAYER_LOGIN" then
-        -- recheck cvars after login
-        rotateMinimap = GetCVar("rotateMinimap") == "1"
-    elseif event == "PLAYER_ENTERING_WORLD" then
-        UpdateMaps()
-    elseif event == "WORLD_MAP_UPDATE" then
-        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")
-pins.updateFrame:RegisterEvent("WORLD_MAP_UPDATE")
-
-HBD.RegisterCallback(pins, "PlayerZoneChanged", UpdateMaps)
-
-
---- Add a icon to the minimap (x/y world coordinate version)
--- Note: This API does not let you specify a floor, as floors are map-specific, not instance/world wide. Use the Map/Floor API to specify a floor.
--- @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")
-    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.mapID = nil
-    t.floor = nil
-
-    minimapPins[icon] = t
-    queueFullUpdate = true
-
-    icon:SetParent(pins.Minimap)
-end
-
---- Add a icon to the minimap (mapid/floor coordinate version)
--- @param ref Reference to your addon to track the icon under (ie. your "self" or string identifier)
--- @param icon Icon Frame
--- @param mapID Map ID of the map to place the icon on
--- @param mapFloor Floor to place the icon on (or nil for all floors)
--- @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 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:AddMinimapIconMF(ref, icon, mapID, mapFloor, x, y, floatOnEdge)
-    if not ref then
-        error(MAJOR..": AddMinimapIconMF: 'ref' must not be nil")
-    end
-    if type(icon) ~= "table" or not icon.SetPoint then
-        error(MAJOR..": AddMinimapIconMF: 'icon' must be a frame")
-    end
-    if type(mapID) ~= "number" or type(x) ~= "number" or type(y) ~= "number" then
-        error(MAJOR..": AddMinimapIconMF: 'mapID', 'x' and 'y' must be numbers")
-    end
-
-    -- convert to world coordinates and use our known adding function
-    local xCoord, yCoord, instanceID = HBD:GetWorldCoordinatesFromZone(x, y, mapID, mapFloor)
-    if not xCoord then return end
-
-    self:AddMinimapIconWorld(ref, icon, instanceID, xCoord, yCoord, floatOnEdge)
-
-    -- store extra information
-    minimapPins[icon].mapID = mapID
-    minimapPins[icon].floor = mapFloor
-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
-
---- Add a icon to the world map (x/y world coordinate version)
--- Note: This API does not let you specify a floor, as floors are map-specific, not instance/world wide. Use the Map/Floor API to specify a floor.
--- @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
-function pins:AddWorldMapIconWorld(ref, icon, instanceID, x, y)
-    if not ref then
-        error(MAJOR..": AddWorldMapIconWorld: 'ref' must not be nil")
-    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 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.mapID = nil
-    t.floor = nil
-
-    worldmapPins[icon] = t
-
-    if WorldMapButton:IsVisible() then
-        local currentMapID, currentMapFloor = GetWorldMapLocation()
-        if currentMapID and HBD.mapData[currentMapID] and (HBD.mapData[currentMapID].instance == instanceID or currentMapID == WORLDMAP_AZEROTH_ID) then
-            PositionWorldMapIcon(icon, t, currentMapID, currentMapFloor)
-        else
-            icon:Hide()
-        end
-    end
-end
-
---- Add a icon to the world map (mapid/floor coordinate version)
--- @param ref Reference to your addon to track the icon under (ie. your "self" or string identifier)
--- @param icon Icon Frame
--- @param mapID Map ID of the map to place the icon on
--- @param mapFloor Floor to place the icon on (or nil for all floors)
--- @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
-function pins:AddWorldMapIconMF(ref, icon, mapID, mapFloor, x, y)
-    if not ref then
-        error(MAJOR..": AddWorldMapIconMF: 'ref' must not be nil")
-    end
-    if type(icon) ~= "table" or not icon.SetPoint then
-        error(MAJOR..": AddWorldMapIconMF: 'icon' must be a frame")
-    end
-    if type(mapID) ~= "number" or type(x) ~= "number" or type(y) ~= "number" then
-        error(MAJOR..": AddWorldMapIconMF: 'mapID', 'x' and 'y' must be numbers")
-    end
-
-    -- convert to world coordinates
-    local xCoord, yCoord, instanceID = HBD:GetWorldCoordinatesFromZone(x, y, mapID, mapFloor)
-    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.mapID = mapID
-    t.floor = mapFloor
-
-    worldmapPins[icon] = t
-
-    if WorldMapButton:IsVisible() then
-        local currentMapID, currentMapFloor = GetWorldMapLocation()
-        if currentMapID and HBD.mapData[currentMapID] and (HBD.mapData[currentMapID].instance == instanceID or currentMapID == WORLDMAP_AZEROTH_ID)
-           and (not mapFloor or (currentMapFloor == mapFloor and (mapFloor == 0 or currentMapID == mapID))) then
-            PositionWorldMapIcon(icon, t, currentMapID, currentMapFloor)
-        else
-            icon:Hide()
-        end
-    end
-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
-    icon:Hide()
-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
-        icon:Hide()
-    end
-    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
diff --git a/libs/HereBeDragons/HereBeDragons-Pins-2.0.lua b/libs/HereBeDragons/HereBeDragons-Pins-2.0.lua
index 8b8cf9e..76da8b4 100755
--- a/libs/HereBeDragons/HereBeDragons-Pins-2.0.lua
+++ b/libs/HereBeDragons/HereBeDragons-Pins-2.0.lua
@@ -1,11 +1,6 @@
 -- HereBeDragons-Pins is a library to show pins/icons on the world map and minimap

--- HereBeDragons-Pins-2.0 is not supported on WoW 7.x
-if select(4, GetBuildInfo()) < 80000 then
-	return
-end
-
-local MAJOR, MINOR = "HereBeDragons-Pins-2.0", 5
+local MAJOR, MINOR = "HereBeDragons-Pins-2.0", 6
 assert(LibStub, MAJOR .. " requires LibStub")

 local pins, oldversion = LibStub:NewLibrary(MAJOR, MINOR)
@@ -427,7 +422,7 @@ function worldmapProvider:HandlePin(icon, data)
         x, y = HBD:GetZoneCoordinatesFromWorld(data.x, data.y, uiMapID)
     end
     if x and y then
-        self:GetMap():AcquirePin("HereBeDragonsPinsTemplate", icon, x, y)
+        self:GetMap():AcquirePin("HereBeDragonsPinsTemplate", icon, x, y, data.frameLevelType)
     end
 end

@@ -437,7 +432,8 @@ function worldmapProviderPin:OnLoad()
     self:SetScalingLimits(1, 1.0, 1.2)
 end

-function worldmapProviderPin:OnAcquired(icon, x, y)
+function worldmapProviderPin:OnAcquired(icon, x, y, frameLevelType)
+    self:UseFrameLevelType(frameLevelType or "PIN_FRAME_LEVEL_AREA_POI")
     self:SetPosition(x, y)

     self.icon = icon
@@ -643,7 +639,8 @@ HBD_PINS_WORLDMAP_SHOW_WORLD     = 3
 -- @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
-function pins:AddWorldMapIconWorld(ref, icon, instanceID, x, y, showFlag)
+-- @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")
     end
@@ -666,6 +663,7 @@ function pins:AddWorldMapIconWorld(ref, icon, instanceID, x, y, showFlag)
     t.y = y
     t.uiMapID = nil
     t.worldMapShowFlag = showFlag or 0
+    t.frameLevelType = frameLevel

     worldmapPins[icon] = t

@@ -679,7 +677,8 @@ end
 -- @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
-function pins:AddWorldMapIconMap(ref, icon, uiMapID, x, y, showFlag)
+-- @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")
     end
@@ -706,6 +705,7 @@ function pins:AddWorldMapIconMap(ref, icon, uiMapID, x, y, showFlag)
     t.y = yCoord
     t.uiMapID = uiMapID
     t.worldMapShowFlag = showFlag or 0
+    t.frameLevelType = frameLevel

     worldmapPins[icon] = t