--- name: wow-335a-ui description: > Верстка и разработка UI-аддонов для World of Warcraft 3.3.5a (WotLK, build 12340). Используй этот скилл при любой работе с интерфейсом WoW 3.3.5: создание фреймов через XML или Lua CreateFrame(), позиционирование через SetPoint/якоря, слои (BACKGROUND/ARTWORK/OVERLAY/HIGHLIGHT), страты (frameStrata), текстуры, шрифты, кнопки, ScrollFrame, скрипты-события, структура аддона (TOC + XML + Lua), SavedVariables, работа с Blizzard API патча 3.3.5. Также применяй когда речь идёт об AzerothCore, кастомных интерфейсах для WoW-прайват серверов 3.3.5a, Eluna или модификации FrameXML. ОБЯЗАТЕЛЬНО применяй при задачах pixel-perfect переноса дизайна (Figma/PSD/скриншот) в WoW UI: пересчёт координат с учётом UIParent scale, конвертация цветов HEX→RGBA, подбор шрифтов, подготовка текстур (степени двойки, TexCoord-кроп), устранение расхождений между макетом и игровым интерфейсом. --- # WoW 3.3.5a — Верстка UI (FrameXML / AddOn Development) ## Ключевые ограничения патча 3.3.5a - **API версии**: `## Interface: 30300` (build 12340) - **Lua версия**: Lua 5.1 - **Текстуры**: только степени двойки по ширине/высоте (8, 16, 32, 64, 128, 256, 512 px); максимум 512×512 - **Нет** `:SetSize()` — только `:SetWidth()` / `:SetHeight()` (или через XML ``) - **Нет** `hooksecurefunc` для защищённых фреймов без taint - Отсутствуют многие retail-API: нет `C_Timer`, нет `CreateFramePool`, нет `PixelUtil` - Таймеры — через `OnUpdate` с накоплением `elapsed` --- ## Pixel-Perfect: перенос дизайна из Figma/PSD в WoW UI ### Главная проблема: система координат WoW ≠ пиксели экрана WoW рисует UI в **виртуальных единицах**, а не в экранных пикселях. Размер виртуального экрана зафиксирован движком на **768 единиц по высоте**, ширина зависит от соотношения сторон: | Разрешение | Виртуальная ширина | Виртуальная высота | |-----------|-------------------|-------------------| | 1024×768 | 1024 | 768 | | 1280×960 | 1024 | 768 | | 1920×1080 | 1365 | 768 | | 2560×1440 | 1365 | 768 | > **Вывод**: высота UIParent всегда = 768. Всё, что сделано в Figma на 768px по высоте, переносится 1:1 без пересчёта. При другой высоте холста в Figma — нужен коэффициент. ### Пересчёт координат из Figma в WoW ```lua -- Формула: wow_value = figma_value * (768 / figma_canvas_height) -- Если макет сделан на 1080px: local SCALE = 768 / 1080 -- ≈ 0.711 local function px(figma_px) return math.floor(figma_px * SCALE + 0.5) -- округление к ближайшему end -- Использование: frame:SetWidth(px(400)) -- figma: 400px → wow: 284 frame:SetHeight(px(200)) -- figma: 200px → wow: 142 frame:SetPoint("TOPLEFT", UIParent, "TOPLEFT", px(50), -px(30)) ``` > Если макет 768px в высоту — `SCALE = 1.0`, пересчёт не нужен. ### UIParent scale и как он ломает пиксели Игрок может изменить масштаб UI в настройках. Это сдвигает все координаты. ```lua -- Получить текущий масштаб UIParent local uiScale = UIParent:GetScale() -- обычно 0.64–1.0 -- Перевести экранные пиксели в виртуальные единицы WoW local function screenPxToWow(px) local screenH = select(2, GetPhysicalScreenSize()) -- реальный размер экрана return px * (768 / screenH) / uiScale end -- Принудительно зафиксировать scale (использовать с умом!) -- UIParent:SetScale(1.0) -- сломает все стандартные фреймы ``` **Рекомендация**: не трогай `UIParent:SetScale()`. Вместо этого рисуй в виртуальных единицах и принимай, что у разных игроков масштаб разный. Для абсолютного pixel-perfect на конкретном разрешении — дай опцию ручного масштабирования фрейма в настройках. ```lua -- Масштабируй только свой фрейм frame:SetScale(0.85) -- Тогда все дочерние элементы тоже масштабируются ``` --- ### Цвета: Figma HEX/RGBA → WoW RGBA WoW принимает цвета в диапазоне **0.0–1.0** (не 0–255). ```lua -- Конвертер HEX → WoW RGBA local function hex(str, a) str = str:gsub("#", "") local r = tonumber(str:sub(1,2), 16) / 255 local g = tonumber(str:sub(3,4), 16) / 255 local b = tonumber(str:sub(5,6), 16) / 255 return r, g, b, a or 1.0 end -- Использование: tex:SetVertexColor(hex("#FF6A00")) -- непрозрачный tex:SetVertexColor(hex("#FF6A00", 0.8)) -- 80% непрозрачность fs:SetTextColor(hex("#FFD100")) frame:SetBackdropColor(hex("#1A1A2E", 0.95)) frame:SetBackdropBorderColor(hex("#4A90D9")) ``` **Figma opacity → WoW alpha**: значение из Figma (например, 75%) делится на 100 → `0.75`. ```lua -- Figma: opacity 60% frame:SetAlpha(0.60) tex:SetAlpha(0.60) ``` --- ### Шрифты: как приблизить к дизайну WoW 3.3.5a поддерживает только `.ttf` шрифты, помещённые в папку аддона. ```lua -- Кастомный шрифт из аддона fs:SetFont("Interface\\AddOns\\MyAddon\\fonts\\MyFont.ttf", 14, "OUTLINE") -- Флаги: "", "OUTLINE", "THICKOUTLINE", "MONOCHROME", "OUTLINE,MONOCHROME" ``` **Соответствие Figma → WoW размер шрифта**: | Figma (макет 1080px) | WoW pt (при SCALE=0.711) | |---------------------|--------------------------| | 12 px | 9 | | 14 px | 10 | | 16 px | 11 | | 18 px | 13 | | 24 px | 17 | | 32 px | 23 | ```lua -- Автоматический пересчёт размера шрифта local function fontSize(figma_pt) return math.max(8, math.floor(figma_pt * SCALE + 0.5)) end fs:SetFont("Fonts\\FRIZQT__.TTF", fontSize(18), "OUTLINE") ``` > **Минимальный читаемый размер в WoW**: 8 pt. Ниже — нечитаемо. **Встроенные шрифты WoW 3.3.5a**: ``` Fonts\FRIZQT__.TTF — основной интерфейсный (без кириллицы!) Fonts\MORPHEUS.ttf — декоративный (заголовки) Fonts\ARIALN.TTF — Arial Narrow Fonts\skurri.ttf — шрифт повреждений ``` > **Кириллица**: встроенные шрифты WoW не поддерживают кириллицу. Положи в аддон `.ttf` с кириллицей (например, PT Sans, Roboto) и подключи явно. --- ### Текстуры: подготовка под ограничения движка #### Требования к текстурам WoW 3.3.5a - Ширина и высота — **степени двойки**: 2, 4, 8, 16, 32, 64, 128, 256, 512 - Максимум: **512×512 px** - Формат: `.tga` (RGBA, 32-bit) или `.blp` - Текстура не обязана быть квадратной: 512×64 — ок, 256×128 — ок #### Как уместить большой элемент дизайна Если элемент из Figma больше 512px — разрезай на части: ``` [512×256] [512×256] ← верхняя половина (левая + правая) [512×256] [512×256] ← нижняя половина ``` ```lua -- Сборка из 4 текстур local tl = frame:CreateTexture(nil, "BACKGROUND") tl:SetTexture("Interface\\AddOns\\MyAddon\\bg_tl") tl:SetPoint("TOPLEFT", frame, "TOPLEFT") tl:SetWidth(256); tl:SetHeight(128) local tr = frame:CreateTexture(nil, "BACKGROUND") tr:SetTexture("Interface\\AddOns\\MyAddon\\bg_tr") tr:SetPoint("TOPRIGHT", frame, "TOPRIGHT") tr:SetWidth(256); tr:SetHeight(128) -- ... и так далее ``` #### TexCoord — показать только часть текстуры Если в одном файле несколько элементов (спрайт-лист), используй `SetTexCoord`: ```lua -- SetTexCoord(left, right, top, bottom) — от 0.0 до 1.0 -- Пример: иконка занимает правый нижний квадрант 256×256 атласа tex:SetTexCoord(0.5, 1.0, 0.5, 1.0) -- Формула для ячейки в сетке (col/row с нуля): local cols, rows = 4, 4 local col, row = 2, 1 -- третья колонка, вторая строка tex:SetTexCoord( col/cols, -- left (col+1)/cols, -- right row/rows, -- top (row+1)/rows -- bottom ) ``` > **Важно**: при SetTexCoord текстура тайлится внутри заданного Size фрейма. SetAllPoints растягивает — SetWidth/SetHeight + SetPoint = точный размер. --- ### Позиционирование: от Figma к SetPoint В Figma позиция элемента — это X,Y от левого верхнего угла холста (или родителя). В WoW — якорная система. **Алгоритм переноса**: 1. Определи ближайший угол/сторону в Figma (от чего считать) 2. Вычисли отступ от этой стороны 3. Пересчитай через `px()` ```lua -- Figma (холст 1365×768): -- Элемент: X=50, Y=30, W=200, H=100 -- → привязка от TOPLEFT UIParent frame:SetWidth(px(200)) frame:SetHeight(px(100)) frame:SetPoint("TOPLEFT", UIParent, "TOPLEFT", px(50), -px(30)) -- ^^^ ^^^ -- X offset Y offset (отрицательный! вниз) ``` > **Знаки осей WoW**: X растёт вправо (+), Y растёт вверх (+). Отступ вниз от TOPLEFT — **отрицательный**. | Figma anchor | WoW SetPoint | |-------------|-------------| | Top-Left | `"TOPLEFT", UIParent, "TOPLEFT", x, -y` | | Top-Right | `"TOPRIGHT", UIParent, "TOPRIGHT", -x, -y` | | Bottom-Left | `"BOTTOMLEFT", UIParent, "BOTTOMLEFT", x, y` | | Bottom-Right | `"BOTTOMRIGHT", UIParent, "BOTTOMRIGHT", -x, y` | | Center | `"CENTER", UIParent, "CENTER", x - W/2_canvas, y` | --- ### Вспомогательный модуль: px-утилиты (вставь в начало Lua) ```lua -- PixelHelper — pixel-perfect вёрстка из Figma local PH = {} PH.CANVAS_HEIGHT = 768 -- высота холста в Figma (измени если другая!) function PH.scale() return 768 / PH.CANVAS_HEIGHT end -- Пересчёт px из Figma → WoW единицы function PH.px(v) return math.floor(v * PH.scale() + 0.5) end -- Цвет из HEX (#RRGGBB или #RRGGBBAA) function PH.color(hex, alpha) hex = hex:gsub("#","") local r = tonumber(hex:sub(1,2),16)/255 local g = tonumber(hex:sub(3,4),16)/255 local b = tonumber(hex:sub(5,6),16)/255 local a = alpha or (hex:len() == 8 and tonumber(hex:sub(7,8),16)/255) or 1 return r, g, b, a end -- Размер шрифта из Figma → WoW function PH.font(pt) return math.max(8, math.floor(pt * PH.scale() + 0.5)) end -- Установка якоря с автопересчётом function PH.point(frame, point, relativeTo, relPoint, x, y) frame:SetPoint(point, relativeTo, relPoint, PH.px(x or 0), PH.px(y or 0)) end -- Размер фрейма из Figma function PH.size(frame, w, h) frame:SetWidth(PH.px(w)) frame:SetHeight(PH.px(h)) end -- Экспорт глобально (или оставь локальным) MyAddon_PH = PH ``` **Использование**: ```lua local px = MyAddon_PH.px local color = MyAddon_PH.color local pxfont = MyAddon_PH.font MyAddon_PH.size(frame, 400, 250) MyAddon_PH.point(frame, "TOPLEFT", UIParent, "TOPLEFT", 50, -30) frame:SetBackdropColor(color("#1A1A2E", 0.95)) fs:SetTextColor(color("#FFD100")) fs:SetFont("Interface\\AddOns\\MyAddon\\fonts\\MyFont.ttf", pxfont(16), "OUTLINE") ``` --- ### Чеклист pixel-perfect переноса - [ ] Холст Figma = 768px в высоту (или задан `PH.CANVAS_HEIGHT`) - [ ] Все размеры и отступы пересчитаны через `px()` - [ ] Цвета конвертированы через `color()` - [ ] Текстуры нарезаны до 512×512, размеры кратны степени двойки - [ ] Шрифты `.ttf` подключены из папки аддона (если кириллица или кастомный шрифт) - [ ] Размеры шрифтов пересчитаны через `font()` - [ ] Y-отступы от верхних якорей — **отрицательные** - [ ] Проверено на scale UIParent ≠ 1.0 (изменить в настройках игры для теста) - [ ] Текстуры без SetAllPoints — точный SetWidth/SetHeight + SetPoint --- ``` MyAddon/ ├── MyAddon.toc ← обязателен, имя файла = имя папки ├── MyAddon.xml ← верстка фреймов (опционально) └── MyAddon.lua ← логика ``` ### TOC-файл (3.3.5a) ```toc ## Interface: 30300 ## Title: My Addon ## Notes: Описание аддона ## Author: Ilyas ## Version: 1.0 ## SavedVariables: MyAddonDB MyAddon.xml MyAddon.lua ``` > Файлы загружаются **в порядке перечисления**. XML раньше Lua — фреймы существуют к моменту выполнения скриптов. --- ## XML-верстка ### Минимальный шаблон XML-файла ```xml