Quantcast

Updated embedded HereBeDragons

Jim Whitehead [07-20-16 - 05:58]
Updated embedded HereBeDragons
Filename
libs/HereBeDragons-1.0/HereBeDragons-1.0.lua
libs/HereBeDragons-1.0/HereBeDragons-Pins-1.0.lua
diff --git a/libs/HereBeDragons-1.0/HereBeDragons-1.0.lua b/libs/HereBeDragons-1.0/HereBeDragons-1.0.lua
index caf5bff..3379d06 100755
--- a/libs/HereBeDragons-1.0/HereBeDragons-1.0.lua
+++ b/libs/HereBeDragons-1.0/HereBeDragons-1.0.lua
@@ -1,6 +1,6 @@
 -- HereBeDragons is a data API for the World of Warcraft mapping system

-local MAJOR, MINOR = "HereBeDragons-1.0", 11
+local MAJOR, MINOR = "HereBeDragons-1.0", 21
 assert(LibStub, MAJOR .. " requires LibStub")

 local HereBeDragons, oldversion = LibStub:NewLibrary(MAJOR, MINOR)
@@ -18,6 +18,8 @@ HereBeDragons.transforms       = HereBeDragons.transforms or {}

 HereBeDragons.callbacks        = CBH:New(HereBeDragons, nil, nil, false)

+local IsLegion = select(4, GetBuildInfo()) >= 70000
+
 -- constants
 local TERRAIN_MATCH = "_terrain%d+$"

@@ -43,13 +45,25 @@ local currentPlayerZoneMapID, currentPlayerLevel, currentMapFile, currentMapIsMi
 local instanceIDOverrides = {
     -- Draenor
     [1152] = 1116, -- Horde Garrison 1
-    [1153] = 1116, -- Horde Garrison 2
-    [1154] = 1116, -- Horde Garrison 3
+    [1330] = 1116, -- Horde Garrison 2
+    [1153] = 1116, -- Horde Garrison 3
+    [1154] = 1116, -- Horde Garrison 4 (unused)
     [1158] = 1116, -- Alliance Garrison 1
-    [1159] = 1116, -- Alliance Garrison 2
-    [1160] = 1116, -- Alliance Garrison 3
+    [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)
+    [1502] = 1220, -- Dalaran Underbelly
+    [1533] = 0,    -- Karazhan Artifact Scenario
+    [1612] = 1220, -- Feral Druid Artifact Scenario (Suramar)
 }

 -- unregister and store all WORLD_MAP_UPDATE registrants, to avoid excess processing when
@@ -72,10 +86,11 @@ local function RestoreWMU()
 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 < 10 then
+if not oldversion or oldversion < 21 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 = {
@@ -163,6 +178,9 @@ if not oldversion or oldversion < 10 then
         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 = GetMapInfo()
         if mapFile then
             -- remove phased terrain from the map names
@@ -177,10 +195,6 @@ if not oldversion or oldversion < 10 then
         mapData[id].Z = Z or -100

         if mapData[id].C > 0 and mapData[id].Z >= 0 then
-            if not microDungeons[instanceID] then
-                microDungeons[instanceID] = {}
-            end
-
             -- store C/Z lookup table
             if not continentZoneMap[C] then
                 continentZoneMap[C] = {}
@@ -190,25 +204,47 @@ if not oldversion or oldversion < 10 then
             end
         end

-        local numFloors = GetNumDungeonMapLevels()
-        if numFloors == 0 and GetCurrentMapDungeonLevel() == 1 then
-            numFloors = 1
-            mapData[id].fakefloor = true
+        local floors
+        if IsLegion then
+            floors = { GetNumDungeonMapLevels() }
+
+            -- offset floors for terrain map
+            if DungeonUsesTerrainMap() then
+                for i = 1, #floors do
+                    floors[i] = floors[i] + 1
+                end
+            end
+        else
+            floors = {}
+            for f = 1, GetNumDungeonMapLevels() do
+                floors[f] = f
+            end
+        end
+        if #floors == 0 and GetCurrentMapDungeonLevel() > 0 then
+            floors[1] = GetCurrentMapDungeonLevel()
+            mapData[id].fakefloor = GetCurrentMapDungeonLevel()
         end

         mapData[id].floors = {}
-        if numFloors > 0 then
-            for f = 1, numFloors do
-                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
+        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] = {}
             end
         end
     end
@@ -218,12 +254,13 @@ if not oldversion or oldversion < 10 then
             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[terrainMapID] then
-                microDungeons[terrainMapID][floorIndex] = { maxX - minX, maxY - minY, maxX, maxY }
-                microDungeons[terrainMapID][floorIndex].instance = terrainMapID
+            if microDungeons[originalTerrainMapID] then
+                microDungeons[originalTerrainMapID][floorIndex] = { maxX - minX, maxY - minY, maxX, maxY }
+                microDungeons[originalTerrainMapID][floorIndex].instance = terrainMapID
             end
         end
     end
@@ -238,15 +275,38 @@ if not oldversion or oldversion < 10 then
         mapData[WORLDMAP_COSMIC_ID].Z = 0
         mapData[WORLDMAP_COSMIC_ID].name = WORLD_MAP

-        -- fake world map
-        mapData[WORLDMAP_AZEROTH_ID] = {0, 0, 0, 0}
-        mapData[WORLDMAP_AZEROTH_ID].instance = -1
+        -- 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].floors = {}
         mapData[WORLDMAP_AZEROTH_ID].C = 0
         mapData[WORLDMAP_AZEROTH_ID].Z = 0
         mapData[WORLDMAP_AZEROTH_ID].name = WORLD_MAP

+        -- we only have data for legion clients, zeroing the coordinates
+        -- and niling out the floors temporarily disables the logic on live
+        if not IsLegion then
+            mapData[WORLDMAP_AZEROTH_ID][1] = 0
+            mapData[WORLDMAP_AZEROTH_ID][2] = 0
+            mapData[WORLDMAP_AZEROTH_ID][3] = 0
+            mapData[WORLDMAP_AZEROTH_ID][4] = 0
+            mapData[WORLDMAP_AZEROTH_ID].floors = {}
+        end
+
         -- alliance draenor garrison
         if mapData[971] then
             mapData[971].Z = 5
@@ -338,15 +398,15 @@ local function getMapDataTable(mapID, level)
     local data = mapData[mapID]
     if not data then return nil end

-    if (level == nil or level == 0) and data.fakefloor then
-        level = 1
+    if (type(level) ~= "number" or level == 0) and data.fakefloor then
+        level = data.fakefloor
     end

-    if level and level > 0 then
+    if type(level) == "number" and level > 0 then
         if data.floors[level] then
             return data.floors[level]
-        elseif microDungeons[data.instance] and microDungeons[data.instance][level] then
-            return microDungeons[data.instance][level]
+        elseif data.originalInstance and microDungeons[data.originalInstance] and microDungeons[data.originalInstance][level] then
+            return microDungeons[data.originalInstance][level]
         end
     else
         return data
@@ -490,9 +550,9 @@ function HereBeDragons:GetNumFloors(mapID)
         mapID = mapToID[mapID]
     end

-    if not mapData[mapID] then return 0 end
+    if not mapData[mapID] or not mapData[mapID].numFloors then return 0 end

-    return #(mapData[mapID].floors)
+    return mapData[mapID].numFloors
 end

 --- Get a list of all map IDs
@@ -616,12 +676,28 @@ function HereBeDragons:GetWorldVector(instanceID, oX, oY, dX, dY)
     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, nil 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, nil end

     -- return transformed coordinates
     return applyCoordinateTransforms(x, y, instanceID)
@@ -636,12 +712,14 @@ 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()
+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)
+    x, y = self:GetZoneCoordinatesFromWorld(x, y, currentPlayerZoneMapID, currentPlayerLevel, allowOutOfBounds)
     if x and y then
         return x, y, currentPlayerZoneMapID, currentPlayerLevel, currentMapFile, currentMapIsMicroDungeon
     end
diff --git a/libs/HereBeDragons-1.0/HereBeDragons-Pins-1.0.lua b/libs/HereBeDragons-1.0/HereBeDragons-Pins-1.0.lua
index 53f2216..cc004d8 100755
--- a/libs/HereBeDragons-1.0/HereBeDragons-Pins-1.0.lua
+++ b/libs/HereBeDragons-1.0/HereBeDragons-Pins-1.0.lua
@@ -1,6 +1,6 @@
 -- HereBeDragons-Pins is a library to show pins/icons on the world map and minimap

-local MAJOR, MINOR = "HereBeDragons-Pins-1.0", 11
+local MAJOR, MINOR = "HereBeDragons-Pins-1.0", 13
 assert(LibStub, MAJOR .. " requires LibStub")

 local pins, oldversion = LibStub:NewLibrary(MAJOR, MINOR)
@@ -276,6 +276,13 @@ local function UpdateMinimapZoom()
 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()
@@ -286,10 +293,22 @@ local function PositionWorldMapIcon(icon, data, currentMapID, currentMapFloor)
     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 = GetCurrentMapAreaID(), GetCurrentMapDungeonLevel()
+    local mapID, mapFloor = GetWorldMapLocation()

     -- not viewing a valid map
     if not mapID or mapID == -1 then
@@ -305,7 +324,7 @@ local function UpdateWorldMap()
     worldmapHeight = WorldMapButton:GetHeight()

     for icon, data in pairs(worldmapPins) do
-        if instanceID == data.instanceID and (not data.floor or (data.floor == mapFloor and (data.floor == 0 or data.mapID == mapID))) then
+        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()
@@ -388,7 +407,7 @@ function pins:AddMinimapIconWorld(ref, icon, instanceID, x, y, floatOnEdge)

     minimapPinRegistry[ref][icon] = true

-    local t = newCachedTable()
+    local t = minimapPins[icon] or newCachedTable()
     t.instanceID = instanceID
     t.x = x
     t.y = y
@@ -503,7 +522,7 @@ function pins:AddWorldMapIconWorld(ref, icon, instanceID, x, y)

     worldmapPinRegistry[ref][icon] = true

-    local t = newCachedTable()
+    local t = worldmapPins[icon] or newCachedTable()
     t.instanceID = instanceID
     t.x = x
     t.y = y
@@ -513,8 +532,8 @@ function pins:AddWorldMapIconWorld(ref, icon, instanceID, x, y)
     worldmapPins[icon] = t

     if WorldMapButton:IsVisible() then
-        local currentMapID, currentMapFloor = GetCurrentMapAreaID(), GetCurrentMapDungeonLevel()
-        if currentMapID and HBD.mapData[currentMapID] and HBD.mapData[currentMapID].instance == instanceID 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()
@@ -550,7 +569,7 @@ function pins:AddWorldMapIconMF(ref, icon, mapID, mapFloor, x, y)

     worldmapPinRegistry[ref][icon] = true

-    local t = newCachedTable()
+    local t = worldmapPins[icon] or newCachedTable()
     t.instanceID = instanceID
     t.x = xCoord
     t.y = yCoord
@@ -560,8 +579,9 @@ function pins:AddWorldMapIconMF(ref, icon, mapID, mapFloor, x, y)
     worldmapPins[icon] = t

     if WorldMapButton:IsVisible() then
-        local currentMapID, currentMapFloor = GetCurrentMapAreaID(), GetCurrentMapDungeonLevel()
-        if currentMapID and HBD.mapData[currentMapID] and HBD.mapData[currentMapID].instance == instanceID and (not mapFloor or (currentMapFloor == mapFloor and (mapFloor == 0 or currentMapID == mapID))) 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()