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