Files
2026-10-27 14:08:54 +00:00

605 lines
24 KiB
Lua

-- Freewood-OctoWoW-Codex: Wegenetz auf der Weltkarte anzeigen (Dev-Reiter, /fwwege).
-- Ebenen: Wege (gruen), Faelle (rot), Gebietsuebergaenge (je Art eine Farbe), Luecken (gelb: bekannte Quest-Geber/-Abgaben und
-- Dienst-NPCs, bei denen noch kein Weg aufgezeichnet ist). Die Welt-Koordinaten des Wegenetzes werden mit drei Messpunkten
-- aus der Kartenschnittstelle in Kartenanteile umgerechnet (die Abbildung ist je Karte linear).
local ADDON, NS = ...
local FW = _G.Freewood
local Codex = NS and NS.Codex
if not FW or not Codex or not Codex.Path then
return
end
local Path = Codex.Path
local PM = { on = false }
Codex.PathMap = PM
local GRID = Codex.GRID or 200
local CELL = Path.CELL or 10
local BIN = 1 / 300 -- Karten-Anteil je Punkt (Raster der Anzeige)
local MAX_DOTS = 5000
local GAP_RADIUS = 0.012 -- so nah muss ein Weg an einem bekannten Ort liegen, damit er nicht als Luecke zaehlt
local MAX_GAPS = 250
local LAYER_LEVEL = 1400
local PROBE = 100 -- Yards Abstand der Messpunkte fuer die Umrechnung
local COLORS = {
w = { 0.30, 0.90, 0.40 }, -- Weg
f = { 0.75, 0.45, 1.00 }, -- Flugroute
r = { 0.10, 0.35, 1.00 }, -- fremder Weg (empfangen oder aus Startdaten): Blau, auf dem Pergament gut sichtbar
d = { 1.00, 0.25, 0.20 }, -- Fall
g = { 1.00, 0.85, 0.10 }, -- Luecke
lw = { 0.20, 0.80, 1.00 }, -- Grenzuebergang
lt = { 0.90, 0.30, 0.90 }, -- Sprung (Zeppelin, Portal)
lf = { 0.60, 0.60, 1.00 }, -- Flugpunkt
li = { 1.00, 0.60, 0.10 }, -- Instanz
s = { 1.00, 1.00, 1.00 }, -- Ortspunkt (nur fuer Berechtigte)
}
local function View()
local db = Codex.db
db.settings.pathView = db.settings.pathView or {}
return db.settings.pathView
end
function PM.LayerOn(k)
local v = View()[k]
return v ~= false
end
function PM.SetLayer(k, on)
View()[k] = on and true or false
PM.Refresh()
end
------------------------------------------------------------------------
-- Umrechnung Welt -> Karte
------------------------------------------------------------------------
local function Vec(a, b)
return _G.CreateVector2D and _G.CreateVector2D(a, b) or { x = a, y = b, GetXY = function(self) return self.x, self.y end }
end
-- Kartenanteil zu einer Weltposition; first/second wie UnitPosition (erster, zweiter Rueckgabewert)
local function MapPosOf(world, first, second, mapID, swap)
local CM = _G.C_Map
if not (CM and CM.GetMapPosFromWorldPos) then return nil end
local vec = swap and Vec(second, first) or Vec(first, second)
local ok, id, pos = pcall(CM.GetMapPosFromWorldPos, world, vec, mapID)
if not ok or not pos or not pos.GetXY then return nil end
local ok2, x, y = pcall(pos.GetXY, pos)
if not ok2 or type(x) ~= "number" or type(y) ~= "number" then return nil end
return x, y
end
local function WorldOfMap(mapID)
local CM = _G.C_Map
if not (CM and CM.GetWorldPosFromMapPos) then return nil end
local ok, cont = pcall(CM.GetWorldPosFromMapPos, mapID, Vec(0.5, 0.5))
if ok and type(cont) == "number" then return cont end
return nil
end
local function CellCentre(cx, cy)
-- Zellen wurden aus x = zweiter, y = erster Rueckgabewert von UnitPosition gebildet
return (cy + 0.5) * CELL, (cx + 0.5) * CELL -- first, second
end
local function InMap(x, y)
return x and y and x >= 0 and x <= 1 and y >= 0 and y <= 1
end
-- Erste Zellen einer Welt (fuer die Messpunkte)
local function SampleCells(cells, n)
local out = {}
for key in pairs(cells) do
out[#out + 1] = key
if #out >= n then break end
end
return out
end
-- Orientierung bestimmen: erst am Spieler (falls er auf der Karte steht), sonst nach der Zahl der Zellen, die auf die Karte fallen
local function DetectSwap(world, cells, mapID)
local db = Codex.db
local saved = db.settings.pathSwap
if type(saved) == "boolean" then return saved end
local CM = _G.C_Map
local pos = Path.Read and Path.Read()
if pos and pos.world == world and CM and CM.GetPlayerMapPosition then
local ok, mp = pcall(CM.GetPlayerMapPosition, mapID, "player")
if ok and mp and mp.GetXY then
local px, py = mp:GetXY()
local best, bestErr
for _, swap in ipairs({ false, true }) do
local x, y = MapPosOf(world, pos.y, pos.x, mapID, swap)
if x then
local err = math.abs(x - px) + math.abs(y - py)
if not bestErr or err < bestErr then best, bestErr = swap, err end
end
end
if best ~= nil and bestErr < 0.05 then
db.settings.pathSwap = best
return best
end
end
end
local score = { [false] = 0, [true] = 0 }
for _, key in ipairs(SampleCells(cells, 40)) do
local cx, cy = Path.KeyToCell(key)
local f, s = CellCentre(cx, cy)
for _, swap in ipairs({ false, true }) do
local x, y = MapPosOf(world, f, s, mapID, swap)
if InMap(x, y) then score[swap] = score[swap] + 1 end
end
end
return score[true] > score[false]
end
-- Lineare Abbildung (first, second) -> (x, y) der Karte, aus drei Messpunkten
local function Fit(world, cells, mapID, swap)
for _, key in ipairs(SampleCells(cells, 60)) do
local cx, cy = Path.KeyToCell(key)
local f0, s0 = CellCentre(cx, cy)
local x0, y0 = MapPosOf(world, f0, s0, mapID, swap)
if InMap(x0, y0) then
local x1, y1 = MapPosOf(world, f0 + PROBE, s0, mapID, swap)
local x2, y2 = MapPosOf(world, f0, s0 + PROBE, mapID, swap)
if x1 and x2 then
local a, b, c, d = (x1 - x0) / PROBE, (x2 - x0) / PROBE, (y1 - y0) / PROBE, (y2 - y0) / PROBE
if a ~= 0 or b ~= 0 or c ~= 0 or d ~= 0 then
return function(f, s)
return x0 + (f - f0) * a + (s - s0) * b, y0 + (f - f0) * c + (s - s0) * d
end
end
end
end
end
return nil
end
-- Ersatz, wenn keine der Stichprobenzellen auf der Karte liegt (kleine Karten wie Staedte): Mittelpunkt der Karte
-- in Weltkoordinaten erfragen und die Abbildung dort messen
local function FitFromMap(world, mapID, swap)
local CM = _G.C_Map
if not (CM and CM.GetWorldPosFromMapPos) then return nil end
local ok, cont, wp = pcall(CM.GetWorldPosFromMapPos, mapID, Vec(0.5, 0.5))
if not ok or cont ~= world or not wp or not wp.GetXY then return nil end
local ok2, vx, vy = pcall(wp.GetXY, wp)
if not ok2 or type(vx) ~= "number" or type(vy) ~= "number" then return nil end
local f0, s0
if swap then f0, s0 = vy, vx else f0, s0 = vx, vy end
local x0, y0 = MapPosOf(world, f0, s0, mapID, swap)
local x1, y1 = MapPosOf(world, f0 + PROBE, s0, mapID, swap)
local x2, y2 = MapPosOf(world, f0, s0 + PROBE, mapID, swap)
if not (x0 and x1 and x2) then return nil end
local a, b, c, d = (x1 - x0) / PROBE, (x2 - x0) / PROBE, (y1 - y0) / PROBE, (y2 - y0) / PROBE
if a == 0 and b == 0 and c == 0 and d == 0 then return nil end
return function(f, s)
return x0 + (f - f0) * a + (s - s0) * b, y0 + (f - f0) * c + (s - s0) * d
end
end
------------------------------------------------------------------------
-- Daten einer Karte
------------------------------------------------------------------------
-- Rueckgabe: { dots = { { x, y, kind, w } }, counts = { w, d, x, g }, swap }, oder nil, wenn nichts umgerechnet werden kann
function PM.Compute(mapID)
local db = Codex.db
if not (db and mapID) then return nil end
local res = { dots = {}, counts = { w = 0, d = 0, x = 0, g = 0 }, mapID = mapID }
local world = WorldOfMap(mapID)
local cells = world and Path.Cells(world)
local map
res.world, res.fit, res.ncells = world, "none", 0
if cells and next(cells) then
for _ in pairs(cells) do res.ncells = res.ncells + 1 end
res.swap = DetectSwap(world, cells, mapID)
map = Fit(world, cells, mapID, res.swap)
if map then
res.fit = "cells"
else
map = FitFromMap(world, mapID, res.swap)
if map then res.fit = "map" end
end
end
local bins = {}
local function Bin(x, y, kind, w)
local bx, by = math.floor(x / BIN), math.floor(y / BIN)
local key = by * 100000 + bx
local b = bins[key]
if not b then b = { x = x, y = y, kind = kind, w = w }; bins[key] = b
elseif kind == "d" then b.kind = "d" end
return b
end
local pathHash = {}
local own = (db.path and db.path[world]) or {}
if map then
for key, mask in pairs(cells) do
local cx, cy = Path.KeyToCell(key)
local f, s = CellCentre(cx, cy)
local x, y = map(f, s)
if InMap(x, y) then
local isDrop = math.floor(mask / 256) % 256 > 0
local mine = own[key] ~= nil
local b = Bin(x, y, isDrop and "d" or (mine and "w" or "r"), 1)
if b.kind == "r" and mine then b.kind = "w" end -- eigener Weg geht vor
if isDrop then res.counts.d = res.counts.d + 1
elseif mine then res.counts.w = res.counts.w + 1
else res.counts.r = (res.counts.r or 0) + 1 end
local hx, hy = math.floor(x / GAP_RADIUS), math.floor(y / GAP_RADIUS)
pathHash[hy * 100000 + hx] = true
end
end
end
-- Flugrouten dieser Karte: Strecke als Punktreihe, Beschriftung in der Mitte
local flights, tags = {}, {}
local TX = Codex.Taxi
if map and TX and TX.Routes then
for _, r in pairs(TX.Routes()) do
local na, nb = TX.Node(r.a), TX.Node(r.b)
if na and nb and na.w == world and r.p and #r.p >= 4 then
local pts = {}
if na.a then pts[#pts + 1] = { na.a, na.b } end
for i = 1, #r.p - 1, 2 do pts[#pts + 1] = { r.p[i], r.p[i + 1] } end
if nb.a then pts[#pts + 1] = { nb.a, nb.b } end
local xs, ys = {}, {}
for i, q in ipairs(pts) do xs[i], ys[i] = map(q[1], q[2]) end
local any = false
for i = 1, #pts - 1 do
local x1, y1, x2, y2 = xs[i], ys[i], xs[i + 1], ys[i + 1]
if x1 and x2 and (InMap(x1, y1) or InMap(x2, y2)) then
any = true
local steps = math.max(1, math.min(40, math.ceil(math.max(math.abs(x2 - x1), math.abs(y2 - y1)) / 0.004)))
for k = 0, steps - 1 do
local fx, fy = x1 + (x2 - x1) * k / steps, y1 + (y2 - y1) * k / steps
if InMap(fx, fy) then flights[#flights + 1] = { x = fx, y = fy, kind = "f", w = 4 } end
end
end
end
if any then
local mid = math.max(1, math.floor(#pts / 2))
if xs[mid] and InMap(xs[mid], ys[mid]) then
tags[#tags + 1] = { x = xs[mid], y = ys[mid],
text = string.format("%s > %s %s %s", na.n or "?", nb.n or "?", TX.Duration(r.t), TX.Money(r.c)) }
end
end
end
end
end
res.counts.f = #tags
res.tags = tags
-- Luecken: bekannte Orte ohne Weg in der Naehe
local function NearPath(x, y)
local hx, hy = math.floor(x / GAP_RADIUS), math.floor(y / GAP_RADIUS)
for dy = -1, 1 do
for dx = -1, 1 do
if pathHash[(hy + dy) * 100000 + hx + dx] then return true end
end
end
return false
end
local gaps, seen = {}, {}
local function Gap(x, y)
if not x or #gaps >= MAX_GAPS or not InMap(x, y) or NearPath(x, y) then return end
local k = math.floor(x / GAP_RADIUS) * 100000 + math.floor(y / GAP_RADIUS)
if seen[k] then return end
seen[k] = true
gaps[#gaps + 1] = { x = x, y = y, kind = "g", w = 2 }
end
local V = Codex.V and Codex.V()
for _, q in pairs(V and V.quests or {}) do
for _, p in ipairs({ q.give, q.turn }) do
if type(p) == "table" and p.m == mapID then Gap(p.x, p.y) end
end
end
for _, u in pairs(V and V.units or {}) do
if u.ro and next(u.ro) and u.cells then
for key in pairs(u.cells) do
local m = math.floor(key / (GRID * GRID))
if m == mapID then
local idx = key % (GRID * GRID)
Gap(((idx % GRID) + 0.5) / GRID, (math.floor(idx / GRID) + 0.5) / GRID)
break
end
end
end
end
res.counts.g = #gaps
-- Gebietsuebergaenge dieser Karte
local crossings = {}
local function AddLink(a, b, kinds)
for kind, e in pairs(kinds) do
for _, p in ipairs(e.p or {}) do
local key, w
if a == mapID then key, w = p.fk, p.fw elseif b == mapID then key, w = p.tk, p.tw end
if key and w == world and map then
local cx, cy = Path.KeyToCell(key)
local f, s = CellCentre(cx, cy)
local x, y = map(f, s)
if InMap(x, y) then crossings[#crossings + 1] = { x = x, y = y, kind = "l" .. kind, w = 3, to = a == mapID and b or a } end
end
end
end
end
for a, row in pairs(Path.Links()) do
for b, kinds in pairs(row) do
if a == mapID or b == mapID then AddLink(a, b, kinds) end
end
end
res.counts.x = #crossings
-- Ortspunkte dieser Karte (Instanz-Eingaenge, Aufstiege): nur fuer Berechtigte
local spotDots = {}
local SP = Codex.Spots
if SP and Codex.Trust and Codex.Trust.CanEdit() then
for _, r in ipairs(SP.All()) do
if r.p.m == mapID and InMap(r.p.x, r.p.y) then
spotDots[#spotDots + 1] = { x = r.p.x, y = r.p.y, kind = "s", w = 3, name = r.n, sk = r.k }
end
end
end
res.counts.s = #spotDots
-- Reihenfolge: Wege unten, dann Faelle, Luecken, Uebergaenge oben
local list = {}
for _, b in pairs(bins) do list[#list + 1] = b end
table.sort(list, function(p, q)
if p.kind ~= q.kind then
local rank = { r = 0, w = 1 }
local a, b2 = rank[p.kind] or 2, rank[q.kind] or 2
return a < b2
end
if p.y ~= q.y then return p.y < q.y end
return p.x < q.x
end)
for _, b in ipairs(list) do res.dots[#res.dots + 1] = b end
for _, g in ipairs(flights) do res.dots[#res.dots + 1] = g end
for _, g in ipairs(gaps) do res.dots[#res.dots + 1] = g end
for _, c in ipairs(crossings) do res.dots[#res.dots + 1] = c end
for _, c in ipairs(spotDots) do res.dots[#res.dots + 1] = c end
return res
end
-- Karten, die Daten haben (fuer die Auswahl): { { map, n } } nach Zahl der Uebergaenge
function PM.Maps()
local db = Codex.db
local count = {}
for a, row in pairs(Path.Links()) do
for b, kinds in pairs(row) do
local n = 0
for _, e in pairs(kinds) do n = n + e.n end
if a > 0 then count[a] = (count[a] or 0) + n end
if b > 0 then count[b] = (count[b] or 0) + n end
end
end
local cur = _G.C_Map and _G.C_Map.GetBestMapForUnit and select(2, pcall(_G.C_Map.GetBestMapForUnit, "player"))
if type(cur) == "number" then count[cur] = count[cur] or 0 end
local out = {}
for m, n in pairs(count) do out[#out + 1] = { map = m, n = n } end
table.sort(out, function(x, y)
if x.n ~= y.n then return x.n > y.n end
return x.map < y.map
end)
return out
end
------------------------------------------------------------------------
-- Anzeige auf der Weltkarte
------------------------------------------------------------------------
local layer, pool, label
local API = Codex.MapAPI
local function Canvas()
return API.Canvas()
end
local function CurrentMapID()
return API.CurrentMapID(function()
local list = {}
for _, e in ipairs(PM.Maps()) do list[#list + 1] = e.map end
return list
end)
end
local function Scale(frame)
local s = 1
if frame and frame.GetEffectiveScale and _G.UIParent then
local a, b = frame:GetEffectiveScale(), _G.UIParent:GetEffectiveScale()
if type(a) == "number" and type(b) == "number" and a > 0 and b > 0 then s = a / b end
end
return s
end
local function EnsureLayer()
if layer then return layer end
local wm = _G.WorldMapFrame
if not (wm and Canvas()) then return nil end
if API.Classic() then
local canvas = Canvas()
layer = CreateFrame("Frame", nil, canvas)
layer:SetAllPoints(canvas)
layer:SetFrameLevel((canvas:GetFrameLevel() or 1) + 15)
else
layer = CreateFrame("Frame", nil, wm)
layer:SetAllPoints(wm.ScrollContainer or wm)
layer:SetFrameLevel((wm:GetFrameLevel() or 1) + LAYER_LEVEL)
end
if wm.GetFrameStrata then layer:SetFrameStrata(wm:GetFrameStrata()) end
if layer.SetClipsChildren then layer:SetClipsChildren(true) end
pool = {}
label = layer:CreateFontString(nil, "OVERLAY", "GameFontNormal")
label:SetPoint("TOPLEFT", layer, "TOPLEFT", 12, API.Classic() and -42 or -12)
label:SetJustifyH("LEFT")
local acc, lastScale, lastMap = 0, nil, nil
layer:SetScript("OnUpdate", function(_, elapsed)
acc = acc + (elapsed or 0)
if acc < 0.3 then return end
acc = 0
if not PM.on then return end
local sc, m = Scale(Canvas()), API.Signature()
if sc ~= lastScale or m ~= lastMap then
lastScale, lastMap = sc, m
PM.Refresh()
end
end)
return layer
end
local function Dot(i)
local t = pool[i]
if not t then
t = layer:CreateTexture(nil, "OVERLAY")
pool[i] = t
end
return t
end
function PM.Refresh()
if not layer then
if not PM.on then return end
EnsureLayer()
end
if not layer then return end
for _, t in ipairs(pool) do t:Hide() end
for _, t in ipairs(PM.tagPool or {}) do t:Hide() end
if not PM.on then
label:SetText("")
layer:Hide()
return
end
layer:Show()
local mapID = CurrentMapID()
local canvas = Canvas()
if not (mapID and canvas) then
label:SetText(FW.L.CODEX_PATHMAP_NONE) -- nicht die Zeile der vorherigen Karte stehen lassen
return
end
local width, height = canvas:GetWidth() or 1, canvas:GetHeight() or 1
if width < 2 or height < 2 then return end
local res = PM.Compute(mapID)
PM.last = res
local L = FW.L
if not res or (res.counts.w + (res.counts.f or 0) + (res.counts.r or 0) + res.counts.d + res.counts.x + res.counts.g + (res.counts.s or 0)) == 0 then
label:SetText(L.CODEX_PATHMAP_NONE)
return
end
label:SetText(string.format(L.CODEX_PATHMAP_LABEL, res.counts.w, res.counts.d, res.counts.x, res.counts.g))
local scale, layerScale = Scale(canvas), Scale(layer)
local ratio = scale / layerScale
PM.drawInfo = { w = width, h = height, scale = scale, layerScale = layerScale, total = #res.dots, drawn = 0, shown = layer:IsShown() and true or false }
local n = 0
for _, d in ipairs(res.dots) do
local show
if d.kind == "w" then show = PM.LayerOn("w")
elseif d.kind == "r" then show = PM.LayerOn("r")
elseif d.kind == "f" then show = PM.LayerOn("f")
elseif d.kind == "d" then show = PM.LayerOn("d")
elseif d.kind == "g" then show = PM.LayerOn("g")
elseif d.kind == "s" then show = PM.LayerOn("s")
else show = PM.LayerOn("x") end
if show then
n = n + 1
if n > MAX_DOTS then break end
PM.drawInfo.drawn = n
local t = Dot(n)
local px = (d.w == 1 and 3 or (d.w == 4 and 4 or d.w == 2 and 8 or (d.kind == "s" and 14 or 10))) / layerScale
local c = COLORS[d.kind] or COLORS.w
t:ClearAllPoints()
t:SetSize(px, px)
t:SetPoint("CENTER", canvas, "TOPLEFT", d.x * width * ratio, -d.y * height * ratio)
t:SetColorTexture(c[1], c[2], c[3], (d.kind == "w" or d.kind == "r") and 0.85 or 1)
t:Show()
end
end
-- Beschriftung der Flugrouten
if PM.LayerOn("f") and res.tags then
PM.tagPool = PM.tagPool or {}
for i, tg in ipairs(res.tags) do
if i > 20 then break end
local fs = PM.tagPool[i]
if not fs then
fs = layer:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
PM.tagPool[i] = fs
end
fs:ClearAllPoints()
fs:SetPoint("BOTTOM", canvas, "TOPLEFT", tg.x * width * ratio, -tg.y * height * ratio + 4)
fs:SetText(tg.text)
fs:SetTextColor(COLORS.f[1], COLORS.f[2], COLORS.f[3])
fs:Show()
end
end
end
-- Weltkarte oeffnen, auf eine Karte stellen (optional) und die Ebenen einblenden
function PM.Show(mapID)
PM.on = true
local wm = _G.WorldMapFrame
if wm then
if not wm:IsShown() then
if type(_G.ToggleWorldMap) == "function" then pcall(_G.ToggleWorldMap) elseif wm.Show then pcall(wm.Show, wm) end
end
if mapID then API.SetMap(mapID) end
if not PM.hooked then
PM.hooked = true
if wm.OnMapChanged then pcall(_G.hooksecurefunc, wm, "OnMapChanged", function() PM.Refresh() end) end
if wm.HookScript then wm:HookScript("OnShow", function() PM.Refresh() end) end
if API.Classic() and _G.hooksecurefunc and _G.SetMapZoom then
pcall(_G.hooksecurefunc, "SetMapZoom", function() PM.Refresh() end)
end
end
end
PM.Refresh()
FW:Fire("CODEX_PATHMAP_CHANGED")
end
function PM.Hide()
PM.on = false
PM.Refresh()
FW:Fire("CODEX_PATHMAP_CHANGED")
end
function PM.Toggle(mapID)
if PM.on then PM.Hide() else PM.Show(mapID) end
end
-- /fwwege diag: Diagnose fuer die Karte, die gerade angezeigt wird (warum ist das Wegenetz leer?)
function PM.Diag()
local CM = _G.C_Map
local mapID = CurrentMapID()
FW:Print("Wegenetz-Diagnose: Karte " .. tostring(mapID))
if not mapID then return end
local info = CM and CM.GetMapInfo and select(2, pcall(CM.GetMapInfo, mapID))
FW:Print("Name: " .. tostring(type(info) == "table" and info.name or "?") .. ", Eltern-Karte: " .. tostring(type(info) == "table" and info.parentMapID or "?"))
local ok, cont, wp = false, nil, nil
if CM and CM.GetWorldPosFromMapPos then ok, cont, wp = pcall(CM.GetWorldPosFromMapPos, mapID, Vec(0.5, 0.5)) end
local vx, vy
if ok and wp and wp.GetXY then vx, vy = wp:GetXY() end
FW:Print(string.format("Kartenmitte in Weltkoordinaten: ok=%s, Welt=%s, x=%s, y=%s", tostring(ok), tostring(cont), tostring(vx), tostring(vy)))
local res = PM.Compute(mapID)
if not res then FW:Print("Compute: nil") return end
FW:Print(string.format("Welt %s, Zellen der Welt: %d, gespeicherte Achsen-Vertauschung: %s, benutzt: %s",
tostring(res.world), res.ncells or 0, tostring(Codex.db.settings.pathSwap), tostring(res.swap)))
FW:Print(string.format("Umrechnung: %s (cells = aus Zellen, map = aus Kartenmitte, none = keine)", tostring(res.fit)))
local di = PM.drawInfo
if di then
FW:Print(string.format("Zeichnen: Punkte %d, gezeichnet %d, Flaeche %.0fx%.0f, Skalierung %.2f / Ebene %.2f, Ebene sichtbar %s, an: %s",
di.total, di.drawn, di.w, di.h, di.scale, di.layerScale, tostring(di.shown), tostring(PM.on)))
else
FW:Print("Zeichnen: noch nicht gezeichnet (Wegenetz einschalten mit /fwwege)")
end
local flags = {}
for _, k in ipairs({ "w", "r", "f", "d", "g", "s", "x" }) do flags[#flags + 1] = k .. "=" .. (PM.LayerOn(k) and "an" or "AUS") end
FW:Print("Ebenen: " .. table.concat(flags, " "))
local c = res.counts
FW:Print(string.format("Punkte auf dieser Karte: eigene %d, fremde %d, Beute %d, Uebergaenge %d, Luecken %d, Orte %d",
c.w or 0, c.r or 0, c.d or 0, c.x or 0, c.g or 0, c.s or 0))
end
SLASH_FREEWOODPATHMAP1 = "/fwwege"
SlashCmdList["FREEWOODPATHMAP"] = function(msg)
if type(msg) == "string" and msg:lower():match("^%s*diag") then PM.Diag() else PM.Toggle() end
end