-- Freewood-OctoWoW-Codex: Kartenschnittstelle fuer die Weltkarte des 1.12-Clients (OctoWoW). -- Die alte Karte kennt weder SetMapID noch GetMapID noch eine Kartenflaeche mit Zoom. Stattdessen: -- Kartenflaeche = WorldMapDetailFrame (Anteile 0..1 von links oben), Zone umschalten per SetMapZoom(Kontinent, Zone). -- Die Zuordnung Karten-ID (AreaTable) -> Kontinent/Zone laeuft ueber den Zonennamen (C_Map.GetMapInfo gegen GetMapZones) -- und wird zusaetzlich beim Spielen gelernt (stehst du in einer Zone, merkt sich Codex ihre Nummer). -- Auf Clients mit Retail-Karte (SetMapID/GetMapID) werden diese Funktionen direkt benutzt. local ADDON, NS = ... local FW = _G.Freewood local Codex = NS and NS.Codex if not FW or not Codex then return end local API = {} Codex.MapAPI = API local resolved = {} -- [mapID] = "c:z" oder false (nicht gefunden) local zoneToID = {} -- ["c:z"] = mapID local function Learned() local st = Codex.db and Codex.db.settings if not st then return {} end st.mapZones = st.mapZones or {} return st.mapZones end local function Norm(s) if type(s) ~= "string" then return nil end s = string.gsub(string.lower(s), "[^%w]", "") if s == "" then return nil end return s end function API.Classic() local wm = _G.WorldMapFrame return not (wm and wm.GetMapID and wm.SetMapID) end function API.Canvas() local wm = _G.WorldMapFrame if not wm then return nil end if not API.Classic() then if wm.GetCanvas then local ok, c = pcall(wm.GetCanvas, wm) if ok and c then return c end end return wm.ScrollContainer and wm.ScrollContainer.Child or nil end return _G.WorldMapDetailFrame or _G.WorldMapButton end -- Kontinent/Zone der angezeigten Karte function API.Current() if not (_G.GetCurrentMapContinent and _G.GetCurrentMapZone) then return nil end local c, z = _G.GetCurrentMapContinent(), _G.GetCurrentMapZone() if type(c) ~= "number" or type(z) ~= "number" then return nil end return c, z end function API.Signature() local c, z = API.Current() return c and (c .. ":" .. z) or "?" end local function MapName(id) local CM = _G.C_Map if CM and CM.GetMapInfo then local ok, info = pcall(CM.GetMapInfo, id) if ok and type(info) == "table" and type(info.name) == "string" then return info.name end end if _G.C_Map and _G.C_Map.GetAreaInfo then local ok, name = pcall(_G.C_Map.GetAreaInfo, id) if ok and type(name) == "string" then return name end end return nil end -- Karten-ID -> Kontinent, Zone (Zone 0 = Kontinentkarte) function API.Resolve(id) if type(id) ~= "number" then return nil end local key = Learned()[id] or resolved[id] if key == nil then key = false local want = Norm(MapName(id)) if want and _G.GetMapContinents and _G.GetMapZones then local conts = { _G.GetMapContinents() } for c = 1, #conts do if Norm(conts[c]) == want then key = c .. ":0" break end local zones = { _G.GetMapZones(c) } for z = 1, #zones do if Norm(zones[z]) == want then key = c .. ":" .. z break end end if key then break end end end resolved[id] = key end if not key then return nil end local _, _, c, z = string.find(key, "^(%d+):(%d+)$") return tonumber(c), tonumber(z) end -- Karten-ID der angezeigten Karte. cands: Liste oder Funktion, die Karten-IDs liefert, unter denen gesucht wird. function API.CurrentMapID(cands) local wm = _G.WorldMapFrame if not API.Classic() then if wm and wm.GetMapID then local ok, id = pcall(wm.GetMapID, wm) if ok then return id end end return nil end local c, z = API.Current() if not c then return nil end local key = c .. ":" .. z if zoneToID[key] then return zoneToID[key] end for id, k in pairs(Learned()) do if k == key then zoneToID[key] = id return id end end if type(cands) == "function" then cands = cands() end local list = cands or {} local bm = _G.C_Map and _G.C_Map.GetBestMapForUnit and select(2, pcall(_G.C_Map.GetBestMapForUnit, "player")) if type(bm) == "number" then list[#list + 1] = bm end for i = 1, #list do local id = list[i] local rc, rz = API.Resolve(id) if rc == c and rz == z then zoneToID[key] = id return id end end -- Zone ohne bekannte Daten (und noch nie betreten): Karten-ID ueber den Zonennamen suchen. -- Quelle: zuerst der Karten-Scan (db.cat.m), dann alle Nummern 1..8000 durchprobieren. Ergebnis wird gemerkt (auch dauerhaft), -- ein Fehlschlag fuer 60 Sekunden. local now = _G.GetTime and _G.GetTime() or 0 if API.noMatch and API.noMatch[key] and now - API.noMatch[key] < 60 then return nil end local zname if _G.GetMapContinents and _G.GetMapZones then if z == 0 then zname = ({ _G.GetMapContinents() })[c] else zname = ({ _G.GetMapZones(c) })[z] end end local target = Norm(zname) if target then local ids = {} local m = Codex.db and Codex.db.cat and Codex.db.cat.m -- erst die Karten aus dem Scan (schnell), dann der ganze Bereich: der Scan kennt nicht jede Karte (z. B. Blackstone Island) local seen = {} if type(m) == "table" then for id in pairs(m) do if type(id) == "number" then ids[#ids + 1] = id; seen[id] = true end end table.sort(ids) end for id = 1, 8000 do if not seen[id] then ids[#ids + 1] = id end end for i = 1, #ids do local id = ids[i] if Norm(MapName(id)) == target then zoneToID[key] = id resolved[id] = key Learned()[id] = key return id end end end API.noMatch = API.noMatch or {} API.noMatch[key] = now return nil end -- Blizzards Kartenfenster (Titel, Auswahlfelder) nachziehen, falls es die Umschaltung nicht selbst tut function API.RefreshWindow(c, z) local names = { "WorldMapFrame_Update", "WorldMapContinentDropDown_Update", "WorldMapZoneDropDown_Update", "WorldMapZoomOutButton_Update" } for i = 1, #names do local f = _G[names[i]] if type(f) == "function" then pcall(f) end end local title = _G.WorldMapFrameTitle or _G.WorldMapFrameTitleText if title and title.SetText and _G.GetMapZones and _G.GetMapContinents then local name if z == 0 then local conts = { _G.GetMapContinents() } name = conts[c] else local zones = { _G.GetMapZones(c) } name = zones[z] end if name then pcall(title.SetText, title, name) end end end function API.SetMap(id) local wm = _G.WorldMapFrame if not API.Classic() then if wm and wm.SetMapID then pcall(wm.SetMapID, wm, id) return true end return false end local c, z = API.Resolve(id) if not c or not _G.SetMapZoom then return false end if z == 0 then pcall(_G.SetMapZoom, c) else pcall(_G.SetMapZoom, c, z) end API.RefreshWindow(c, z) if _G.C_Timer and _G.C_Timer.After then _G.C_Timer.After(0.2, function() API.RefreshWindow(c, z) end) end return true end -- Mauszeiger auf der Kartenflaeche als Anteil 0..1 (links oben), sonst nil function API.CursorNorm() local canvas = API.Canvas() if not (canvas and canvas.IsVisible and canvas:IsVisible()) then return nil end local l, t, w, h = canvas:GetLeft(), canvas:GetTop(), canvas:GetWidth(), canvas:GetHeight() if not (l and t and w and h and w > 1 and h > 1) then return nil end local cx, cy = _G.GetCursorPosition() local s = canvas:GetEffectiveScale() if not (cx and s and s > 0) then return nil end local x, y = (cx / s - l) / w, (t - cy / s) / h if x < 0 or x > 1 or y < 0 or y > 1 then return nil end return x, y end -- Zonen-Nummern lernen: Karte kurz auf die aktuelle Zone stellen, solange das Kartenfenster zu ist local function LearnHere() local wm = _G.WorldMapFrame if not API.Classic() or (wm and wm:IsShown()) then return end if not (_G.SetMapToCurrentZone and _G.C_Map and _G.C_Map.GetBestMapForUnit) then return end local ok, id = pcall(_G.C_Map.GetBestMapForUnit, "player") if not ok or type(id) ~= "number" then return end pcall(_G.SetMapToCurrentZone) local c, z = API.Current() if c and z and z > 0 then local L = Learned() L[id] = c .. ":" .. z zoneToID[c .. ":" .. z] = id end end local function LearnLater() if _G.C_Timer and _G.C_Timer.After then _G.C_Timer.After(1.5, LearnHere) else LearnHere() end end local f = CreateFrame("Frame") f:RegisterEvent("PLAYER_ENTERING_WORLD") f:RegisterEvent("ZONE_CHANGED_NEW_AREA") f:SetScript("OnEvent", LearnLater) -- /fwcodex mapzones: zeigt die Zuordnung fuer die angezeigte Karte function API.Debug() local c, z = API.Current() FW:Print("Karte: Kontinent " .. tostring(c) .. ", Zone " .. tostring(z) .. ", Flaeche " .. tostring(API.Canvas() and API.Canvas():GetName()) .. ", klassisch: " .. tostring(API.Classic())) local n = 0 for id, k in pairs(Learned()) do n = n + 1 end FW:Print("Gelernte Zonen: " .. n) end