From 08c1b9da3f8e398205ede205e6de138966d803e4 Mon Sep 17 00:00:00 2001 From: sindoring Date: Sat, 13 Jun 2026 11:51:05 +0400 Subject: [PATCH] ai ready --- AGENTS.md | 46 +++ ai-skills/ui/SKILL.md | 535 ++++++++++++++++++++++++++++++++ ai-skills/ui/widgets-and-api.md | 256 +++++++++++++++ 3 files changed, 837 insertions(+) create mode 100644 AGENTS.md create mode 100644 ai-skills/ui/SKILL.md create mode 100644 ai-skills/ui/widgets-and-api.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..b3da6b1 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,46 @@ +# MoonWell Client Sources — Agent Guide + +## Проект + +WoW-клиент на базе патча **3.3.5a (build 12340, WotLK)**. Репозиторий содержит UI-аддоны и +кастомные интерфейсы для приватного сервера MoonWell. + +Стек: Lua 5.1, FrameXML (XML + Lua), WoW Addon API 3.3.5. + +--- + +## Навыки (Skills) + +Перед тем как работать с UI — **прочитай соответствующий файл из `ai-skills/`**. + +| Задача | Файл | +|--------|------| +| Верстка фреймов, XML, Lua CreateFrame, pixel-perfect из Figma, текстуры, шрифты, якоря | [`ai-skills/ui/SKILL.md`](ai-skills/ui/SKILL.md) | +| Виджеты, типы фреймов, Button/EditBox/ScrollFrame, события, SavedVariables, Blizzard API | [`ai-skills/ui/widgets-and-api.md`](ai-skills/ui/widgets-and-api.md) | + +Читай **весь файл** скилла, а не отдельные секции — там есть ограничения API, типичные ошибки +и готовые шаблоны. + +--- + +## Ключевые ограничения (всегда держи в голове) + +- `## Interface: 30300` — никаких retail-API +- Нет `SetSize()` → только `SetWidth()` / `SetHeight()` +- Нет `C_Timer`, `CreateFramePool`, `PixelUtil` +- Текстуры: только степени двойки, максимум 512×512 px, формат `.tga` / `.blp` +- XML-атрибуты: `relativeTo` (не `relativeKey`), `$parentX` (не `$parent.X`) +- Кнопки и ScrollFrame в XML должны иметь `name` + +--- + +## Структура аддона + +``` +AddonName/ +├── AddonName.toc ← обязательно, имя файла = имя папки +├── AddonName.xml ← верстка (загружается первой) +└── AddonName.lua ← логика +``` + +Порядок файлов в TOC важен: XML раньше Lua. diff --git a/ai-skills/ui/SKILL.md b/ai-skills/ui/SKILL.md new file mode 100644 index 0000000..af0c6d3 --- /dev/null +++ b/ai-skills/ui/SKILL.md @@ -0,0 +1,535 @@ +--- +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 + + +