ca2b78e217
/fui keeps edit mode, reset and list. The thirty diagnostic and tuning functions behind the other subcommands (bar, bags, character, tabs, close, model, minimap, mouse, map, questlog, chat, chatlines, tabard...) go with them, with Panes.Report, the bags read-back and the 205 texts they alone used. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
767 lines
25 KiB
Lua
767 lines
25 KiB
Lua
-- Minimap in camelot style. Since 1.60.1.70450 camelot has its own minimap code (camelot/
|
|
-- Minimap.lua, MinimapZoneTextButton, MiniMapTrackingButton, MiniMapMailFrameTemplate,
|
|
-- GameTimeFrame, Diel.lua; Skin.lua is gone): a one-image zone bar, 23 px calendar and tracking
|
|
-- buttons, the mail under the bar. camelot drops the instance difficulty; ForeverUI keeps it
|
|
-- (user's choice), under the right end of the bar.
|
|
-- The client's Minimap frame is kept (only the client draws terrain, blips and the player
|
|
-- arrow): it is resized, masked and scaled, and everything around it is replaced.
|
|
-- Sizes and anchors: mainline Minimap.xml, GameTime.xml and Blizzard_TimeManager.xml.
|
|
|
|
ForeverUI = ForeverUI or {}
|
|
|
|
-- Cluster and container. Frame 253 x 253 is the c60 atlas size (camelot Skin.lua); the map
|
|
-- keeps 198. 3.3.5 SetMaskTexture takes a file path, not an atlas name.
|
|
local CLUSTER_W, CLUSTER_H = 256, 256
|
|
local MOUSE_MARGINS = { 30, 10, 0, 30 } -- left, right, top, bottom
|
|
|
|
local ATLAS_FRAME = "ui-hud-minimap-frame-c60"
|
|
local FRAME_W, FRAME_H = 253, 253
|
|
local MASK_PATH = "interface\\ForeverUI\\hud\\uiminimapmaskgeneralc60"
|
|
|
|
local CONTAINER_X, CONTAINER_Y = 10, -30
|
|
local MAP_SIDE = 198
|
|
|
|
-- 3.3.5 draws out-of-range arrows (corpse, quest, POI) at a fixed radius in map units, sized
|
|
-- for its 140 px map, and no method changes it. So the map keeps size 140 and gets scale
|
|
-- 198 / 140: it still shows 198 wide on screen, and the arrows reach its edge.
|
|
local CLIENT_MAP = 140
|
|
local MAP_SCALE = MAP_SIDE / CLIENT_MAP
|
|
|
|
-- Arrow images shrunk by 140 / 198 (tools/shrink_arrows.py) to undo the scale. The party arrow
|
|
-- has no setter: its shrunk file replaces the original in patch-Z.
|
|
local ARROWS = {
|
|
path = "interface\\ForeverUI\\minimap\\",
|
|
{ method = "SetStaticPOIArrowTexture", file = "rotating-minimaparrow" },
|
|
{ method = "SetPOIArrowTexture", file = "rotating-minimapguidearrow" },
|
|
{ method = "SetCorpsePOIArrowTexture", file = "rotating-minimapcorpsearrow" },
|
|
}
|
|
|
|
-- Zone name bar (camelot MinimapZoneTextButtonTemplate): 152 x 23 at TOP (10, 0) of the cluster,
|
|
-- its own background ui-hud-minimap-header-button; text GameFontHighlightSmall 110 x 12 at
|
|
-- LEFT (8, -1). Hover and press: a copy of the same image in ADD at alpha 0.1 / 0.2
|
|
-- (mouseoverAddAlpha, mousedownAddAlpha), for the bar, the calendar and the tracking button.
|
|
local BAR_ATLAS = "ui-hud-minimap-header-button"
|
|
local BAR_W, BAR_H = 152, 23
|
|
local BAR_X, BAR_Y = 10, 0
|
|
local TEXT_W, TEXT_H = 110, 12
|
|
local TEXT_X, TEXT_Y = 8, -1
|
|
local HOVER_ALPHA, PRESS_ALPHA = 0.1, 0.2
|
|
|
|
-- Tracking (camelot MiniMapTrackingButtonTemplate): 17 x 17 frame RIGHT on the bar's LEFT, its
|
|
-- button 23 x 23 centered. Mail (MiniMapMailFrameTemplate): 32 x 24, TOPLEFT on the bar's
|
|
-- BOTTOMLEFT, a still icon.
|
|
local TRACKING_SIDE = 17
|
|
local TRACKING_X = 0
|
|
local TRACKING_BUTTON = 23
|
|
local MAIL_W, MAIL_H = 32, 24
|
|
|
|
-- Zoom (buttons hidden unless hovered)
|
|
local ZOOM_ZONE = 40
|
|
local ZOOM_ZONE_X, ZOOM_ZONE_Y = 77, -77
|
|
local ZOOM_PLUS_W, ZOOM_PLUS_H = 17, 17
|
|
local ZOOM_PLUS_X, ZOOM_PLUS_Y = 88, -68
|
|
local ZOOM_MINUS_W, ZOOM_MINUS_H = 17, 9
|
|
local ZOOM_MINUS_X, ZOOM_MINUS_Y = 72, -84
|
|
|
|
-- Day/night ring (camelot Diel.lua, 70450: parent MinimapContainer, CENTER (55, 86)). 3.3.5 has
|
|
-- no DIEL_CYCLE_CHANGED or IsDayTime, only the server hour: day is 6 h to 18 h.
|
|
local ATLAS_CYCLE = "ui-hud-minimap-frame-cycle-c60"
|
|
local ATLAS_DAY = "ui-hud-minimap-daycycle-c60"
|
|
local ATLAS_NIGHT = "ui-hud-minimap-nightcycle-c60"
|
|
local CYCLE_SIDE = 42
|
|
local ORB_SIDE = 33
|
|
local CYCLE_X, CYCLE_Y = 55, 86
|
|
local CYCLE_LEVEL = 5
|
|
local DAWN, DUSK = 6, 18
|
|
local CYCLE_PERIOD = 60 -- re-read the server hour once a minute
|
|
|
|
-- Instance difficulty: kept (camelot removes it), under the right end of the bar, where camelot
|
|
-- has its addon compartment (absent here)
|
|
local DIFFICULTY_X, DIFFICULTY_Y = 0, 2
|
|
|
|
-- One table per element rather than one local per value: Lua 5.1 allows at most 60 upvalues
|
|
-- per function, and build() exceeded it.
|
|
|
|
-- Calendar (camelot GameTimeFrameTemplate): 23 x 23, TOPLEFT on the bar's TOPRIGHT (-4, 0), one
|
|
-- fixed image: camelot no longer shows the day
|
|
local CALENDAR = { L = 23, H = 23, X = -4, ATLAS = "ui-hud-minimap-calendar-button" }
|
|
|
|
-- Clock, in the bar
|
|
-- camelot 70450 (Blizzard_TimeManagerOverrides): RIGHT on the bar's RIGHT (-3, -1), ticker
|
|
-- CENTER (3, 0); at the alarm the ticker flashes instead of the glow.
|
|
local CLOCK = {
|
|
L = 40, H = 16, X = -3, Y = -1,
|
|
MARGINS = { 8, 5, 3, 3 },
|
|
TEXT_X = 3, TEXT_Y = 0,
|
|
-- WhiteNormalNumberFont (GameFontStyles.xml): FRIZQT__ 10, white, black shadow (1, -1).
|
|
-- 3.3.5 lacks this font object, so its settings are applied one by one.
|
|
FONT = "Fonts\\FRIZQT__.TTF", SIZE = 10,
|
|
}
|
|
|
|
-- Player coordinates under the map (camelot MinimapPlayerCoordsMixin), every 0.1 s.
|
|
-- Formats from camelot GlobalStrings: 3.3.5 does not have them.
|
|
local COORD = {
|
|
L = 90, H = 10, Y = -18,
|
|
INTERVAL = 0.1,
|
|
INTEGER = "%d, %d", -- MINIMAP_PLAYER_COORDS_INTEGER
|
|
TENTHS = "%.1f, %.1f", -- MINIMAP_PLAYER_COORDS
|
|
}
|
|
|
|
-- 3.3.5 buttons camelot places elsewhere, set on the ring at our own angles: their 3.3.5
|
|
-- offsets fit a 140 map in a 192 cluster. Radius 100 is the middle of the ring's metal
|
|
-- (hole 185, metal 15 on each side).
|
|
local RING_RADIUS = 100
|
|
local RING_ITEMS = {
|
|
{ name = "MiniMapWorldMapButton", angle = 180 },
|
|
{ name = "MiniMapLFGFrame", angle = 215 },
|
|
{ name = "MiniMapBattlefieldFrame", angle = 250 },
|
|
{ name = "MiniMapRecordingButton", angle = 285 },
|
|
}
|
|
|
|
local PREFIX = "|cff66ccffForeverUI|r "
|
|
local L = ForeverUI.L
|
|
|
|
local function say(message)
|
|
DEFAULT_CHAT_FRAME:AddMessage(PREFIX .. message)
|
|
end
|
|
|
|
-- Hides a client region for good. Its own code shows it again or restores its file
|
|
-- (Minimap_UpdateRotationSetting, MiniMapTracking_Update), so texture, alpha and visibility
|
|
-- are all cleared.
|
|
local function suppress(region)
|
|
if not region then
|
|
return
|
|
end
|
|
if region.SetTexture then
|
|
region:SetTexture(nil)
|
|
end
|
|
if region.SetAlpha then
|
|
region:SetAlpha(0)
|
|
end
|
|
if region.Hide then
|
|
region:Hide()
|
|
end
|
|
end
|
|
|
|
-- Puts an atlas on a button state. A 3.3.5 button has only the states its XML declares
|
|
-- (MiniMapTrackingButton has only a highlight), and Set<State>Texture takes only a file path,
|
|
-- so the sheet is set first, then the atlas rectangle on the texture it creates.
|
|
-- state: "Normal", "Pushed", "Highlight" or "Disabled"; name: atlas name
|
|
local function applyButtonState(button, state, name)
|
|
local e = ForeverUI.AtlasEntry(name)
|
|
if not e then
|
|
return nil
|
|
end
|
|
local texture = button["Get" .. state .. "Texture"](button)
|
|
if not texture then
|
|
button["Set" .. state .. "Texture"](button, e[1])
|
|
texture = button["Get" .. state .. "Texture"](button)
|
|
end
|
|
if texture then
|
|
ForeverUI.SetAtlas(texture, name, true)
|
|
end
|
|
return texture
|
|
end
|
|
|
|
-- Places a frame on the ring at angle degrees: 0 is east, counterclockwise.
|
|
local function placeOnRing(frame, map, angle)
|
|
local radians = angle * math.pi / 180
|
|
frame:ClearAllPoints()
|
|
frame:SetPoint("CENTER", map, "CENTER",
|
|
RING_RADIUS * math.cos(radians), RING_RADIUS * math.sin(radians))
|
|
end
|
|
|
|
local M = {}
|
|
ForeverUI.Minimap = M
|
|
|
|
-- camelot's hover and press (OnButtonStateChanged): a copy of the button's image in ADD, alpha
|
|
-- 0.1 on hover and 0.2 pressed, hidden otherwise. target: the frame that gets the mouse; host:
|
|
-- the frame that holds the copy; atlas: the image.
|
|
local function addGlow(target, host, atlas)
|
|
if target.foreverGlow then
|
|
return target.foreverGlow
|
|
end
|
|
local glow = host:CreateTexture(nil, "OVERLAY")
|
|
ForeverUI.SetAtlas(glow, atlas, true)
|
|
glow:SetAllPoints(host)
|
|
glow:SetBlendMode("ADD")
|
|
glow:Hide()
|
|
target.foreverGlow = glow
|
|
local function paint()
|
|
if target.foreverPressed then
|
|
glow:SetAlpha(PRESS_ALPHA)
|
|
glow:Show()
|
|
elseif target.foreverHovered then
|
|
glow:SetAlpha(HOVER_ALPHA)
|
|
glow:Show()
|
|
else
|
|
glow:Hide()
|
|
end
|
|
end
|
|
target:HookScript("OnEnter", function(self) self.foreverHovered = true paint() end)
|
|
target:HookScript("OnLeave", function(self) self.foreverHovered, self.foreverPressed = nil, nil paint() end)
|
|
target:HookScript("OnMouseDown", function(self) self.foreverPressed = true paint() end)
|
|
target:HookScript("OnMouseUp", function(self) self.foreverPressed = nil paint() end)
|
|
return glow
|
|
end
|
|
|
|
-- Calendar: one fixed image (camelot OnButtonStateChanged ignores the day). The 3.3.5
|
|
-- GameTimeFrame_SetDate still writes the day as text: hidden.
|
|
function M.updateCalendar()
|
|
local button = _G["GameTimeFrame"]
|
|
if not button then
|
|
return
|
|
end
|
|
applyButtonState(button, "Normal", CALENDAR.ATLAS)
|
|
applyButtonState(button, "Pushed", CALENDAR.ATLAS)
|
|
-- the client highlight gives way to the ADD copy (addGlow)
|
|
local highlight = button.GetHighlightTexture and button:GetHighlightTexture()
|
|
if highlight then
|
|
highlight:SetAlpha(0)
|
|
end
|
|
local text = button.GetFontString and button:GetFontString()
|
|
if text then
|
|
text:SetAlpha(0)
|
|
end
|
|
end
|
|
|
|
-- Clock: its 3.3.5 background is an unnamed texture (ClockBackground), found among the
|
|
-- regions; the text and the alarm glow are kept.
|
|
function M.skinClock()
|
|
local clock = _G["TimeManagerClockButton"]
|
|
local bar = M.bar
|
|
if not clock or not bar then
|
|
return
|
|
end
|
|
local text = _G["TimeManagerClockTicker"]
|
|
local alarm = _G["TimeManagerAlarmFiredTexture"]
|
|
for _, region in ipairs({ clock:GetRegions() }) do
|
|
if region ~= text and region ~= alarm then
|
|
suppress(region)
|
|
end
|
|
end
|
|
clock:SetParent(M.cluster)
|
|
clock:SetWidth(CLOCK.L)
|
|
clock:SetHeight(CLOCK.H)
|
|
clock:ClearAllPoints()
|
|
clock:SetPoint("RIGHT", bar, "RIGHT", CLOCK.X, CLOCK.Y)
|
|
-- above the zone button, which covers the bar
|
|
clock:SetFrameLevel(bar:GetFrameLevel() + 2)
|
|
clock:SetHitRectInsets(CLOCK.MARGINS[1], CLOCK.MARGINS[2],
|
|
CLOCK.MARGINS[3], CLOCK.MARGINS[4])
|
|
if text then
|
|
text:SetFont(CLOCK.FONT, CLOCK.SIZE)
|
|
text:SetShadowOffset(1, -1)
|
|
text:SetShadowColor(0, 0, 0, 1)
|
|
text:SetTextColor(1, 1, 1)
|
|
text:ClearAllPoints()
|
|
text:SetPoint("CENTER", clock, "CENTER", CLOCK.TEXT_X, CLOCK.TEXT_Y)
|
|
end
|
|
-- camelot TimeManager_FireAlarm / _TurnOffAlarm: the glow stays hidden, the ticker flashes
|
|
if not M.alarmHooked and hooksecurefunc and TimeManager_FireAlarm and TimeManager_TurnOffAlarm then
|
|
hooksecurefunc("TimeManager_FireAlarm", function()
|
|
local glow, ticker = _G["TimeManagerAlarmFiredTexture"], _G["TimeManagerClockTicker"]
|
|
if glow then
|
|
if UIFrameFlashStop then UIFrameFlashStop(glow) end
|
|
glow:SetAlpha(1)
|
|
glow:Hide()
|
|
end
|
|
if ticker and UIFrameFlash then
|
|
UIFrameFlash(ticker, 0.5, 0.5, -1)
|
|
end
|
|
end)
|
|
hooksecurefunc("TimeManager_TurnOffAlarm", function()
|
|
local ticker = _G["TimeManagerClockTicker"]
|
|
if ticker then
|
|
if UIFrameFlashStop then UIFrameFlashStop(ticker) end
|
|
ticker:SetAlpha(1)
|
|
ticker:Show()
|
|
end
|
|
end)
|
|
M.alarmHooked = true
|
|
end
|
|
M.clockSkinned = true
|
|
end
|
|
|
|
-- 3.3.5 GetPlayerMapPosition answers for the map shown by the world map, so it is set back to
|
|
-- the player's zone on zone change and when the world map closes, never while it is open.
|
|
-- In instances 3.3.5 returns (0, 0) and nothing is shown.
|
|
function M.recenterWorldMap()
|
|
if SetMapToCurrentZone and not (WorldMapFrame and WorldMapFrame:IsShown()) then
|
|
SetMapToCurrentZone()
|
|
end
|
|
end
|
|
|
|
function M.updateCoordinates()
|
|
local coords = M.coords
|
|
if not coords then
|
|
return
|
|
end
|
|
local x, y = 0, 0
|
|
if GetPlayerMapPosition then
|
|
x, y = GetPlayerMapPosition("player")
|
|
end
|
|
if not x or not y or (x == 0 and y == 0) then
|
|
coords.text:SetText("")
|
|
return
|
|
end
|
|
if GetCVar and GetCVar("coordsByTenths") == "1" then
|
|
coords.text:SetText(string.format(COORD.TENTHS, x * 100, y * 100))
|
|
else
|
|
coords.text:SetText(string.format(COORD.INTEGER,
|
|
math.floor(x * 100 + 0.5), math.floor(y * 100 + 0.5)))
|
|
end
|
|
end
|
|
|
|
-- Applies scale k to the map: its size becomes 198 / k, so it stays 198 on screen. Children the
|
|
-- addon places (MinimapBackdrop, zoom hit area) get 1 / k to keep their size. MinimapPing
|
|
-- follows the map: Minimap_SetPing works in map units.
|
|
local function applyScale(k)
|
|
local map = M.map
|
|
if not map then
|
|
return
|
|
end
|
|
M.scale = k
|
|
map:SetScale(k)
|
|
map:SetWidth(MAP_SIDE / k)
|
|
map:SetHeight(MAP_SIDE / k)
|
|
for _, childFrame in ipairs({ _G["MinimapBackdrop"], M.zoomZone }) do
|
|
if childFrame then
|
|
childFrame:SetScale(1 / k)
|
|
end
|
|
end
|
|
end
|
|
|
|
M.applyScale = applyScale
|
|
|
|
-- Reskins the cluster; returns false when client frames or AtlasUtil are missing.
|
|
local function build()
|
|
local cluster = _G["MinimapCluster"]
|
|
local map = _G["Minimap"]
|
|
local background = _G["MinimapBackdrop"]
|
|
|
|
if not cluster or not map or not background then
|
|
say(L.MINIMAP_DEBUG_NO_CLIENT_FRAMES)
|
|
return false
|
|
end
|
|
|
|
if not ForeverUI.SetAtlas then
|
|
say(L.MINIMAP_DEBUG_NO_ATLASUTIL)
|
|
return false
|
|
end
|
|
|
|
M.cluster = cluster
|
|
M.map = map
|
|
|
|
-- 1. Cluster: same mouse margins as 3.3.5, new size.
|
|
cluster:SetWidth(CLUSTER_W)
|
|
cluster:SetHeight(CLUSTER_H)
|
|
cluster:SetFrameStrata("LOW")
|
|
cluster:SetHitRectInsets(MOUSE_MARGINS[1], MOUSE_MARGINS[2],
|
|
MOUSE_MARGINS[3], MOUSE_MARGINS[4])
|
|
|
|
suppress(_G["MinimapBorderTop"])
|
|
|
|
-- 2. Container and map.
|
|
local container = M.container
|
|
if not container then
|
|
container = CreateFrame("Frame", "ForeverUIMinimapContainer", cluster)
|
|
M.container = container
|
|
end
|
|
container:SetWidth(FRAME_W)
|
|
container:SetHeight(FRAME_H)
|
|
container:ClearAllPoints()
|
|
container:SetPoint("TOP", cluster, "TOP", CONTAINER_X, CONTAINER_Y)
|
|
|
|
map:SetParent(container)
|
|
map:ClearAllPoints()
|
|
map:SetPoint("CENTER", container, "CENTER", 0, 0)
|
|
map:SetMaskTexture(MASK_PATH)
|
|
for _, arrow in ipairs(ARROWS) do
|
|
if map[arrow.method] then
|
|
map[arrow.method](map, ARROWS.path .. arrow.file)
|
|
end
|
|
end
|
|
|
|
-- 3. Frame ring on MinimapBackdrop, as in camelot: a child of the map, it draws above the
|
|
-- terrain, and its own children (the buttons) draw above it.
|
|
-- MinimapCompassTexture is the 3.3.5 compass here, not camelot's frame of the same name.
|
|
background:SetWidth(FRAME_W)
|
|
background:SetHeight(FRAME_H)
|
|
background:ClearAllPoints()
|
|
background:SetPoint("CENTER", map, "CENTER", 0, 0)
|
|
|
|
suppress(_G["MinimapBorder"])
|
|
suppress(_G["MinimapNorthTag"])
|
|
suppress(_G["MinimapCompassTexture"])
|
|
|
|
local ring = M.ring
|
|
if not ring then
|
|
ring = background:CreateTexture(nil, "ARTWORK")
|
|
M.ring = ring
|
|
end
|
|
ForeverUI.SetAtlas(ring, ATLAS_FRAME, true)
|
|
ring:SetWidth(FRAME_W)
|
|
ring:SetHeight(FRAME_H)
|
|
ring:ClearAllPoints()
|
|
ring:SetPoint("CENTER", background, "CENTER", 0, 0)
|
|
|
|
-- 4. Zone name bar.
|
|
local bar = M.bar
|
|
if not bar then
|
|
bar = CreateFrame("Frame", "ForeverUIMinimapBorderTop", cluster)
|
|
M.bar = bar
|
|
bar.background = bar:CreateTexture(nil, "BACKGROUND")
|
|
ForeverUI.SetAtlas(bar.background, BAR_ATLAS, true)
|
|
bar.background:SetAllPoints(bar)
|
|
end
|
|
bar:SetWidth(BAR_W)
|
|
bar:SetHeight(BAR_H)
|
|
bar:ClearAllPoints()
|
|
bar:SetPoint("TOP", cluster, "TOP", BAR_X, BAR_Y)
|
|
|
|
local zoneButton = _G["MinimapZoneTextButton"]
|
|
if zoneButton then
|
|
zoneButton:SetParent(cluster)
|
|
-- the button is the bar (camelot: the bar's image is the button's StateTexture)
|
|
zoneButton:ClearAllPoints()
|
|
zoneButton:SetAllPoints(bar)
|
|
zoneButton:SetFrameLevel(bar:GetFrameLevel() + 1)
|
|
addGlow(zoneButton, bar, BAR_ATLAS)
|
|
end
|
|
|
|
local zoneText = _G["MinimapZoneText"]
|
|
if zoneText then
|
|
-- GameFontHighlightSmall; the client keeps setting the color (Minimap_Update, PvP status)
|
|
local r, g, b = zoneText:GetTextColor()
|
|
zoneText:SetFontObject(GameFontHighlightSmall)
|
|
if r then
|
|
zoneText:SetTextColor(r, g, b)
|
|
end
|
|
zoneText:SetWidth(TEXT_W)
|
|
zoneText:SetHeight(TEXT_H)
|
|
zoneText:ClearAllPoints()
|
|
zoneText:SetPoint("LEFT", zoneButton or bar, "LEFT", TEXT_X, TEXT_Y)
|
|
zoneText:SetDrawLayer("OVERLAY")
|
|
zoneText:SetJustifyH("LEFT")
|
|
zoneText:SetJustifyV("MIDDLE")
|
|
end
|
|
|
|
-- 5. Tracking: the client frame stays (it opens the tracking menu), only reskinned.
|
|
local tracking = _G["MiniMapTracking"]
|
|
if tracking then
|
|
tracking:SetParent(cluster)
|
|
tracking:SetWidth(TRACKING_SIDE)
|
|
tracking:SetHeight(TRACKING_SIDE)
|
|
tracking:ClearAllPoints()
|
|
tracking:SetPoint("RIGHT", bar, "LEFT", TRACKING_X, 0)
|
|
tracking:SetFrameLevel(bar:GetFrameLevel() + 1)
|
|
|
|
suppress(_G["MiniMapTrackingBackground"])
|
|
suppress(_G["MiniMapTrackingIcon"])
|
|
suppress(_G["MiniMapTrackingIconOverlay"])
|
|
|
|
local button = _G["MiniMapTrackingButton"]
|
|
if button then
|
|
button:SetWidth(TRACKING_BUTTON)
|
|
button:SetHeight(TRACKING_BUTTON)
|
|
button:ClearAllPoints()
|
|
button:SetPoint("CENTER", tracking, "CENTER", 0, 0)
|
|
button:SetHitRectInsets(0, 0, 0, 0)
|
|
|
|
suppress(_G["MiniMapTrackingButtonBorder"])
|
|
suppress(_G["MiniMapTrackingButtonShine"])
|
|
|
|
applyButtonState(button, "Normal", "ui-hud-minimap-tracking-button")
|
|
applyButtonState(button, "Pushed", "ui-hud-minimap-tracking-button")
|
|
local highlight = button.GetHighlightTexture and button:GetHighlightTexture()
|
|
if highlight then
|
|
highlight:SetAlpha(0)
|
|
end
|
|
addGlow(button, button, "ui-hud-minimap-tracking-button")
|
|
end
|
|
end
|
|
|
|
-- 6. Mail, under the bar's left end.
|
|
local rowLine = M.rowLine
|
|
if not rowLine then
|
|
rowLine = CreateFrame("Frame", "ForeverUIMinimapIndicators", cluster)
|
|
M.rowLine = rowLine
|
|
end
|
|
rowLine:SetWidth(MAIL_W)
|
|
rowLine:SetHeight(MAIL_H)
|
|
rowLine:ClearAllPoints()
|
|
rowLine:SetPoint("TOPLEFT", bar, "BOTTOMLEFT", 0, 0)
|
|
|
|
local mail = _G["MiniMapMailFrame"]
|
|
if mail then
|
|
mail:SetParent(rowLine)
|
|
mail:SetWidth(MAIL_W)
|
|
mail:SetHeight(MAIL_H)
|
|
mail:ClearAllPoints()
|
|
mail:SetPoint("TOPLEFT", rowLine, "TOPLEFT", 0, 0)
|
|
|
|
suppress(_G["MiniMapMailIcon"])
|
|
suppress(_G["MiniMapMailBorder"])
|
|
|
|
if not M.mailIcon then
|
|
M.mailIcon = mail:CreateTexture(nil, "ARTWORK")
|
|
end
|
|
ForeverUI.SetAtlas(M.mailIcon, "ui-hud-minimap-mail-icon")
|
|
M.mailIcon:ClearAllPoints()
|
|
M.mailIcon:SetPoint("TOPLEFT", mail, "TOPLEFT", 0, 0)
|
|
end
|
|
|
|
-- 7. Zoom. The hit area uses the BACKGROUND strata, as in camelot, so it takes the mouse only
|
|
-- where nothing else does and does not steal map clicks.
|
|
local zoomZone = M.zoomZone
|
|
if not zoomZone then
|
|
zoomZone = CreateFrame("Frame", "ForeverUIMinimapZoomHitArea", map)
|
|
zoomZone:EnableMouse(true)
|
|
zoomZone:SetFrameStrata("BACKGROUND")
|
|
M.zoomZone = zoomZone
|
|
end
|
|
zoomZone:SetWidth(ZOOM_ZONE)
|
|
zoomZone:SetHeight(ZOOM_ZONE)
|
|
zoomZone:ClearAllPoints()
|
|
zoomZone:SetPoint("CENTER", map, "CENTER", ZOOM_ZONE_X, ZOOM_ZONE_Y)
|
|
|
|
local plus = _G["MinimapZoomIn"]
|
|
local minus = _G["MinimapZoomOut"]
|
|
|
|
local function skinZoom(button, width, height, x, y, name)
|
|
if not button then
|
|
return
|
|
end
|
|
button:SetWidth(width)
|
|
button:SetHeight(height)
|
|
button:ClearAllPoints()
|
|
button:SetPoint("CENTER", map, "CENTER", x, y)
|
|
-- Client hit insets cut 4 px on each side of a 32 px button; a 17 px one needs none.
|
|
button:SetHitRectInsets(0, 0, 0, 0)
|
|
applyButtonState(button, "Normal", name)
|
|
applyButtonState(button, "Pushed", name .. "-down")
|
|
|
|
-- camelot desaturates the disabled state. 3.3.5 SetDesaturated returns false when the GPU
|
|
-- cannot do it: then darken by hand.
|
|
local disabled = applyButtonState(button, "Disabled", name)
|
|
if disabled and not (disabled.SetDesaturated and disabled:SetDesaturated(true)) then
|
|
disabled:SetVertexColor(0.45, 0.45, 0.45)
|
|
end
|
|
|
|
-- The client highlight is additive; camelot's is a full, lighter image, drawn with BLEND.
|
|
local hover = applyButtonState(button, "Highlight", name .. "-mouseover")
|
|
if hover then
|
|
hover:SetBlendMode("BLEND")
|
|
end
|
|
button:Hide()
|
|
end
|
|
|
|
skinZoom(plus, ZOOM_PLUS_W, ZOOM_PLUS_H, ZOOM_PLUS_X, ZOOM_PLUS_Y,
|
|
"ui-hud-minimap-zoom-in")
|
|
skinZoom(minus, ZOOM_MINUS_W, ZOOM_MINUS_H, ZOOM_MINUS_X, ZOOM_MINUS_Y,
|
|
"ui-hud-minimap-zoom-out")
|
|
|
|
-- As MinimapMixin:OnLeave: hide the zoom buttons only when the mouse is on none of the four.
|
|
local function showRegion()
|
|
if plus then plus:Show() end
|
|
if minus then minus:Show() end
|
|
end
|
|
|
|
local function hideIfPossible()
|
|
if plus and plus:IsMouseOver() then return end
|
|
if minus and minus:IsMouseOver() then return end
|
|
if zoomZone:IsMouseOver() then return end
|
|
if map:IsMouseOver() then return end
|
|
if plus then plus:Hide() end
|
|
if minus then minus:Hide() end
|
|
end
|
|
|
|
|
|
if not M.zoomHooked then
|
|
map:HookScript("OnEnter", showRegion)
|
|
map:HookScript("OnLeave", hideIfPossible)
|
|
zoomZone:SetScript("OnEnter", showRegion)
|
|
zoomZone:SetScript("OnLeave", hideIfPossible)
|
|
if plus then
|
|
plus:HookScript("OnEnter", showRegion)
|
|
plus:HookScript("OnLeave", hideIfPossible)
|
|
end
|
|
if minus then
|
|
minus:HookScript("OnEnter", showRegion)
|
|
minus:HookScript("OnLeave", hideIfPossible)
|
|
end
|
|
-- Mouse wheel as camelot MinimapMixin:OnMouseWheel: clicks + or - (same effect and sound,
|
|
-- nothing when disabled). 3.3.5 does not give the wheel to the map.
|
|
-- IsEnabled returns 0 or 1, and 0 is true in Lua.
|
|
local function click(button)
|
|
local state = button and button:IsEnabled()
|
|
if state and state ~= 0 then button:Click() end
|
|
end
|
|
map:EnableMouseWheel(true)
|
|
map:SetScript("OnMouseWheel", function(_, direction)
|
|
if direction > 0 then
|
|
click(plus)
|
|
elseif direction < 0 then
|
|
click(minus)
|
|
end
|
|
end)
|
|
M.zoomHooked = true
|
|
end
|
|
|
|
-- 8. Day/night ring.
|
|
local cycle = M.cycle
|
|
if not cycle then
|
|
cycle = CreateFrame("Frame", "ForeverUIMinimapDiel", container)
|
|
cycle:SetFrameLevel(CYCLE_LEVEL)
|
|
M.cycle = cycle
|
|
M.orb = cycle:CreateTexture(nil, "BACKGROUND")
|
|
M.orb:SetPoint("CENTER", cycle, "CENTER", 0, 0)
|
|
M.cycleEdge = cycle:CreateTexture(nil, "OVERLAY")
|
|
M.cycleEdge:SetAllPoints(cycle)
|
|
end
|
|
cycle:SetWidth(CYCLE_SIDE)
|
|
cycle:SetHeight(CYCLE_SIDE)
|
|
cycle:ClearAllPoints()
|
|
cycle:SetPoint("CENTER", container, "CENTER", CYCLE_X, CYCLE_Y)
|
|
ForeverUI.SetAtlas(M.cycleEdge, ATLAS_CYCLE, true)
|
|
M.orb:SetWidth(ORB_SIDE)
|
|
M.orb:SetHeight(ORB_SIDE)
|
|
|
|
-- 9. Instance difficulty (camelot removes it; kept), under the bar's right end.
|
|
local difficulty = _G["MiniMapInstanceDifficulty"]
|
|
if difficulty then
|
|
difficulty:SetParent(cluster)
|
|
difficulty:ClearAllPoints()
|
|
difficulty:SetPoint("TOPRIGHT", bar, "BOTTOMRIGHT", DIFFICULTY_X, DIFFICULTY_Y)
|
|
end
|
|
|
|
-- 10. Calendar, right of the bar.
|
|
local calendar = _G["GameTimeFrame"]
|
|
if calendar then
|
|
calendar:SetParent(cluster)
|
|
calendar:SetWidth(CALENDAR.L)
|
|
calendar:SetHeight(CALENDAR.H)
|
|
calendar:ClearAllPoints()
|
|
calendar:SetPoint("TOPLEFT", bar, "TOPRIGHT", CALENDAR.X, 0)
|
|
calendar:SetFrameLevel(bar:GetFrameLevel() + 1)
|
|
-- 3.3.5 gives it hit insets of 6/0/5/10 for a 40 px button; camelot declares none.
|
|
calendar:SetHitRectInsets(0, 0, 0, 0)
|
|
M.updateCalendar()
|
|
addGlow(calendar, calendar, CALENDAR.ATLAS)
|
|
end
|
|
|
|
-- 11. Clock, in the bar. Blizzard_TimeManager loads on demand: ADDON_LOADED skins it later.
|
|
M.skinClock()
|
|
|
|
-- 12. Player coordinates under the map, a sibling of the map in the container
|
|
-- (camelot PlayerCoords).
|
|
local coords = M.coords
|
|
if not coords then
|
|
coords = CreateFrame("Frame", "ForeverUIMinimapPlayerCoords", container)
|
|
coords.text = coords:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
|
|
coords.text:SetAllPoints(coords)
|
|
coords.text:SetJustifyH("CENTER")
|
|
coords.pending = 0
|
|
coords:SetScript("OnUpdate", function(self, elapsed)
|
|
self.pending = self.pending - (elapsed or 0)
|
|
if self.pending > 0 then
|
|
return
|
|
end
|
|
self.pending = COORD.INTERVAL
|
|
M.updateCoordinates()
|
|
end)
|
|
M.coords = coords
|
|
end
|
|
coords:SetWidth(COORD.L)
|
|
coords:SetHeight(COORD.H)
|
|
coords:ClearAllPoints()
|
|
coords:SetPoint("BOTTOM", map, "BOTTOM", 0, COORD.Y)
|
|
|
|
-- 13. Scale last: the zoom hit area must exist.
|
|
applyScale(M.scale or MAP_SCALE)
|
|
|
|
-- 14. The buttons camelot does not have, on the ring.
|
|
for _, item in ipairs(RING_ITEMS) do
|
|
local button = _G[item.name]
|
|
if button then
|
|
button:SetParent(background)
|
|
placeOnRing(button, map, item.angle)
|
|
end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
-- The server hour picks sun or moon; 3.3.5 has no cycle event, so it is read once a minute.
|
|
local function updateCycle()
|
|
if not M.orb then
|
|
return
|
|
end
|
|
local hour = GetGameTime and GetGameTime() or 12
|
|
local day = hour >= DAWN and hour < DUSK
|
|
M.day = day
|
|
ForeverUI.SetAtlas(M.orb, day and ATLAS_DAY or ATLAS_NIGHT, true)
|
|
end
|
|
|
|
M.Refresh = function()
|
|
if M.cluster then
|
|
updateCycle()
|
|
end
|
|
end
|
|
|
|
local listener = CreateFrame("Frame")
|
|
listener.elapsed = 0
|
|
listener:RegisterEvent("PLAYER_ENTERING_WORLD")
|
|
listener:RegisterEvent("MINIMAP_UPDATE_ZOOM")
|
|
listener:RegisterEvent("ADDON_LOADED")
|
|
listener:RegisterEvent("ZONE_CHANGED_NEW_AREA")
|
|
listener:SetScript("OnEvent", function(_self, event, arg1)
|
|
if event == "PLAYER_ENTERING_WORLD" then
|
|
build()
|
|
updateCycle()
|
|
M.recenterWorldMap()
|
|
elseif event == "ADDON_LOADED" then
|
|
if arg1 == "Blizzard_TimeManager" then
|
|
M.skinClock()
|
|
end
|
|
elseif event == "ZONE_CHANGED_NEW_AREA" then
|
|
M.recenterWorldMap()
|
|
elseif event == "MINIMAP_UPDATE_ZOOM" then
|
|
-- As MinimapMixin:OnEvent: disable a zoom button at the end of its range.
|
|
local plus, minus = _G["MinimapZoomIn"], _G["MinimapZoomOut"]
|
|
local map = M.map
|
|
if plus and minus and map and map.GetZoom and map.GetZoomLevels then
|
|
local level = map:GetZoom()
|
|
if level == map:GetZoomLevels() - 1 then plus:Disable() else plus:Enable() end
|
|
if level == 0 then minus:Disable() else minus:Enable() end
|
|
end
|
|
end
|
|
end)
|
|
|
|
listener:SetScript("OnUpdate", function(self, elapsed)
|
|
self.elapsed = self.elapsed + (elapsed or 0)
|
|
if self.elapsed < CYCLE_PERIOD then
|
|
return
|
|
end
|
|
self.elapsed = 0
|
|
updateCycle()
|
|
end)
|
|
|
|
-- The client calls GameTimeFrame_SetDate on each day change: reapply the calendar image.
|
|
if hooksecurefunc and GameTimeFrame_SetDate then
|
|
hooksecurefunc("GameTimeFrame_SetDate", function() M.updateCalendar() end)
|
|
end
|
|
if WorldMapFrame and WorldMapFrame.HookScript then
|
|
WorldMapFrame:HookScript("OnHide", function() M.recenterWorldMap() end)
|
|
end
|
|
|
|
if build() then
|
|
updateCycle()
|
|
-- Registered with the layout like every frame, so the player can move it.
|
|
if ForeverUI.Layout and ForeverUI.Layout.Register then
|
|
ForeverUI.Layout.Register(_G["MinimapCluster"], "minimap", MINIMAP_LABEL,
|
|
"TOPRIGHT", "TOPRIGHT", 0, 0)
|
|
end
|
|
end
|