a29e891c8d
The full screen scales WorldMapFrame up to fit the map, which doubled the quest POIs. Their scale is now the small window's over the current one and their offsets are divided by it, in the same pass that follows the zoom. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
512 lines
17 KiB
Lua
512 lines
17 KiB
Lua
-- World map zoom: the mouse wheel enlarges the map toward the cursor (x 1.25 per step, 1 to 4)
|
|
-- and a left-button drag pans the zoomed map; a click without drag keeps its meaning.
|
|
-- Camelot zooms through map art layers (mapcanvas_scrollcontainermixin.lua), which 3.3.5
|
|
-- lacks. The zoom returns to 1 when the map changes, changes mode or closes.
|
|
-- The map is ENLARGED, not scaled: WorldMapDetailFrame and WorldMapButton get their width and
|
|
-- height x zoom at their own scale. The client places every marker at its map fraction x
|
|
-- their width, so the markers move with the map and keep their size, as in camelot. The art
|
|
-- (tiles, explored areas) has fixed sizes in the client: it is laid out and cropped here.
|
|
-- Units: "map units" are those of the whole map (1002 x 668); the frames' are map units x zoom.
|
|
|
|
local W = ForeverUI.WorldMap
|
|
local Z = { z = 1, cx = 0.5, cy = 0.5, active = false, STEP = 1.25, MAX = 4, THRESHOLD = 4 }
|
|
W.zoom = Z
|
|
|
|
local CARD_W, CARD_H = 1002, 668
|
|
local TILE, COLUMNS, ROWS = 256, 4, 3
|
|
|
|
local function tile(k)
|
|
return _G["WorldMapDetailTile" .. k]
|
|
end
|
|
|
|
local function ready()
|
|
return W.built and W.canvas and WorldMapDetailFrame and WorldMapButton and WORLDMAP_SETTINGS
|
|
end
|
|
|
|
-- Screen pixels per map unit
|
|
local function pixelsPerUnit()
|
|
return WorldMapDetailFrame:GetEffectiveScale() * Z.z
|
|
end
|
|
|
|
-- The view: the canvas rectangle in map units, origin at the map's top left
|
|
local function viewSize()
|
|
local c, d = W.canvas, WorldMapDetailFrame
|
|
local k = c:GetEffectiveScale() / d:GetEffectiveScale() / Z.z
|
|
return c:GetWidth() * k, c:GetHeight() * k
|
|
end
|
|
|
|
local function view()
|
|
local vw, vh = viewSize()
|
|
local x, y = Z.cx * CARD_W, Z.cy * CARD_H
|
|
return x - vw / 2, y - vh / 2, x + vw / 2, y + vh / 2
|
|
end
|
|
Z.view = view
|
|
|
|
-- Keep the center where the view stays inside the map
|
|
local function clampView()
|
|
local vw, vh = viewSize()
|
|
local halfX, halfY = vw / 2 / CARD_W, vh / 2 / CARD_H
|
|
if halfX >= 0.5 then Z.cx = 0.5 else Z.cx = math.max(halfX, math.min(1 - halfX, Z.cx)) end
|
|
if halfY >= 0.5 then Z.cy = 0.5 else Z.cy = math.max(halfY, math.min(1 - halfY, Z.cy)) end
|
|
end
|
|
|
|
-- ------------------------------------------------------------------ Art
|
|
|
|
-- 3.3.5 clips nothing outside a ScrollFrame and client frames are never reparented, so each
|
|
-- piece of art is cropped to the view: its visible part, at its place x zoom, with matching
|
|
-- tex coords. x, y, w, h: the piece in map units; u0, u1, v0, v1: its tex coords.
|
|
local function cropRegion(t, x, y, w, h, u0, u1, v0, v1, x0, y0, x1, y1)
|
|
local d, z = WorldMapDetailFrame, Z.z
|
|
local ix0, iy0 = math.max(x, x0), math.max(y, y0)
|
|
local ix1 = math.min(x + w, CARD_W, x1)
|
|
local iy1 = math.min(y + h, CARD_H, y1)
|
|
t:ClearAllPoints()
|
|
if ix1 - ix0 < 0.01 or iy1 - iy0 < 0.01 then
|
|
t:SetPoint("TOPLEFT", d, "TOPLEFT", x * z, -y * z)
|
|
t:SetWidth(1)
|
|
t:SetHeight(1)
|
|
t:SetAlpha(0)
|
|
else
|
|
local du, dv = (u1 - u0) / w, (v1 - v0) / h
|
|
t:SetPoint("TOPLEFT", d, "TOPLEFT", ix0 * z, -iy0 * z)
|
|
t:SetWidth((ix1 - ix0) * z)
|
|
t:SetHeight((iy1 - iy0) * z)
|
|
t:SetTexCoord(u0 + (ix0 - x) * du, u0 + (ix1 - x) * du, v0 + (iy0 - y) * dv, v0 + (iy1 - y) * dv)
|
|
t:SetAlpha(1)
|
|
end
|
|
end
|
|
|
|
-- Explored areas (WorldMapOverlay<n>, laid out by WorldMapFrame_Update in map units from the
|
|
-- map's top left): each piece's place, size and tex coords, read after the client sets them
|
|
local function overlay(i)
|
|
return _G["WorldMapOverlay" .. i]
|
|
end
|
|
|
|
local function recordOverlays()
|
|
for i = 1, NUM_WORLDMAP_OVERLAYS or 0 do
|
|
local o = overlay(i)
|
|
if o and o:IsShown() then
|
|
local _, _, _, x, y = o:GetPoint(1)
|
|
local ulx, uly, llx, lly, urx = o:GetTexCoord()
|
|
o.foreverZoomArt = { x or 0, -(y or 0), o:GetWidth(), o:GetHeight(),
|
|
ulx or 0, urx or 1, uly or 0, lly or 1 }
|
|
elseif o then
|
|
o.foreverZoomArt = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
local function restoreOverlays()
|
|
for i = 1, NUM_WORLDMAP_OVERLAYS or 0 do
|
|
local o = overlay(i)
|
|
local a = o and o.foreverZoomArt
|
|
if a then
|
|
o:ClearAllPoints()
|
|
o:SetPoint("TOPLEFT", WorldMapDetailFrame, "TOPLEFT", a[1], -a[2])
|
|
o:SetWidth(a[3])
|
|
o:SetHeight(a[4])
|
|
o:SetTexCoord(a[5], a[6], a[7], a[8])
|
|
o:SetAlpha(1)
|
|
o.foreverZoomArt = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
local function cropTexture()
|
|
local x0, y0, x1, y1 = view()
|
|
for rowLine = 1, ROWS do
|
|
for column = 1, COLUMNS do
|
|
local t = tile((rowLine - 1) * COLUMNS + column)
|
|
if t then
|
|
cropRegion(t, (column - 1) * TILE, (rowLine - 1) * TILE, TILE, TILE, 0, 1, 0, 1,
|
|
x0, y0, x1, y1)
|
|
end
|
|
end
|
|
end
|
|
for i = 1, NUM_WORLDMAP_OVERLAYS or 0 do
|
|
local o = overlay(i)
|
|
local a = o and o.foreverZoomArt
|
|
if a and o:IsShown() then
|
|
cropRegion(o, a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], x0, y0, x1, y1)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Tile anchors from WorldMapFrame.xml (541-636): the first at the corner, each right of the
|
|
-- previous one, each row under the first tile of the row above; then the small-window crop
|
|
-- (WorldMap.lua)
|
|
local function rechainTiles()
|
|
for k = 1, COLUMNS * ROWS do
|
|
local t = tile(k)
|
|
if t then
|
|
t:ClearAllPoints()
|
|
t:SetAlpha(1)
|
|
if k == 1 then
|
|
t:SetPoint("TOPLEFT", WorldMapDetailFrame, "TOPLEFT", 0, 0)
|
|
elseif (k - 1) % COLUMNS == 0 then
|
|
t:SetPoint("TOPLEFT", tile(k - COLUMNS), "BOTTOMLEFT", 0, 0)
|
|
else
|
|
t:SetPoint("TOPLEFT", tile(k - 1), "TOPRIGHT", 0, 0)
|
|
end
|
|
end
|
|
end
|
|
if W.cropTiles then W.cropTiles(true) end
|
|
end
|
|
|
|
-- --------------------------------------------------------------- Markers
|
|
|
|
-- A marker out of the view fades out and releases the mouse (invisible, it would still
|
|
-- steal hover and clicks from the quest log or the frame); back in view, it gets its alpha
|
|
-- and mouse back
|
|
local function turnOff(f)
|
|
f.foreverZoomAlpha = f:GetAlpha()
|
|
f.foreverZoomMouse = f.IsMouseEnabled and f:IsMouseEnabled() and true or false
|
|
f:SetAlpha(0)
|
|
if f.foreverZoomMouse then f:EnableMouse(false) end
|
|
end
|
|
|
|
local function restore(f)
|
|
f:SetAlpha(f.foreverZoomAlpha)
|
|
if f.foreverZoomMouse then f:EnableMouse(true) end
|
|
f.foreverZoomAlpha, f.foreverZoomMouse = nil, nil
|
|
end
|
|
|
|
-- Fade out the markers outside the view, restore those back inside
|
|
local function sortBy()
|
|
local c = W.canvas
|
|
local e = c:GetEffectiveScale()
|
|
local left, down, right, top = c:GetLeft(), c:GetBottom(), c:GetRight(), c:GetTop()
|
|
if not (left and down and right and top) then return end
|
|
left, down, right, top = left * e, down * e, right * e, top * e
|
|
for _, parent in ipairs({ WorldMapButton, WorldMapPOIFrame }) do
|
|
if parent then
|
|
for _, f in ipairs({ parent:GetChildren() }) do
|
|
if f ~= WorldMapFrameAreaFrame and f:IsShown() then
|
|
local x, y = f:GetCenter()
|
|
local fe = f:GetEffectiveScale()
|
|
local inside = not x or (x * fe >= left and x * fe <= right and y * fe >= down and y * fe <= top)
|
|
if inside then
|
|
if f.foreverZoomAlpha then restore(f) end
|
|
elseif not f.foreverZoomAlpha then
|
|
turnOff(f)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function restoreChildren()
|
|
for _, parent in ipairs({ WorldMapButton, WorldMapPOIFrame }) do
|
|
if parent then
|
|
for _, f in ipairs({ parent:GetChildren() }) do
|
|
if f.foreverZoomAlpha then restore(f) end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Quest POIs (children of WorldMapPOIFrame, 32 x 32 at its scale) keep on screen the size
|
|
-- they have in the small window: the full screen scales WorldMapFrame up to fit the map to the
|
|
-- screen, which doubled them. Their scale is the small window's over the current one.
|
|
local function markerScale(parent)
|
|
if parent ~= WorldMapPOIFrame or not W.reducedScale or not UIParent then return 1 end
|
|
local now = WorldMapPOIFrame:GetEffectiveScale() / UIParent:GetEffectiveScale()
|
|
if not now or now <= 0 then return 1 end
|
|
return W.reducedScale() / now
|
|
end
|
|
|
|
-- Markers anchored to the map's top left. Their offsets (from the client, in their parent's
|
|
-- units) are kept as a base at zoom 1, then written x zoom / their scale: landmarks and quest
|
|
-- POIs placed once follow the zoom, quest POIs keep their size. Markers the client places on
|
|
-- each frame (units) already follow the map's width. foreverZoomSet remembers what was
|
|
-- written here, to tell it from offsets the client wrote since, at zoom clientZoom.
|
|
local function placeMarkers(clientZoom)
|
|
local d, b, p = WorldMapDetailFrame, WorldMapButton, WorldMapPOIFrame
|
|
for _, parent in ipairs({ b, p, d }) do
|
|
if parent then
|
|
local m = markerScale(parent)
|
|
for _, f in ipairs({ parent:GetChildren() }) do
|
|
if f ~= b and f ~= p and f ~= WorldMapBlobFrame and f ~= WorldMapFrameAreaFrame
|
|
and f:GetNumPoints() == 1 then
|
|
local point, rel, relPoint, x, y = f:GetPoint(1)
|
|
if (rel == d or rel == b or rel == p) and relPoint == "TOPLEFT" and x and y then
|
|
local s = f.foreverZoomSet
|
|
local bx, by
|
|
if s and math.abs(s[1] - x) < 1e-3 and math.abs(s[2] - y) < 1e-3 then
|
|
bx, by = s[3], s[4]
|
|
else
|
|
bx, by = x / clientZoom, y / clientZoom
|
|
end
|
|
if parent == p and math.abs((f:GetScale() or 1) - m) > 1e-6 then
|
|
f:SetScale(m)
|
|
end
|
|
local nx, ny = bx * Z.z / m, by * Z.z / m
|
|
if math.abs(nx - x) > 1e-3 or math.abs(ny - y) > 1e-3 then
|
|
f:ClearAllPoints()
|
|
f:SetPoint(point, rel, relPoint, nx, ny)
|
|
end
|
|
f.foreverZoomSet = { nx, ny, bx, by }
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
Z.placeMarkers = placeMarkers
|
|
|
|
-- ------------------------------------------------------------- Apply
|
|
|
|
-- Current map key: a map change resets the zoom to 1
|
|
function Z.mapKey()
|
|
return tostring(GetCurrentMapAreaID and GetCurrentMapAreaID()) .. ":" ..
|
|
tostring(GetCurrentMapDungeonLevel and GetCurrentMapDungeonLevel())
|
|
end
|
|
|
|
-- The enlarged frames: WorldMapDetailFrame and WorldMapButton (siblings, same scale and corner)
|
|
local function enlarge()
|
|
for f, s in pairs(Z.sizes) do
|
|
f:SetWidth(s[1] * Z.z)
|
|
f:SetHeight(s[2] * Z.z)
|
|
end
|
|
end
|
|
|
|
-- The quest blob (WorldMapBlobFrame) is drawn by the engine in one piece and would overflow,
|
|
-- so it hides while zoomed; WorldMapButton's hit rect is limited to the view.
|
|
-- old: the zoom before this change (markers placed once follow it)
|
|
function Z.apply(old)
|
|
local d = WorldMapDetailFrame
|
|
if not Z.active then
|
|
Z.active = true
|
|
Z.points = {}
|
|
for i = 1, d:GetNumPoints() do
|
|
Z.points[i] = { d:GetPoint(i) }
|
|
end
|
|
Z.sizes = {
|
|
[d] = { d:GetWidth(), d:GetHeight() },
|
|
[WorldMapButton] = { WorldMapButton:GetWidth(), WorldMapButton:GetHeight() },
|
|
}
|
|
Z.key = Z.mapKey()
|
|
recordOverlays()
|
|
old = 1
|
|
end
|
|
old = old or Z.z
|
|
local z = Z.z
|
|
enlarge()
|
|
clampView()
|
|
d:ClearAllPoints()
|
|
d:SetPoint("CENTER", W.canvas, "CENTER", (0.5 - Z.cx) * CARD_W * z, (Z.cy - 0.5) * CARD_H * z)
|
|
if WorldMapBlobFrame then
|
|
WorldMapBlobFrame:SetAlpha(0)
|
|
if WorldMapBlobFrame_CalculateHitTranslations then WorldMapBlobFrame_CalculateHitTranslations() end
|
|
end
|
|
local a = WorldMapFrameAreaFrame
|
|
if a then
|
|
a:ClearAllPoints()
|
|
a:SetPoint("TOP", W.canvas, "TOP", 0, -10)
|
|
end
|
|
local x0, y0, x1, y1 = view()
|
|
WorldMapButton:SetHitRectInsets(math.max(0, x0) * z, math.max(0, CARD_W - x1) * z,
|
|
math.max(0, y0) * z, math.max(0, CARD_H - y1) * z)
|
|
cropTexture()
|
|
placeMarkers(old)
|
|
sortBy()
|
|
end
|
|
|
|
-- Back to the whole map. After a mode change, the client and WorldMap.lua have just set
|
|
-- their own anchors: the map's is kept then.
|
|
function Z.reset(isModeChange)
|
|
if not Z.active then
|
|
Z.z = 1
|
|
return
|
|
end
|
|
local old = Z.z
|
|
Z.z = 1
|
|
Z.active = false
|
|
enlarge()
|
|
local d = WorldMapDetailFrame
|
|
if not isModeChange and Z.points then
|
|
d:ClearAllPoints()
|
|
for _, p in ipairs(Z.points) do
|
|
d:SetPoint(p[1], p[2], p[3], p[4], p[5])
|
|
end
|
|
end
|
|
if WorldMapBlobFrame then
|
|
WorldMapBlobFrame:SetAlpha(1)
|
|
if WorldMapBlobFrame_CalculateHitTranslations then WorldMapBlobFrame_CalculateHitTranslations() end
|
|
end
|
|
local a = WorldMapFrameAreaFrame
|
|
if a then
|
|
a:ClearAllPoints()
|
|
a:SetPoint("TOP", WorldMapButton, "TOP", 0, -10)
|
|
end
|
|
WorldMapButton:SetHitRectInsets(0, 0, 0, 0)
|
|
WorldMapButton:EnableMouse(true)
|
|
Z.pressPoint, Z.dragging = nil, false
|
|
rechainTiles()
|
|
restoreOverlays()
|
|
placeMarkers(old)
|
|
restoreChildren()
|
|
Z.cx, Z.cy = 0.5, 0.5
|
|
end
|
|
|
|
-- ------------------------------------------------------------- Mouse wheel
|
|
|
|
-- Map point under the cursor, and the cursor offset from the canvas center in screen pixels
|
|
local function pointUnderCursor()
|
|
local x, y = GetCursorPosition()
|
|
local c = W.canvas
|
|
local cx, cy = c:GetCenter()
|
|
if not cx then return nil end
|
|
local ce = c:GetEffectiveScale()
|
|
local dx, dy = x - cx * ce, y - cy * ce
|
|
local de = pixelsPerUnit()
|
|
return Z.cx + dx / (CARD_W * de), Z.cy - dy / (CARD_H * de), dx, dy, de
|
|
end
|
|
|
|
function Z.wheel(direction)
|
|
if not ready() then return end
|
|
local old = Z.z
|
|
local new = direction > 0 and old * Z.STEP or old / Z.STEP
|
|
if new > Z.MAX then new = Z.MAX end
|
|
if new < 1.0001 then new = 1 end
|
|
if new == old then return end
|
|
if new == 1 then
|
|
Z.reset(false)
|
|
return
|
|
end
|
|
if not Z.active then Z.cx, Z.cy = 0.5, 0.5 end
|
|
-- Keep the point under the cursor in place (camelot: SetPanTarget)
|
|
local fx, fy, dx, dy, de = pointUnderCursor()
|
|
Z.z = new
|
|
if fx then
|
|
local de2 = de * new / old
|
|
Z.cx = fx - dx / (CARD_W * de2)
|
|
Z.cy = fy + dy / (CARD_H * de2)
|
|
end
|
|
Z.apply(old)
|
|
end
|
|
|
|
-- --------------------------------------------------------------- Watcher
|
|
|
|
local mapKey = Z.mapKey
|
|
|
|
-- Child of the map frame, so it only runs while the map is open
|
|
local watcher = CreateFrame("Frame", nil, WorldMapFrame)
|
|
watcher:SetScript("OnUpdate", function(_, elapsed)
|
|
-- Markers the client placed since the last pass (ten passes a second)
|
|
Z.markWait = (Z.markWait or 0) - elapsed
|
|
if Z.markWait <= 0 and ready() and not Z.dragging then
|
|
Z.markWait = 0.1
|
|
placeMarkers(Z.z)
|
|
end
|
|
if not (Z.active and ready()) then
|
|
Z.key = ready() and mapKey() or nil
|
|
return
|
|
end
|
|
if mapKey() ~= Z.key then
|
|
Z.reset(false)
|
|
Z.key = mapKey()
|
|
return
|
|
end
|
|
-- Drag: past THRESHOLD pixels the map follows the cursor. WorldMapButton_OnClick navigates
|
|
-- on mouse up, so WorldMapButton releases the mouse until the button is up. The client code
|
|
-- is not wrapped: it closes dropdowns and would taint their state if called from the addon.
|
|
local pressPoint = Z.pressPoint
|
|
if pressPoint then
|
|
local x, y = GetCursorPosition()
|
|
if not IsMouseButtonDown("LeftButton") then
|
|
Z.pressPoint = nil
|
|
if Z.dragging then
|
|
Z.dragging = false
|
|
WorldMapButton:EnableMouse(true)
|
|
end
|
|
else
|
|
if not Z.dragging and math.abs(x - pressPoint.x) + math.abs(y - pressPoint.y) > Z.THRESHOLD then
|
|
Z.dragging = true
|
|
WorldMapButton:EnableMouse(false)
|
|
end
|
|
if Z.dragging then
|
|
local de = pixelsPerUnit()
|
|
Z.cx = pressPoint.cx - (x - pressPoint.x) / (CARD_W * de)
|
|
Z.cy = pressPoint.cy + (y - pressPoint.y) / (CARD_H * de)
|
|
Z.apply()
|
|
return
|
|
end
|
|
end
|
|
end
|
|
-- Markers move with their units: ten passes a second are enough (Z.apply sorts at once).
|
|
-- Each pass reads every child of the map buttons (about sixty) and builds three tables.
|
|
Z.sortWait = (Z.sortWait or 0) - elapsed
|
|
if Z.sortWait <= 0 then
|
|
Z.sortWait = 0.1
|
|
sortBy()
|
|
end
|
|
end)
|
|
Z.watcher = watcher
|
|
|
|
-- ------------------------------------------------------------ Hooks
|
|
|
|
if WorldMapButton then
|
|
WorldMapButton:EnableMouseWheel(true)
|
|
WorldMapButton:SetScript("OnMouseWheel", function(_, direction)
|
|
Z.wheel(direction)
|
|
end)
|
|
WorldMapButton:HookScript("OnMouseDown", function(_, button)
|
|
if button == "LeftButton" and Z.active then
|
|
local x, y = GetCursorPosition()
|
|
Z.pressPoint = { x = x, y = y, cx = Z.cx, cy = Z.cy }
|
|
Z.dragging = false
|
|
end
|
|
end)
|
|
-- Player arrow: WorldMapButton_OnUpdate places it (position x the map's width x
|
|
-- WORLDMAP_SETTINGS.size); place it again, or hide it out of the view
|
|
WorldMapButton:HookScript("OnUpdate", function()
|
|
if not Z.active then return end
|
|
local x, y = GetPlayerMapPosition("player")
|
|
if not x or (x == 0 and y == 0) then return end
|
|
local px, py = x * CARD_W, y * CARD_H
|
|
local x0, y0, x1, y1 = view()
|
|
if px < x0 or px > x1 or py < y0 or py > y1 then
|
|
if ShowWorldMapArrowFrame then ShowWorldMapArrowFrame(nil) end
|
|
elseif PositionWorldMapArrowFrame then
|
|
local s = WORLDMAP_SETTINGS.size * Z.z
|
|
PositionWorldMapArrowFrame("CENTER", "WorldMapDetailFrame", "TOPLEFT", px * s, -py * s)
|
|
end
|
|
end)
|
|
end
|
|
|
|
if WorldMapFrame then
|
|
WorldMapFrame:HookScript("OnHide", function() Z.reset(false) end)
|
|
end
|
|
|
|
-- WorldMapFrame_Update reloads the tiles and lays out the explored areas again
|
|
-- (WORLD_MAP_UPDATE): crop them again while zoomed
|
|
if WorldMapFrame_Update then
|
|
hooksecurefunc("WorldMapFrame_Update", function()
|
|
if Z.active then
|
|
recordOverlays()
|
|
cropTexture()
|
|
end
|
|
if ready() then placeMarkers(Z.z) end
|
|
end)
|
|
end
|
|
|
|
-- The quest POIs, placed again on each quest update
|
|
if WorldMapFrame_DisplayQuests then
|
|
hooksecurefunc("WorldMapFrame_DisplayQuests", function()
|
|
if ready() then placeMarkers(Z.z) end
|
|
end)
|
|
end
|
|
|
|
-- Mode or view change: back to 1, without undoing what the client has just set
|
|
for _, name in ipairs({ "WorldMap_ToggleSizeDown", "WorldMap_ToggleSizeUp", "WorldMapFrame_SetMiniMode",
|
|
"WorldMapFrame_SetQuestMapView", "WorldMapFrame_SetFullMapView" }) do
|
|
if _G[name] then
|
|
hooksecurefunc(name, function()
|
|
Z.reset(true)
|
|
if ready() then placeMarkers(Z.z) end
|
|
end)
|
|
end
|
|
end
|