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()