Files
Nanami-UI/MapReveal.lua
rucky ec9e3c29d6 完成多出修改
修复拾取界面点击无效问题
修复宠物训练界面不显示训练点问题
新增天赋分享到聊天界面
修复飞行界面无法关闭问题
修复术士宠物的显示问题
为天赋界面添加默认数据库支持
框架现在也可自主选择是否启用
玩家框架和目标框架可取消显示3D头像以及透明度修改
背包和银行也添加透明度自定义支持
优化tooltip性能和背包物品显示方式
修复布局模式在ui缩放后不能正常定位的问题
添加硬核模式危险和死亡的工会通报
添加拾取和已拾取的框体
等等
2026-03-23 10:25:25 +08:00

513 lines
18 KiB
Lua

--------------------------------------------------------------------------------
-- Nanami-UI: MapReveal -- Reveal unexplored world map areas
-- Adapted from ShaguTweaks-extras worldmap-reveal approach
-- Uses LibMapOverlayData (from !Libs) supplemented with Turtle WoW zones
--------------------------------------------------------------------------------
SFrames.MapReveal = SFrames.MapReveal or {}
local MapReveal = SFrames.MapReveal
local origWorldMapFrame_Update = nil
local overlayDBPatched = false
local errata = {
["Interface\\WorldMap\\Tirisfal\\BRIGHTWATERLAKE"] = { offsetX = { 587, 584 } },
["Interface\\WorldMap\\Silverpine\\BERENSPERIL"] = { offsetY = { 417, 415 } },
}
-- Turtle WoW new/modified zones not present in LibMapOverlayData
-- Updated for latest Turtle WoW version
local TurtleWoW_Zones = {
["StonetalonMountains"] = {
"SUNROCKRETREAT:512:256:256:256", "WINDSHEARCRAG:256:256:512:256",
"MIRKFALLONLAKE:512:512:256:0", "THECHARREDVALE:256:512:256:256",
"STONETALONPEAK:256:256:256:0", "WEBWINDERPATH:256:512:512:256",
"AMANIALOR:512:256:0:0", "GRIMTOTEMPOST:512:256:512:512",
"CAMPAPARAJE:512:256:512:512", "MALAKAJIN:512:256:512:512",
"BOULDERSLIDERAVINE:256:256:512:512", "SISHIRCANYON:256:512:512:256",
"VENTURECOMPANYCAMP:256:512:256:0", "BLACKSANDOILFIELDS:512:512:0:0",
"POWDERTOWN:256:256:256:256", "BRAMBLETHORNPASS:512:512:512:256",
"BAELHARDUL:512:256:512:256", "BROKENCLIFFMINE:256:512:256:0",
"THEEARTHENRING:512:256:256:256",
},
["UpperKarazhan2f"] = {
"OUTLAND:1024:768:0:0",
},
["GrimReaches"] = {
"DUNKITHAS:512:256:256:256", "THEGRIMHOLLOW:512:512:256:256",
"LAKEKITHAS:512:256:256:256", "SLATEBEARDSFORGE:512:256:256:256",
"THEHIGHPASS:256:256:256:256", "SALGAZMINES:256:256:512:256",
"EASTRIDGEOUTPOST:512:512:256:0", "BAGGOTHSRAMPART:256:512:256:0",
"RUINSOFSTOLGAZKEEP:256:256:256:0", "GROLDANSEXCAVATION:256:512:512:0",
"ZARMGETHSTRONGHOLD:512:256:256:0", "GETHKAR:512:256:256:0",
"ZARMGETHPOINT:512:256:256:0", "SHATTERBLADEPOST:256:256:512:0",
"BRANGARSFOLLY:256:256:512:0", "BARLEYCRESTFARMSTEAD:512:512:256:0",
},
["Balor"] = {
"GULLWINGWRECKAGE:512:256:0:0", "BILGERATCOMPOUND:256:256:256:0",
"SIOUTPOST:256:512:512:256", "RUINSOFBREEZEHAVEN:512:256:256:256",
"CROAKINGPLATEAU:512:512:256:0", "LANGSTONORCHARD:256:256:256:256",
"SORROWMORELAKE:256:256:256:256", "SCURRYINGTHICKET:256:512:256:0",
"STORMWROUGHTCASTLE:512:256:256:256", "STORMREAVERSPIRE:512:512:256:256",
"WINDROCKCLIFFS:256:512:256:256", "TREACHEROUSCRAGS:512:512:256:256",
"VANDERFARMSTEAD:256:256:256:256", "GRAHANESTATE:256:256:256:256",
"STORMBREAKERPOINT:512:512:512:0",
},
["Northwind"] = {
"MERCHANTSHIGHROAD:512:512:256:256", "AMBERSHIRE:512:256:256:256",
"AMBERWOODKEEP:512:256:0:256", "CRYSTALFALLS:512:512:512:256",
"CRAWFORDWINERY:256:256:512:256", "NORTHWINDLOGGINGCAMP:256:512:256:0",
"WITCHCOVEN:256:256:256:0", "RUINSOFBIRKHAVEN:512:512:512:0",
"SHERWOODQUARRY:512:512:512:0", "BLACKROCKBREACH:512:512:512:0",
"GRIMMENLAKE:256:512:512:256", "ABBEYGARDENS:256:256:512:0",
"STILLHEARTPORT:512:512:0:0", "TOWEROFMAGILOU:512:512:0:0",
"BRISTLEWHISKERCAVERN:256:512:512:0", "NORTHRIDGEPOINT:512:512:256:0",
"CINDERFALLPASS:512:512:512:256",
},
}
-- Runtime-discovered overlay data (populated by ScanAllMaps)
local scannedOverlays = {}
local function IsTurtleWoW()
return TargetHPText and TargetHPPercText
end
local function GetOverlayDB()
return MapOverlayData or LibMapOverlayData or zMapOverlayData or mapOverlayData
end
local function PatchOverlayDB()
if overlayDBPatched then return end
overlayDBPatched = true
if not IsTurtleWoW() then return end
local db = GetOverlayDB()
if not db then return end
for zone, data in pairs(TurtleWoW_Zones) do
db[zone] = data
end
for zone, data in pairs(scannedOverlays) do
if not db[zone] then
db[zone] = data
end
end
end
local function MergeScannedData()
local db = GetOverlayDB()
if not db then return end
for zone, data in pairs(scannedOverlays) do
if not db[zone] or table.getn(db[zone]) < table.getn(data) then
db[zone] = data
end
end
end
local function GetConfig()
if not SFramesDB or type(SFramesDB.MapReveal) ~= "table" then
return { enabled = true, unexploredAlpha = 0.7 }
end
return SFramesDB.MapReveal
end
local function NextPowerOf2(n)
local p = 16
while p < n do
p = p * 2
end
return p
end
local function DoMapRevealUpdate()
local db = GetOverlayDB()
if not db then return end
local mapFileName = GetMapInfo and GetMapInfo()
if not mapFileName then mapFileName = "World" end
local zoneData = db[mapFileName]
if not zoneData then return end
local prefix = "Interface\\WorldMap\\" .. mapFileName .. "\\"
local numExploredOverlays = GetNumMapOverlays and GetNumMapOverlays() or 0
local explored = {}
for i = 1, numExploredOverlays do
local textureName = GetMapOverlayInfo(i)
if textureName and textureName ~= "" then
explored[textureName] = true
end
end
local cfg = GetConfig()
local dimR, dimG, dimB = 0.4, 0.4, 0.4
if cfg.unexploredAlpha then
dimR = cfg.unexploredAlpha
dimG = cfg.unexploredAlpha
dimB = cfg.unexploredAlpha
end
local textureCount = 0
for idx = 1, table.getn(zoneData) do
local entry = zoneData[idx]
local _, _, name, sW, sH, sX, sY = string.find(entry, "^(%u+):(%d+):(%d+):(%d+):(%d+)$")
if not name then
_, _, name, sW, sH, sX, sY = string.find(entry, "^([^:]+):(%d+):(%d+):(%d+):(%d+)$")
end
if name then
local textureWidth = tonumber(sW)
local textureHeight = tonumber(sH)
local offsetX = tonumber(sX)
local offsetY = tonumber(sY)
local textureName = prefix .. name
local isExplored = explored[textureName]
if cfg.enabled or isExplored then
if errata[textureName] then
local e = errata[textureName]
if e.offsetX and e.offsetX[1] == offsetX then
offsetX = e.offsetX[2]
end
if e.offsetY and e.offsetY[1] == offsetY then
offsetY = e.offsetY[2]
end
end
local numTexturesHorz = math.ceil(textureWidth / 256)
local numTexturesVert = math.ceil(textureHeight / 256)
local neededTextures = textureCount + (numTexturesHorz * numTexturesVert)
if neededTextures > NUM_WORLDMAP_OVERLAYS then
for j = NUM_WORLDMAP_OVERLAYS + 1, neededTextures do
WorldMapDetailFrame:CreateTexture("WorldMapOverlay" .. j, "ARTWORK")
end
NUM_WORLDMAP_OVERLAYS = neededTextures
end
for row = 1, numTexturesVert do
local texturePixelHeight, textureFileHeight
if row < numTexturesVert then
texturePixelHeight = 256
textureFileHeight = 256
else
texturePixelHeight = math.mod(textureHeight, 256)
if texturePixelHeight == 0 then texturePixelHeight = 256 end
textureFileHeight = NextPowerOf2(texturePixelHeight)
end
for col = 1, numTexturesHorz do
if textureCount > NUM_WORLDMAP_OVERLAYS then return end
local texture = _G["WorldMapOverlay" .. (textureCount + 1)]
local texturePixelWidth, textureFileWidth
if col < numTexturesHorz then
texturePixelWidth = 256
textureFileWidth = 256
else
texturePixelWidth = math.mod(textureWidth, 256)
if texturePixelWidth == 0 then texturePixelWidth = 256 end
textureFileWidth = NextPowerOf2(texturePixelWidth)
end
texture:SetWidth(texturePixelWidth)
texture:SetHeight(texturePixelHeight)
texture:SetTexCoord(0, texturePixelWidth / textureFileWidth,
0, texturePixelHeight / textureFileHeight)
texture:ClearAllPoints()
texture:SetPoint("TOPLEFT", "WorldMapDetailFrame", "TOPLEFT",
offsetX + (256 * (col - 1)),
-(offsetY + (256 * (row - 1))))
local tileIndex = ((row - 1) * numTexturesHorz) + col
texture:SetTexture(textureName .. tileIndex)
if not isExplored then
texture:SetVertexColor(dimR, dimG, dimB, 1)
else
texture:SetVertexColor(1, 1, 1, 1)
end
texture:Show()
textureCount = textureCount + 1
end
end
end
end
end
end
function MapReveal:Initialize()
local db = GetOverlayDB()
if not db then
SFrames:Print("MapReveal: LibMapOverlayData 未找到,地图揭示功能不可用。")
return
end
PatchOverlayDB()
if not origWorldMapFrame_Update and WorldMapFrame_Update then
origWorldMapFrame_Update = WorldMapFrame_Update
WorldMapFrame_Update = function()
for i = 1, NUM_WORLDMAP_OVERLAYS do
local tex = _G["WorldMapOverlay" .. i]
if tex then tex:Hide() end
end
origWorldMapFrame_Update()
local cfg = GetConfig()
if cfg.enabled then
DoMapRevealUpdate()
end
end
end
end
function MapReveal:Toggle()
if not SFramesDB then SFramesDB = {} end
if type(SFramesDB.MapReveal) ~= "table" then
SFramesDB.MapReveal = { enabled = true, unexploredAlpha = 0.7 }
end
SFramesDB.MapReveal.enabled = not SFramesDB.MapReveal.enabled
if SFramesDB.MapReveal.enabled then
SFrames:Print("地图迷雾揭示: |cff00ff00已开启|r")
if not origWorldMapFrame_Update and WorldMapFrame_Update then
self:Initialize()
end
else
SFrames:Print("地图迷雾揭示: |cffff0000已关闭|r")
end
if WorldMapFrame and WorldMapFrame:IsShown() then
WorldMapFrame_Update()
end
end
function MapReveal:SetAlpha(alpha)
if not SFramesDB or type(SFramesDB.MapReveal) ~= "table" then return end
SFramesDB.MapReveal.unexploredAlpha = alpha
if SFramesDB.MapReveal.enabled and WorldMapFrame and WorldMapFrame:IsShown() then
WorldMapFrame_Update()
end
end
function MapReveal:Refresh()
if WorldMapFrame and WorldMapFrame:IsShown() then
WorldMapFrame_Update()
end
end
--------------------------------------------------------------------------------
-- Map Scanner: enumerate all continents/zones, collect overlay data from
-- explored areas, discover new zones, and merge into the overlay database.
-- Usage: /nui mapscan or SFrames.MapReveal:ScanAllMaps()
--------------------------------------------------------------------------------
local scanFrame = nil
local scanQueue = {}
local scanIndex = 0
local scanRunning = false
local scanResults = {}
local scanNewZones = {}
local scanUpdatedZones = {}
local savedMapC, savedMapZ = 0, 0
local function ExtractOverlayName(fullPath)
if not fullPath or fullPath == "" then return nil end
local _, _, name = string.find(fullPath, "\\([^\\]+)$")
return name and string.upper(name) or nil
end
local function ProcessScanZone()
if scanIndex > table.getn(scanQueue) then
MapReveal:FinishScan()
return
end
local entry = scanQueue[scanIndex]
SetMapZoom(entry.cont, entry.zone)
local mapFile = GetMapInfo and GetMapInfo() or ""
if mapFile == "" then
scanIndex = scanIndex + 1
return
end
local numOverlays = GetNumMapOverlays and GetNumMapOverlays() or 0
if numOverlays > 0 then
local overlays = {}
for i = 1, numOverlays do
local texName, texW, texH, offX, offY = GetMapOverlayInfo(i)
if texName and texName ~= "" then
local name = ExtractOverlayName(texName)
if name then
table.insert(overlays, name .. ":" .. texW .. ":" .. texH .. ":" .. offX .. ":" .. offY)
end
end
end
if table.getn(overlays) > 0 then
local db = GetOverlayDB()
local existing = db and db[mapFile]
local existingCount = existing and table.getn(existing) or 0
if not existing then
scanNewZones[mapFile] = overlays
scanResults[mapFile] = { overlays = overlays, status = "new", count = table.getn(overlays) }
elseif table.getn(overlays) > existingCount then
scanUpdatedZones[mapFile] = overlays
scanResults[mapFile] = { overlays = overlays, status = "updated", count = table.getn(overlays), oldCount = existingCount }
else
scanResults[mapFile] = { status = "ok", count = existingCount }
end
end
end
scanIndex = scanIndex + 1
end
function MapReveal:FinishScan()
scanRunning = false
if scanFrame then
scanFrame:SetScript("OnUpdate", nil)
end
if savedMapZ > 0 then
SetMapZoom(savedMapC, savedMapZ)
elseif savedMapC > 0 then
SetMapZoom(savedMapC, 0)
else
if SetMapToCurrentZone then SetMapToCurrentZone() end
end
local cf = DEFAULT_CHAT_FRAME
local newCount = 0
local updCount = 0
for zone, overlays in pairs(scanNewZones) do
newCount = newCount + 1
scannedOverlays[zone] = overlays
end
for zone, overlays in pairs(scanUpdatedZones) do
updCount = updCount + 1
scannedOverlays[zone] = overlays
end
MergeScannedData()
cf:AddMessage("|cffffb3d9[Nanami-UI]|r 地图扫描完成!")
cf:AddMessage(string.format(" 扫描了 |cff00ff00%d|r 个区域", table.getn(scanQueue)))
if newCount > 0 then
cf:AddMessage(string.format(" 发现 |cff00ff00%d|r 个新区域 (已自动添加迷雾数据):", newCount))
for zone, overlays in pairs(scanNewZones) do
cf:AddMessage(" |cff00ffff" .. zone .. "|r (" .. table.getn(overlays) .. " 个覆盖层)")
end
end
if updCount > 0 then
cf:AddMessage(string.format(" 更新了 |cffffff00%d|r 个区域 (发现更多已探索覆盖层):", updCount))
for zone, info in pairs(scanResults) do
if info.status == "updated" then
cf:AddMessage(" |cffffff00" .. zone .. "|r (" .. (info.oldCount or 0) .. " -> " .. info.count .. ")")
end
end
end
if newCount == 0 and updCount == 0 then
cf:AddMessage(" 所有区域数据已是最新,未发现变动。")
end
cf:AddMessage(" 提示: 新发现的区域仅记录已探索区域的覆盖层,完全探索后再次扫描可获取完整数据。")
if WorldMapFrame and WorldMapFrame:IsShown() then
WorldMapFrame_Update()
end
end
function MapReveal:ScanAllMaps()
if scanRunning then
SFrames:Print("地图扫描正在进行中...")
return
end
local db = GetOverlayDB()
if not db then
SFrames:Print("MapReveal: 未找到覆盖层数据库,无法扫描。")
return
end
scanRunning = true
scanQueue = {}
scanIndex = 1
scanResults = {}
scanNewZones = {}
scanUpdatedZones = {}
savedMapC = GetCurrentMapContinent and GetCurrentMapContinent() or 0
savedMapZ = GetCurrentMapZone and GetCurrentMapZone() or 0
local numContinents = 0
if GetMapContinents then
local continents = { GetMapContinents() }
numContinents = table.getn(continents)
end
for c = 1, numContinents do
local zones = { GetMapZones(c) }
for z = 1, table.getn(zones) do
table.insert(scanQueue, { cont = c, zone = z, name = zones[z] or "" })
end
end
SFrames:Print("开始扫描所有地图... (共 " .. table.getn(scanQueue) .. " 个区域)")
if not scanFrame then
scanFrame = CreateFrame("Frame")
end
local scanElapsed = 0
scanFrame:SetScript("OnUpdate", function()
scanElapsed = scanElapsed + (arg1 or 0)
if scanElapsed < 0.02 then return end
scanElapsed = 0
if not scanRunning then return end
local batch = 0
while scanIndex <= table.getn(scanQueue) and batch < 5 do
ProcessScanZone()
batch = batch + 1
end
if scanIndex > table.getn(scanQueue) then
MapReveal:FinishScan()
end
end)
end
function MapReveal:ExportScannedData()
local cf = DEFAULT_CHAT_FRAME
local hasData = false
for zone, overlays in pairs(scannedOverlays) do
hasData = true
cf:AddMessage("|cffffb3d9[MapReveal Export]|r |cff00ffff" .. zone .. "|r = {")
for _, entry in ipairs(overlays) do
cf:AddMessage(' "' .. entry .. '",')
end
cf:AddMessage("}")
end
if not hasData then
cf:AddMessage("|cffffb3d9[MapReveal]|r 没有扫描到的新数据可导出。先运行 /nui mapscan")
end
end