3 Commits

Author SHA1 Message Date
sindoring d5e2dce359 правки по encounter journal 2026-06-13 15:07:58 +04:00
sindoring 22ae941c4a Merge branch 'master' into encounter-journal 2026-06-13 11:52:07 +04:00
sindoring 08c1b9da3f ai ready 2026-06-13 11:51:05 +04:00
9 changed files with 1131 additions and 22 deletions
+4
View File
@@ -6,3 +6,7 @@ AWS_DEFAULT_REGION=
AWS_BUCKET= AWS_BUCKET=
AWS_ENDPOINT= AWS_ENDPOINT=
AWS_USE_PATH_STYLE_ENDPOINT= AWS_USE_PATH_STYLE_ENDPOINT=
PRODUCTION_REALMLIST=
PTR_REALMLIST=
LOCAL_REALMLIST=
+46
View File
@@ -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.
+535
View File
@@ -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 `<Size>`)
- **Нет** `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.641.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.01.0** (не 0255).
```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
<Ui xmlns="http://www.blizzard.com/wow/ui/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.blizzard.com/wow/ui/ ..\FrameXML\UI.xsd">
<Script file="MyAddon.lua"/>
<Frame name="MyAddon_Frame" parent="UIParent" toplevel="true" enableMouse="true" movable="true">
<Size>
<AbsDimension x="300" y="200"/>
</Size>
<Anchors>
<Anchor point="CENTER"/>
</Anchors>
<Backdrop bgFile="Interface\DialogFrame\UI-DialogBox-Background"
edgeFile="Interface\DialogFrame\UI-DialogBox-Border"
tile="true">
<BackgroundInsets>
<AbsInset left="11" right="12" top="12" bottom="11"/>
</BackgroundInsets>
<TileSize><AbsValue val="32"/></TileSize>
<EdgeSize><AbsValue val="32"/></EdgeSize>
</Backdrop>
<Layers>
<Layer level="ARTWORK">
<FontString name="$parentTitle" inherits="GameFontNormalLarge" text="Заголовок">
<Anchors>
<Anchor point="TOP"><Offset><AbsDimension x="0" y="-16"/></Offset></Anchor>
</Anchors>
</FontString>
</Layer>
</Layers>
<Frames>
<Button name="$parentClose" inherits="UIPanelCloseButton">
<Anchors>
<Anchor point="TOPRIGHT"><Offset><AbsDimension x="4" y="4"/></Offset></Anchor>
</Anchors>
</Button>
</Frames>
<Scripts>
<OnLoad>MyAddon_Frame_OnLoad(self)</OnLoad>
<OnMouseDown>self:StartMoving()</OnMouseDown>
<OnMouseUp>self:StopMovingOrSizing()</OnMouseUp>
</Scripts>
</Frame>
</Ui>
```
---
## Точки якоря (FRAMEPOINT)
| Точка | Расположение |
|-------|-------------|
| `TOPLEFT` | Верхний левый угол |
| `TOP` | Верхний центр |
| `TOPRIGHT` | Верхний правый угол |
| `LEFT` | Левый центр |
| `CENTER` | Центр |
| `RIGHT` | Правый центр |
| `BOTTOMLEFT` | Нижний левый угол |
| `BOTTOM` | Нижний центр |
| `BOTTOMRIGHT` | Нижний правый угол |
### SetPoint через Lua
```lua
-- frame:SetPoint(point, relativeTo, relativePoint, offsetX, offsetY)
frame:SetPoint("TOPLEFT", UIParent, "TOPLEFT", 10, -10)
frame:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
frame:SetPoint("BOTTOMRIGHT", anotherFrame, "TOPRIGHT", 0, 5)
-- Сброс якорей перед переустановкой
frame:ClearAllPoints()
frame:SetPoint("CENTER", UIParent, "CENTER")
```
> **Правило**: фрейм должен иметь достаточно якорей для однозначного определения позиции **и** размера. Либо Size + один якорь, либо два якоря задающих оба измерения.
---
## Слои и страты
### Draw Layers (внутри фрейма, сверху вниз по видимости)
```
BACKGROUND (0) → BORDER (1) → ARTWORK (2) → OVERLAY (3) → HIGHLIGHT (4)
```
- `HIGHLIGHT` — автоматически показывается при наведении мыши (требует `enableMouse="true"`)
- FontString всегда рисуется **поверх** Texture в том же слое
- Порядок текстур в одном слое **не гарантирован** → разноси по слоям
### Frame Strata (между фреймами)
```
WORLD → BACKGROUND → LOW → MEDIUM → HIGH → DIALOG → FULLSCREEN → FULLSCREEN_DIALOG → TOOLTIP
```
```lua
frame:SetFrameStrata("DIALOG")
frame:SetFrameLevel(5) -- внутри страты (0–255)
```
---
## Виджеты, события, Blizzard API
> Полный справочник — в `references/widgets-and-api.md`.
> Читай его когда нужны: типы фреймов, Button/EditBox/ScrollFrame/StatusBar, события OnEvent/OnUpdate, SavedVariables, пути к текстурам, типичные ошибки.
---
## Шаблонный аддон «с нуля» (минимальный рабочий пример)
**MyAddon.toc**
```toc
## Interface: 30300
## Title: MyAddon
## SavedVariables: MyAddonDB
MyAddon.lua
```
**MyAddon.lua**
```lua
local ADDON_NAME = "MyAddon"
local f = CreateFrame("Frame", ADDON_NAME.."_Frame", UIParent)
f:SetWidth(250)
f:SetHeight(150)
f:SetPoint("CENTER")
f:SetFrameStrata("DIALOG")
f:SetMovable(true)
f:EnableMouse(true)
f:SetBackdrop({
bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
tile = true, tileSize = 32, edgeSize = 32,
insets = {left=11, right=12, top=12, bottom=11}
})
f:Hide()
-- Заголовок
local title = f:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
title:SetPoint("TOP", f, "TOP", 0, -12)
title:SetText("My Addon")
-- Закрытие
local close = CreateFrame("Button", nil, f, "UIPanelCloseButton")
close:SetPoint("TOPRIGHT", f, "TOPRIGHT", 4, 4)
close:SetScript("OnClick", function() f:Hide() end)
-- Перетаскивание
f:SetScript("OnMouseDown", function(self, btn)
if btn == "LeftButton" then self:StartMoving() end
end)
f:SetScript("OnMouseUp", function(self) self:StopMovingOrSizing() end)
-- Инициализация
f:RegisterEvent("ADDON_LOADED")
f:SetScript("OnEvent", function(self, event, name)
if event == "ADDON_LOADED" and name == ADDON_NAME then
MyAddonDB = MyAddonDB or {}
print("|cff00ff00"..ADDON_NAME.."|r загружен. /myaddon")
end
end)
-- Слэш-команда
SLASH_MYADDON1 = "/myaddon"
SlashCmdList["MYADDON"] = function()
if f:IsShown() then f:Hide() else f:Show() end
end
```
+256
View File
@@ -0,0 +1,256 @@
# WoW 3.3.5a — Виджеты, события и Blizzard API
## Создание фреймов через Lua
### CreateFrame
```lua
-- CreateFrame(frameType, name, parent, template)
local f = CreateFrame("Frame", "MyAddon_MainFrame", UIParent)
f:SetWidth(300)
f:SetHeight(200)
f:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
f:SetFrameStrata("DIALOG")
f:SetBackdrop({
bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
tile = true, tileSize = 32, edgeSize = 32,
insets = { left = 11, right = 12, top = 12, bottom = 11 }
})
f:SetBackdropColor(0, 0, 0, 0.8)
```
### Типы фреймов (frameType)
| Тип | Назначение |
|-----|-----------|
| `Frame` | Базовый контейнер |
| `Button` | Кнопка |
| `EditBox` | Поле ввода текста |
| `ScrollFrame` | Прокручиваемая область |
| `StatusBar` | Полоска (HP, мана, XP) |
| `Cooldown` | Кулдаун-свип на иконке |
| `GameTooltip` | Тултип |
| `MessageFrame` | Прокручиваемые сообщения |
| `Model` | 3D-модель |
| `MovieFrame` | Видео |
---
## Текстуры
```lua
local tex = frame:CreateTexture(nil, "ARTWORK")
tex:SetTexture("Interface\\AddOns\\MyAddon\\images\\icon")
tex:SetAllPoints(frame)
tex:SetWidth(32)
tex:SetHeight(32)
tex:SetPoint("CENTER", frame, "CENTER")
tex:SetVertexColor(1, 0.5, 0, 1)
tex:SetAlpha(0.8)
-- Вырезка части текстуры
tex:SetTexCoord(0, 0.5, 0, 0.5)
```
## FontString (текст)
```lua
local fs = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
fs:SetPoint("CENTER", frame, "CENTER")
fs:SetText("Привет, Азерот!")
fs:SetTextColor(1, 1, 0, 1)
fs:SetFont("Fonts\\FRIZQT__.TTF", 14, "OUTLINE")
```
### Стандартные шаблоны шрифтов
| Шаблон | Применение |
|--------|-----------|
| `GameFontNormal` | Обычный текст интерфейса |
| `GameFontNormalLarge` | Заголовки |
| `GameFontNormalSmall` | Мелкий текст |
| `GameFontHighlight` | Подсвеченный |
| `GameFontDisable` | Неактивный (серый) |
| `NumberFontNormal` | Числа |
| `ChatFontNormal` | Чат |
---
## Button
```lua
local btn = CreateFrame("Button", "MyAddon_Btn", frame, "UIPanelButtonTemplate")
btn:SetWidth(80)
btn:SetHeight(22)
btn:SetPoint("BOTTOM", frame, "BOTTOM", 0, 10)
btn:SetText("Нажми")
btn:SetScript("OnClick", function(self)
print("Нажато!")
end)
btn:SetNormalTexture("Interface\\Buttons\\UI-Panel-Button-Up")
btn:SetPushedTexture("Interface\\Buttons\\UI-Panel-Button-Down")
btn:SetHighlightTexture("Interface\\Buttons\\UI-Panel-Button-Highlight")
```
### Шаблоны кнопок
| Шаблон | Вид |
|--------|-----|
| `UIPanelButtonTemplate` | Стандартная кнопка интерфейса |
| `UIPanelCloseButton` | Крестик закрытия окна |
| `UIMenuButtonStretchTemplate` | Растягиваемая кнопка меню |
| `SecureActionButtonTemplate` | Для действий в бою (protected) |
---
## ScrollFrame
```lua
local scroll = CreateFrame("ScrollFrame", "MyAddon_Scroll", frame, "UIPanelScrollFrameTemplate")
scroll:SetPoint("TOPLEFT", frame, "TOPLEFT", 8, -30)
scroll:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -26, 8)
local child = CreateFrame("Frame", "MyAddon_ScrollChild", scroll)
child:SetWidth(scroll:GetWidth())
child:SetHeight(400)
scroll:SetScrollChild(child)
```
## EditBox
```lua
local eb = CreateFrame("EditBox", "MyAddon_Input", frame, "InputBoxTemplate")
eb:SetWidth(200)
eb:SetHeight(20)
eb:SetPoint("CENTER", frame, "CENTER")
eb:SetAutoFocus(false)
eb:SetMaxLetters(255)
eb:SetScript("OnEnterPressed", function(self)
local text = self:GetText()
self:ClearFocus()
end)
```
## StatusBar
```lua
local bar = CreateFrame("StatusBar", nil, frame)
bar:SetWidth(200)
bar:SetHeight(16)
bar:SetPoint("CENTER", frame, "CENTER")
bar:SetStatusBarTexture("Interface\\TargetingFrame\\UI-StatusBar")
bar:SetStatusBarColor(0, 1, 0, 1)
bar:SetMinMaxValues(0, 100)
bar:SetValue(75)
```
---
## Скрипты-события
```lua
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
frame:RegisterEvent("CHAT_MSG_SAY")
frame:SetScript("OnEvent", function(self, event, ...)
if event == "PLAYER_ENTERING_WORLD" then
-- вход в мир
elseif event == "CHAT_MSG_SAY" then
local msg, author = ...
end
end)
```
### Ключевые события
| Событие | Когда |
|---------|-------|
| `PLAYER_ENTERING_WORLD` | Вход в мир / reload |
| `ADDON_LOADED` | Аддон загружен (arg1 == имя) |
| `PLAYER_LOGIN` | После входа |
| `PLAYER_LOGOUT` | При выходе |
| `VARIABLES_LOADED` | SavedVariables загружены |
| `UI_ERROR_MESSAGE` | Ошибка интерфейса |
### OnUpdate — таймер
```lua
local elapsed_total = 0
frame:SetScript("OnUpdate", function(self, elapsed)
elapsed_total = elapsed_total + elapsed
if elapsed_total >= 1.0 then
elapsed_total = 0
-- действие каждую секунду
end
end)
```
---
## SavedVariables
```lua
-- В TOC: ## SavedVariables: MyAddonDB
frame:RegisterEvent("ADDON_LOADED")
frame:SetScript("OnEvent", function(self, event, addonName)
if event == "ADDON_LOADED" and addonName == "MyAddon" then
MyAddonDB = MyAddonDB or { position = { "CENTER", 0, 0 } }
local p = MyAddonDB.position
self:ClearAllPoints()
self:SetPoint(p[1], UIParent, p[1], p[2], p[3])
end
end)
frame:SetScript("OnMouseUp", function(self)
self:StopMovingOrSizing()
local point, _, _, x, y = self:GetPoint()
MyAddonDB.position = { point, x, y }
end)
```
---
## Полезные Blizzard-утилиты (3.3.5a)
```lua
_G["MyAddon_Frame"]:Show()
IsAddOnLoaded("MyAddon")
UnitName("player")
UnitClass("target")
UnitHealth("player"), UnitHealthMax("player")
DEFAULT_CHAT_FRAME:AddMessage("|cffff0000Красный текст|r")
print("Белый текст")
UnitAffectingCombat("player")
```
---
## Часто используемые пути к текстурам Blizzard
```
Interface\DialogFrame\UI-DialogBox-Background
Interface\DialogFrame\UI-DialogBox-Border
Interface\DialogFrame\UI-DialogBox-Gold-Background
Interface\Tooltips\UI-Tooltip-Background
Interface\Tooltips\UI-Tooltip-Border
Interface\Buttons\UI-Panel-Button-Up
Interface\Buttons\UI-Panel-Button-Down
Interface\Icons\INV_Misc_QuestionMark
Interface\TargetingFrame\UI-StatusBar
Interface\ChatFrame\UI-ChatIcon-ScrollDown-Up
```
---
## Типичные ошибки и решения
| Ошибка | Причина | Решение |
|--------|---------|---------|
| `attempt to index global 'X' (nil)` | Фрейм не создан | Проверь порядок файлов в TOC |
| `SetPoint anchor family connection` | Циклические якоря | Переписать цепочку якорей |
| `Script ran too long` | Бесконечный цикл OnUpdate | Early return или счётчик |
| Текстура не отображается | Размер не кратен 2 / >512px | Пересохрани в TGA/BLP |
| `bad argument #1 (string expected)` | nil вместо строки | `if val then` |
| Frame не кликается | Нет `enableMouse="true"` | Добавить атрибут |
+21
View File
@@ -1,3 +1,9 @@
param(
[Parameter(Mandatory=$false)]
[ValidateSet("production", "ptr", "local")]
[string]$Env = "local"
)
# Stop on errors # Stop on errors
$ErrorActionPreference = "Stop" $ErrorActionPreference = "Stop"
@@ -70,6 +76,21 @@ if ($LASTEXITCODE -ge 8) {
Write-Host "MPQ archives deployed to $WOW_HOME" Write-Host "MPQ archives deployed to $WOW_HOME"
# --- Write realmlist.wtf based on selected environment
$realmlist = switch ($Env) {
"production" { $env:PRODUCTION_REALMLIST }
"ptr" { $env:PTR_REALMLIST }
"local" { $env:LOCAL_REALMLIST }
}
if ($realmlist) {
$realmlistPath = Join-Path $WOW_HOME "Data\ruRU\realmlist.wtf"
Set-Content -Path $realmlistPath -Value "set realmlist $realmlist" -Encoding ASCII
Write-Host "Realmlist ($Env): $realmlist"
} else {
Write-Warning "REALMLIST for '$Env' is not set in .env -- skipping realmlist.wtf"
}
# --- Run WoW reload script # --- Run WoW reload script
Write-Host "Launching WoW..." Write-Host "Launching WoW..."
cmd /c $RELOAD_SCRIPT cmd /c $RELOAD_SCRIPT
@@ -177,12 +177,75 @@ function EncounterJournal_InitTab(self)
end end
end end
local function EncounterJournal_ScrollFrame_OnMouseWheel(scrollFrame, delta, isHybrid)
if not scrollFrame then
return false;
end
if isHybrid and HybridScrollFrame_OnMouseWheel then
HybridScrollFrame_OnMouseWheel(scrollFrame, delta);
return true;
end
if ScrollFrameTemplate_OnMouseWheel then
ScrollFrameTemplate_OnMouseWheel(scrollFrame, delta);
return true;
end
local scrollBar = scrollFrame.ScrollBar or scrollFrame.scrollBar or _G[scrollFrame:GetName() .. "ScrollBar"];
if scrollBar then
local minValue, maxValue = scrollBar:GetMinMaxValues();
local step = scrollBar:GetValueStep() or 20;
scrollBar:SetValue(math.min(maxValue, math.max(minValue, scrollBar:GetValue() - delta * step)));
return true;
end
return false;
end
function EncounterJournal_OnMouseWheel(self, delta)
if PlayerGuideFrame and PlayerGuideFrame:IsVisible() and PlayerGuideFrame.BodyScroll and PlayerGuideFrame.BodyScroll:IsShown() then
return EncounterJournal_ScrollFrame_OnMouseWheel(PlayerGuideFrame.BodyScroll, delta);
end
if self.searchResults and self.searchResults:IsVisible() and self.searchResults.scrollFrame and self.searchResults.scrollFrame:IsShown() then
return EncounterJournal_ScrollFrame_OnMouseWheel(self.searchResults.scrollFrame, delta, true);
end
if self.suggestFrame and self.suggestFrame:IsVisible() then
EJSuggestFrame_OnMouseWheel(self.suggestFrame, delta);
return true;
end
local instanceSelect = self.instanceSelect;
if instanceSelect and instanceSelect:IsVisible() and instanceSelect.scroll and instanceSelect.scroll:IsShown() then
return EncounterJournal_ScrollFrame_OnMouseWheel(instanceSelect.scroll, delta);
end
local info = self.encounter and self.encounter.info;
if info and info:IsVisible() then
if info.lootScroll and info.lootScroll:IsShown() then
return EncounterJournal_ScrollFrame_OnMouseWheel(info.lootScroll, delta, true);
elseif info.detailsScroll and info.detailsScroll:IsShown() then
return EncounterJournal_ScrollFrame_OnMouseWheel(info.detailsScroll, delta);
elseif info.overviewScroll and info.overviewScroll:IsShown() then
return EncounterJournal_ScrollFrame_OnMouseWheel(info.overviewScroll, delta);
elseif info.bossesScroll and info.bossesScroll:IsShown() then
return EncounterJournal_ScrollFrame_OnMouseWheel(info.bossesScroll, delta);
end
end
return false;
end
function EncounterJournal_OnLoad(self) function EncounterJournal_OnLoad(self)
EncounterJournalTitleText:SetText(ADVENTURE_JOURNAL); EncounterJournalTitleText:SetText(ADVENTURE_JOURNAL);
SetPortraitToTexture(EncounterJournalPortrait, "Interface\\EncounterJournal\\UI-EJ-PortraitIcon"); SetPortraitToTexture(EncounterJournalPortrait, "Interface\\EncounterJournal\\UI-EJ-PortraitIcon");
self:RegisterCustomEvent("EJ_LOOT_DATA_RECIEVED"); self:RegisterCustomEvent("EJ_LOOT_DATA_RECIEVED");
self:RegisterCustomEvent("EJ_DIFFICULTY_UPDATE"); self:RegisterCustomEvent("EJ_DIFFICULTY_UPDATE");
self:RegisterCustomEvent("SEARCH_DB_LOADED"); self:RegisterCustomEvent("SEARCH_DB_LOADED");
self:EnableMouseWheel(true);
self:SetScript("OnMouseWheel", EncounterJournal_OnMouseWheel);
do do
SetParentFrameLevel(self.inset) SetParentFrameLevel(self.inset)
@@ -1149,6 +1212,30 @@ local toggleTempList = {};
local headerCount = 0; local headerCount = 0;
local loopedSections = {}; local loopedSections = {};
local function EncounterJournal_GetHeaderWidth(sourceFrame)
local width = sourceFrame and sourceFrame:GetWidth() or 0;
if width and width > 20 then
return width;
end
local info = EncounterJournal and EncounterJournal.encounter and EncounterJournal.encounter.info;
local scrollFrame = info and ((info.detailsScroll and info.detailsScroll:IsShown() and info.detailsScroll) or
(info.overviewScroll and info.overviewScroll:IsShown() and info.overviewScroll) or info.detailsScroll or info.overviewScroll);
local child = scrollFrame and (scrollFrame.child or scrollFrame.ScrollChild);
width = child and child:GetWidth() or 0;
if width and width > 20 then
return width;
end
width = scrollFrame and scrollFrame:GetWidth() or 0;
if width and width > 20 then
return math.max(1, width - 30);
end
return 320;
end
function EncounterJournal_UpdateButtonState(self) function EncounterJournal_UpdateButtonState(self)
local oldtex = self.textures.expanded; local oldtex = self.textures.expanded;
if self:GetParent().expanded then if self:GetParent().expanded then
@@ -1178,6 +1265,22 @@ function EncounterJournal_UpdateButtonState(self)
self.tex.down[3]:Hide(); self.tex.down[3]:Hide();
end end
local function EncounterJournal_NormalizeHeaderButton(infoHeader, width)
if not infoHeader or not infoHeader.button then
return;
end
width = width or EncounterJournal_GetHeaderWidth(infoHeader);
infoHeader:SetWidth(width);
infoHeader.button:SetWidth(width);
if infoHeader.button:GetButtonState() ~= "NORMAL" then
infoHeader.button:SetButtonState("NORMAL");
end
EncounterJournal_UpdateButtonState(infoHeader.button);
end
function EncounterJournal_OnClick(self) function EncounterJournal_OnClick(self)
if IsModifiedClick("CHATLINK") and ChatEdit_GetActiveWindow() then if IsModifiedClick("CHATLINK") and ChatEdit_GetActiveWindow() then
if self.link then if self.link then
@@ -1471,6 +1574,7 @@ function EncounterJournal_SetUpOverview(self, role, index)
infoHeader.button.link = link; infoHeader.button.link = link;
infoHeader.sectionID = nextSectionID; infoHeader.sectionID = nextSectionID;
EncounterJournal_NormalizeHeaderButton(infoHeader, EncounterJournal_GetHeaderWidth(self));
infoHeader.overviewDescription:SetWidth(infoHeader:GetWidth() - 20); infoHeader.overviewDescription:SetWidth(infoHeader:GetWidth() - 20);
EncounterJournal_SetDescriptionWithBullets(infoHeader, description); EncounterJournal_SetDescriptionWithBullets(infoHeader, description);
infoHeader:Show(); infoHeader:Show();
@@ -1479,7 +1583,7 @@ end
function EncounterJournal_ToggleHeaders(self, doNotShift) function EncounterJournal_ToggleHeaders(self, doNotShift)
local numAdded = 0; local numAdded = 0;
local infoHeader, parentID, _; local infoHeader, parentID, _;
local hWidth = self:GetWidth(); local hWidth = EncounterJournal_GetHeaderWidth(self);
local nextSectionID; local nextSectionID;
local topLevelSection = false; local topLevelSection = false;
@@ -1703,7 +1807,7 @@ function EncounterJournal_ToggleHeaders(self, doNotShift)
end end
infoHeader.index = nil; infoHeader.index = nil;
infoHeader:SetWidth(hWidth); EncounterJournal_NormalizeHeaderButton(infoHeader, hWidth);
EncounterJournal_SetHeaderDescription(infoHeader, description); EncounterJournal_SetHeaderDescription(infoHeader, description);
-- If this section has not be seen and should start open -- If this section has not be seen and should start open
@@ -1717,6 +1821,7 @@ function EncounterJournal_ToggleHeaders(self, doNotShift)
numAdded = numAdded + EncounterJournal_ToggleHeaders(infoHeader, true); numAdded = numAdded + EncounterJournal_ToggleHeaders(infoHeader, true);
end end
EncounterJournal_NormalizeHeaderButton(infoHeader, hWidth);
infoHeader:Show(); infoHeader:Show();
end -- if not filteredByDifficulty end -- if not filteredByDifficulty
@@ -68,6 +68,22 @@ local function updateScrollBar(scrollFrame, contentHeight, contentWidth)
end end
end end
local function scrollFrameOnMouseWheel(scrollFrame, delta)
if not scrollFrame then return end
if ScrollFrameTemplate_OnMouseWheel then
ScrollFrameTemplate_OnMouseWheel(scrollFrame, delta)
return
end
local scrollBar = getScrollBar(scrollFrame)
if scrollBar then
local minValue, maxValue = scrollBar:GetMinMaxValues()
local step = scrollBar:GetValueStep() or 20
scrollBar:SetValue(math.min(maxValue, math.max(minValue, scrollBar:GetValue() - delta * step)))
end
end
local function hideScrollBar(scrollFrame) local function hideScrollBar(scrollFrame)
local scrollBar = getScrollBar(scrollFrame) local scrollBar = getScrollBar(scrollFrame)
if scrollBar then scrollBar:Hide() end if scrollBar then scrollBar:Hide() end
@@ -78,6 +94,24 @@ local function getBodyChild()
return body and body.ScrollChild return body and body.ScrollChild
end end
local resolveEncounterJournalInstanceID
local function getRecommendationArtwork(data)
if type(data) ~= "table" then return nil end
local artwork = data.artwork or data.cover or data.coverImage or data.buttonImage or data.bgImage or data.icon
if artwork and artwork ~= "" then return artwork end
if not resolveEncounterJournalInstanceID or not EJ_GetInstanceInfo then return nil end
local instanceID = resolveEncounterJournalInstanceID(data)
if not instanceID then return nil end
local ok, _, _, bgImage, buttonImage, loreImage, buttonSmallImage = pcall(EJ_GetInstanceInfo, instanceID)
if ok then
return buttonImage or bgImage or loreImage or buttonSmallImage
end
end
-- ─────────────────────────────────── Цепочка этапов ── -- ─────────────────────────────────── Цепочка этапов ──
local stageBadges = {} local stageBadges = {}
@@ -248,7 +282,12 @@ local function renderRecommendations()
card:SetBackdropBorderColor(0.64, 0.45, 0.15, 0.95) card:SetBackdropBorderColor(0.64, 0.45, 0.15, 0.95)
end end
card.Artwork:SetTexture(BIOME_TEX[r.biome] or BIOME_TEX.fire) local artwork = getRecommendationArtwork(r)
card.Artwork:SetTexCoord(0, 0.68359375, 0, 0.7421875)
card.Artwork:SetVertexColor(1, 1, 1, 1)
if not artwork or not card.Artwork:SetTexture(artwork) then
card.Artwork:SetTexture(BIOME_TEX[r.biome] or BIOME_TEX.fire)
end
card.Name:SetText(r.name or r.title or "") card.Name:SetText(r.name or r.title or "")
card.Loc:SetText(r.loc or r.description or "") card.Loc:SetText(r.loc or r.description or "")
card.Level:SetText(r.level or "") card.Level:SetText(r.level or "")
@@ -368,6 +407,8 @@ end
function MW_PlayerGuide_OnLoad(self) function MW_PlayerGuide_OnLoad(self)
EncounterJournal.playerGuideFrame = self EncounterJournal.playerGuideFrame = self
self:EnableMouse(true)
self:EnableMouseWheel(true)
local bodyChild = self.BodyScroll and self.BodyScroll.ScrollChild local bodyChild = self.BodyScroll and self.BodyScroll.ScrollChild
if bodyChild then if bodyChild then
@@ -386,6 +427,12 @@ function MW_PlayerGuide_OnLoad(self)
C_PlayerGuide.RegisterListener(function() refresh() end) C_PlayerGuide.RegisterListener(function() refresh() end)
end end
function MW_PlayerGuide_OnMouseWheel(self, delta)
if self.BodyScroll and self.BodyScroll:IsShown() then
scrollFrameOnMouseWheel(self.BodyScroll, delta)
end
end
function MW_PlayerGuide_OnShow() function MW_PlayerGuide_OnShow()
if not C_PlayerGuide.HasData() then if not C_PlayerGuide.HasData() then
C_PlayerGuide.SetLoading() C_PlayerGuide.SetLoading()
@@ -399,16 +446,94 @@ function MW_PlayerGuide_RefreshButton_OnClick()
C_PlayerGuide.RequestRefresh() C_PlayerGuide.RequestRefresh()
end end
local function validEncounterJournalInstanceID(value)
local instanceID = tonumber(value)
if not instanceID or instanceID <= 0 or not EJ_GetInstanceInfo then return nil end
local ok, instanceName = pcall(EJ_GetInstanceInfo, instanceID)
if ok and instanceName then return instanceID end
end
local function mapIDToEncounterJournalInstanceID(value)
local mapID = tonumber(value)
if not mapID or mapID <= 0 then return nil end
if not C_EncounterJournal or not C_EncounterJournal.GetInstanceIDByMapID then return nil end
local ok, instanceID = pcall(C_EncounterJournal.GetInstanceIDByMapID, mapID)
if ok then
return validEncounterJournalInstanceID(instanceID)
end
end
function resolveEncounterJournalInstanceID(data)
if type(data) == "number" or type(data) == "string" then
return validEncounterJournalInstanceID(data) or mapIDToEncounterJournalInstanceID(data)
end
if type(data) ~= "table" then return nil end
local instanceID =
validEncounterJournalInstanceID(data.ej_instanceID) or
validEncounterJournalInstanceID(data.ejInstanceID) or
validEncounterJournalInstanceID(data.journalInstanceID) or
validEncounterJournalInstanceID(data.instanceID) or
validEncounterJournalInstanceID(data.dungeonID) or
validEncounterJournalInstanceID(data.targetID)
if instanceID then return instanceID end
return mapIDToEncounterJournalInstanceID(data.mapID) or
mapIDToEncounterJournalInstanceID(data.worldMapAreaID) or
mapIDToEncounterJournalInstanceID(data.instanceID) or
mapIDToEncounterJournalInstanceID(data.dungeonID) or
mapIDToEncounterJournalInstanceID(data.targetID)
end
local function requestOpenTarget(data)
if type(data) == "table" and data.id and C_PlayerGuide and C_PlayerGuide.RequestOpenTarget then
C_PlayerGuide.RequestOpenTarget(data.id)
end
end
local function openEncounterJournalInstance(data, encounterID)
if not EncounterJournal then return nil end
local instanceID = resolveEncounterJournalInstanceID(data)
if not instanceID then
requestOpenTarget(data)
return nil
end
if type(data) == "table" and not encounterID then
encounterID = data.encounterID
end
if not EncounterJournal:IsShown() then
ShowUIPanel(EncounterJournal)
end
if EncounterJournal.encounter then
EncounterJournal.encounter:Show()
end
if EncounterJournal.instanceSelect then
EJ_ContentTab_SelectAppropriateInstanceTab(instanceID)
end
if NavBar_Reset and EncounterJournal.navBar then
NavBar_Reset(EncounterJournal.navBar)
end
EncounterJournal_DisplayInstance(instanceID)
if encounterID and encounterID > 0 then
EncounterJournal_DisplayEncounter(encounterID)
end
end
function MW_PlayerGuide_Objective_OnClick(self) function MW_PlayerGuide_Objective_OnClick(self)
local o = self.objectiveData local o = self.objectiveData
if not o then return end if not o then return end
if o.type == "dungeon" or o.type == "raid" then if o.type == "dungeon" or o.type == "raid" then
if o.instanceID and o.instanceID > 0 then openEncounterJournalInstance(o)
if not EncounterJournal:IsShown() then ShowUIPanel(EncounterJournal) end
EJ_ContentTab_SelectAppropriateInstanceTab(o.instanceID)
EncounterJournal_DisplayInstance(o.instanceID)
end
elseif o.type == "boss" then elseif o.type == "boss" then
if o.encounterID and o.encounterID > 0 then if o.encounterID and o.encounterID > 0 then
if not EncounterJournal:IsShown() then ShowUIPanel(EncounterJournal) end if not EncounterJournal:IsShown() then ShowUIPanel(EncounterJournal) end
@@ -457,11 +582,7 @@ function MW_PlayerGuide_Objective_OnLeave() GameTooltip:Hide() end
function MW_PlayerGuide_Rec_OnClick(self) function MW_PlayerGuide_Rec_OnClick(self)
local r = self.recData local r = self.recData
if r and r.instanceID and r.instanceID > 0 then if r then openEncounterJournalInstance(r) end
if not EncounterJournal:IsShown() then ShowUIPanel(EncounterJournal) end
EJ_ContentTab_SelectAppropriateInstanceTab(r.instanceID)
EncounterJournal_DisplayInstance(r.instanceID)
end
end end
function MW_PlayerGuide_Rec_OnEnter(self) function MW_PlayerGuide_Rec_OnEnter(self)
@@ -133,16 +133,19 @@
<Layers> <Layers>
<Layer level="ARTWORK"> <Layer level="ARTWORK">
<!-- Artwork: 138x56, top offset 5 → bottom at y=61 from card top --> <!-- Artwork: 138x56, top offset 5 → bottom at y=61 from card top -->
<Texture name="$parentArtwork" parentKey="Artwork" file="Interface\Buttons\WHITE8X8"> <Texture name="$parentArtwork" parentKey="Artwork" file="Interface\Buttons\WHITE8X8" setAllPoints="true">
<Size><AbsDimension x="168" y="72"/></Size> <Color r="1" g="1" b="1"/>
<Anchors>
<Anchor point="TOP"><Offset><AbsDimension x="0" y="-5"/></Offset></Anchor>
</Anchors>
<Color r="0.55" g="0.16" b="0.07"/>
</Texture> </Texture>
</Layer> </Layer>
<Layer level="OVERLAY"> <Layer level="OVERLAY">
<!-- Level: top-right corner of artwork (artwork starts at y=-5 → level at TOPRIGHT+(-4,-9)) --> <!-- Level: top-right corner of artwork (artwork starts at y=-5 → level at TOPRIGHT+(-4,-9)) -->
<Texture name="$parentTextBackdrop" parentKey="TextBackdrop" file="Interface\Buttons\WHITE8X8">
<Size><AbsDimension x="176" y="42"/></Size>
<Anchors>
<Anchor point="BOTTOM"/>
</Anchors>
<Color r="0" g="0" b="0" a="0.72"/>
</Texture>
<FontString name="$parentLevel" parentKey="Level" inherits="GameFontNormalSmall" justifyH="RIGHT"> <FontString name="$parentLevel" parentKey="Level" inherits="GameFontNormalSmall" justifyH="RIGHT">
<Size><AbsDimension x="80" y="12"/></Size> <Size><AbsDimension x="80" y="12"/></Size>
<Anchors> <Anchors>
@@ -154,7 +157,7 @@
<FontString name="$parentName" parentKey="Name" inherits="GameFontNormal" justifyH="LEFT"> <FontString name="$parentName" parentKey="Name" inherits="GameFontNormal" justifyH="LEFT">
<Size><AbsDimension x="162" y="16"/></Size> <Size><AbsDimension x="162" y="16"/></Size>
<Anchors> <Anchors>
<Anchor point="TOPLEFT"><Offset><AbsDimension x="8" y="-80"/></Offset></Anchor> <Anchor point="TOPLEFT"><Offset><AbsDimension x="8" y="-82"/></Offset></Anchor>
</Anchors> </Anchors>
<Color r="1.0" g="0.91" b="0.63"/> <Color r="1.0" g="0.91" b="0.63"/>
</FontString> </FontString>
@@ -162,7 +165,7 @@
<FontString name="$parentLoc" parentKey="Loc" inherits="MW_PG_Italic" justifyH="LEFT"> <FontString name="$parentLoc" parentKey="Loc" inherits="MW_PG_Italic" justifyH="LEFT">
<Size><AbsDimension x="162" y="16"/></Size> <Size><AbsDimension x="162" y="16"/></Size>
<Anchors> <Anchors>
<Anchor point="TOPLEFT"><Offset><AbsDimension x="8" y="-96"/></Offset></Anchor> <Anchor point="TOPLEFT"><Offset><AbsDimension x="8" y="-98"/></Offset></Anchor>
</Anchors> </Anchors>
</FontString> </FontString>
</Layer> </Layer>
@@ -480,6 +483,7 @@
<Scripts> <Scripts>
<OnLoad function="MW_PlayerGuide_OnLoad"/> <OnLoad function="MW_PlayerGuide_OnLoad"/>
<OnShow function="MW_PlayerGuide_OnShow"/> <OnShow function="MW_PlayerGuide_OnShow"/>
<OnMouseWheel function="MW_PlayerGuide_OnMouseWheel"/>
</Scripts> </Scripts>
</Frame> </Frame>
@@ -313,6 +313,11 @@ function L.GetObjectiveInfo(index)
questID = o.questID, questID = o.questID,
questChainID = o.questChainID, questChainID = o.questChainID,
instanceID = o.instanceID, instanceID = o.instanceID,
ej_instanceID = o.ej_instanceID or o.ejInstanceID,
journalInstanceID = o.journalInstanceID,
mapID = o.mapID,
worldMapAreaID = o.worldMapAreaID,
dungeonID = o.dungeonID,
encounterID = o.encounterID, encounterID = o.encounterID,
achievementID = o.achievementID, achievementID = o.achievementID,
factionID = o.factionID, factionID = o.factionID,
@@ -342,7 +347,19 @@ function L.GetRecommendationInfo(index)
loc = r.loc, loc = r.loc,
level = r.level, level = r.level,
biome = r.biome, biome = r.biome,
artwork = r.artwork,
cover = r.cover,
coverImage = r.coverImage,
bgImage = r.bgImage,
buttonImage = r.buttonImage,
icon = r.icon,
instanceID = r.instanceID, instanceID = r.instanceID,
ej_instanceID = r.ej_instanceID or r.ejInstanceID,
journalInstanceID = r.journalInstanceID,
mapID = r.mapID,
worldMapAreaID = r.worldMapAreaID,
targetID = r.targetID,
dungeonID = r.dungeonID,
encounterID = r.encounterID, encounterID = r.encounterID,
priority = r.priority or 0, priority = r.priority or 0,
} }