PaperDollStatTooltip upper-cases UnitClass(unit)'s token, and a unit whose class byte has no ChrClasses row answers two nils (UnitClass, 0x518350): the server's demons do, so hovering the pet page's stats raised "bad argument #1 to strupper". Such a unit now gets the stock default line, in both styles. LEARNED_SPELL_IN_TAB's glow flashed where 1.12's hidden skill line tabs stood: it now sits on the book's own tabs, and choosing a tab stops it, as SpellBookSkillLineTab_OnClick does. Verified: harness probe (the pet tooltip shows the default strength line, no error; the tab 2 glow's center matches our tab 2's; choosing tab 2 hides it). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MoonWell UI: Forever UI для клиента 1.12.1
Форк mod-forever-ui для MoonWell, то есть для клиента benilla (World of Warcraft 1.12.1). Апстрим написан под 3.3.5a. Здесь его переносят на API 1.12 и поставляют отдельным патчем, без правок исходников клиента.
Что где лежит
data/— апстрим как есть: аддон и экраны входа под 3.3.5, арт, данные сервера. Это эталон для порта и источник арта. Здесь не правим, чтобыgit merge upstream/mainпроходил без конфликтов.vanilla/— порт под 1.12:Interface/FrameXML/MoonWellUI/— код, который клиент грузит вместе со стандартным UI: окно выбора стиля и пункт меню;Interface/FrameXML/MoonWellUI/Forever/— перенесённые файлы Forever UI, в порядкеForeverUI.toc(см.Forever.xml);art-src/— исходники нашего арта (PNG). Сборка кодирует их в BLP;tools/build.py— сборка источника патча.
Что перенесено
| Файл апстрима | Как |
|---|---|
UIAtlas*.lua, Texts.lua, Texts_enUS.lua, Texts_ruRU.lua |
как есть: сборка копирует их из data/ и ставит сверху проверку стиля |
AtlasUtil.lua |
целиком: атласы, заливка, nine-slice, панели, вставки, кнопки |
Layout.lua |
позиции и их сохранение (ForeverUIDB через RegisterForSave), /fui reset |
PlayerFrame.lua |
целиком, без транспорта и API угрозы |
TargetFrame.lua |
рамка цели, цель цели, очки серии |
TargetFrameAuras.lua |
баффы и дебаффы цели; в 1.12 у ауры цели нет заклинателя и времени, поэтому все значки одного размера и без таймера |
ActionBar.lua |
главная панель: клиентские кнопки в новой отделке, рамка, стрелки страниц, грифоны/виверны, выезд бонусной панели |
MultiBars.lua |
панели 2–5; левый край считается от ряда, а позиции клиента (UIParent_ManageFramePositions) перебиваются после него |
StanceBar.lua, PetBar.lua |
панели стоек и питомца, без тотемной панели |
BottomBar.lua |
микроменю (кнопки 1.12; «Карта» и «Помощь» убраны, как в camelot), сумки и кольцо ключей, сборка ряда, задержка в подсказке кнопки меню |
StatusBars.lua |
полосы опыта и репутации; потолок уровня 60 |
CastBar.lua |
полоса заклинаний на событиях SPELLCAST_* из 1.12; без щита «нельзя прервать» (1.12 этого не сообщает) |
Minimap.lua |
миникарта: кольцо, маска, полоса зоны, отслеживание (значок способности в рамке), почта, масштаб колесом, кольцо день/ночь поверх GameTimeFrame, серверное время в полосе, координаты; константы в таблице C (лимит 32 upvalue в Lua 5.0) |
Buffs.lua |
баффы и дебаффы игрока на GetPlayerBuff/PLAYER_AURAS_CHANGED, сворачивание долгих баффов; без объединения баффов и отмены зачарований оружия (их нет в 1.12) |
Tooltips.lua |
подсказки: nine-slice camelot вместо рамки клиента, заголовок сравнения у ShoppingTooltip1/2 при их показе |
Bags.lua |
окна сумок: рамка, портрет, сетка, поиск, сортировка, раскладка столбцами; GetItemInfo по строке item:, общая сумка по подклассу аукциона; без валют |
CharacterFrame.lua |
лист персонажа 631×484 в вёрстке camelot: рамка, портрет, полоса уровня, модель с вращением мышью, слоты, статы 1.12 группами, сопротивления, колонка боковых вкладок; страницы регистрируются через CharacterSheet.RegisterPage; питомец пока в вёрстке клиента без старого фона |
ScrollBar.lua |
полоса прокрутки camelot, обработчики на this |
ReputationTab.lua |
страница репутации в панелях листа: свои строки по GetFactionInfo (без подзаголовков 1.12), подробности и флажки справа |
SkillsTab.lua |
страница навыков в панелях листа: свои строки по GetSkillLineInfo, ранг и описание выбранного справа |
PvPTab.lua |
страница чести: циферблат ранга camelot на нативных рангах 1.12 (UnitPVPRank, GetPVPRankProgress), статистика пяти периодов справа; без арен и списка полей боя |
PetTab.lua |
страница питомца: строка уровня и семейства, модель питомца с поворотом мышью, значок рациона и полоса опыта слева; преданность и очки обучения в каменной плашке, характеристики, ближний бой, защита и сопротивления справа; без спутников и маунтов (их нет в 1.12) |
SpellBook.lua |
книга заклинаний camelot: разворот на две страницы или одна (кнопка в заголовке), вкладки категорий (общая с иконкой класса, ветки класса, питомец), сетка с заголовком, листание стрелками и колесом, меню «Скрыть пассивные» и «Показывать все ранги»; обычные кнопки вместо secure-контроллера 3.3.5 (в 1.12 CastSpell не защищён), клики как в стоковом SpellButton_OnClick; без групп-выпадашек (в 1.12 нет GetSpellInfo) и без поиска |
Compat.lua — наши помощники: HookScript, HookGlobal/HookMethod вместо hooksecurefunc,
сила через UnitMana, тексты полос по statusBarText.
Арт. Сборка кладёт в архив только листы, которые нужны перенесённым файлам: она ищет в них
имена атласов и пути Interface\ForeverUI\.... Сейчас это 6 листов, около 12 МБ.
Как это работает
Модуль — это один архив, Data/patch-F.MPQ:
Interface\FrameXML\FrameXML.toc— стандартный toc игрока плюс строкаMoonWellUI\MoonWellUI.xml. Сборка берёт toc из установки, файлов Blizzard в репозитории нет. benilla грузит этот toc из цепочки патчей так же, как 1.12 грузит пропатченный.Interface\FrameXML\MoonWellUI\— наш код. Он грузится после стандартных файлов и до слоя benilla.Interface\MoonWellUI\— наш арт (сейчас превью для окна выбора).
Стиль интерфейса хранится в CVar moonwellUIStyle (classic или forever; пусто, пока
игрок не выбрал). CVar общий для всего клиента и сохраняется в benilla-config/config.toml.
- Первый вход в мир. Пока стиль не выбран, после входа в мир открывается окно «Выберите интерфейс»: два превью, радиокнопки, «Готово». Escape его не закрывает, Enter подтверждает.
- Смена стиля. В меню игры (Esc) под «Макросами» есть пункт «Стиль интерфейса». Он открывает
то же окно, уже с «Отменой». Если стиль изменился, UI перезагружается (
ReloadUI). - Почему не на экране выбора персонажа. В 1.12 у экранов входа (GlueXML) нет
GetCVar/SetCVar. Общий для аккаунта выбор там не сохранить без правок клиента.
Сборка
Нужны Python 3.9+, Pillow и mpqx из benilla (cargo build -p benilla-formats --bin mpqx).
python vanilla\tools\build.py --data D:\WoW-1.12.1\Data `
--mpqx C:\Users\sindo\benilla\target\debug\mpqx.exe
Скрипт пишет build/patch-source/Data/patch-F/. Дальше — обычный деплой MoonWell:
# в C:\Users\sindo\benilla
.\scripts\deploy-moonwell.ps1 -DataPath D:\WoW-1.12.1\Data -IconPath ... -AuthHost ... `
-PatchSource C:\Users\sindo\moonwell-forever-ui\build\patch-source
-PatchSource принимает один каталог. Если у тебя есть другие свои патчи, положи
build/patch-source/Data/patch-F рядом с ними в их Data/.
Порядок патчей. Архивы patch-?.MPQ монтируются по имени, и побеждает последний. Если
архив с более поздней буквой (patch-G … patch-Z) тоже содержит
Interface\FrameXML\FrameXML.toc, он перекроет наш. Тогда либо дай нашему патчу более позднюю
букву (--patch patch-Z), либо добавь нашу строку в тот toc. Русский пакет Data/ruRU
монтируется после всех patch-?.MPQ, но FrameXML.toc в нём нет, поэтому наш он не перекрывает.
Проверка без сервера
Харнес benilla грузит стандартный UI с нашим патчем без окна и логина:
# Data с patch-F.MPQ; в харнесе только англ. локаль
WOW_DATA=<Data> target/debug/examples/addon_harness --probe-ui \
--eval 'return MoonWellUIChoiceFrame:IsVisible()'
Харнес сам шлёт VARIABLES_LOADED, PLAYER_LOGIN и PLAYER_ENTERING_WORLD, поэтому окно выбора
уже открыто.
Харнес и capture-режим ничего не сохраняют, и CVar в них всегда пустой. Чтобы проверить стиль
Forever, собери тестовый патч с --test-style forever: в нём невыбранный клиент сразу стартует
на Forever. Игрокам такой патч не отдаём.
Правила порта
- API — только 1.12. В обработчиках
this,event,arg1…, а неselfи.... Lua 5.0:table.getnвместо#,string.findвместоstring.match,argв функциях с...,math.modвместо%; не больше 32 upvalue на функцию. Нетhooksecurefunc, secure-шаблонов,SetAtlasиз 3.x (у ForeverUI свойAtlasUtilнаSetTexCoord). - Каждый файл Forever UI начинается с
if not MoonWellUI_IsForever() then return endи подключается вForever/Forever.xmlпередForever.lua. На классическом стиле он ничего не меняет. - Файл апстрима, который уже работает на 1.12 (только данные, синтаксис Lua 5.0), не
копируем руками, а добавляем в
UPSTREAM_AS_ISвbuild.py: тогда он обновляется вместе с апстримом. - Перенесённый файл называется как в апстриме и в первой строке говорит, откуда он и чем отличается.
- Не переносим то, чего нет в 1.12: транспорт, руны рыцаря смерти, символы, достижения и статистику, LFG, арены, гильдбанк, парикмахерскую, менеджер экипировки, валюты, тотемную панель.
- Арт берём из
data/art/interface/foreverui. Имя атласа или путь к текстуре пишем в перенесённом файле строкой целиком, или хотя бы его начало ("ui-hud-micromenu-".. набор .. состояние): по этим строкам сборка находит листы. Имя, склеенное из одних переменных, она не увидит.
Синхронизация с апстримом
git fetch upstream
git merge upstream/main # меняется только data/ и файлы апстрима в корне
Новые правки апстрима в data/addon и data/glue смотрим через git diff и переносим в
vanilla/ руками.
Лицензия и арт
Код апстрима — GPL-2 (LICENSE), наш код в vanilla/ — тоже. Арт в data/art взят из
современного клиента. Ни код, ни арт не кладём в репозиторий клиента (moonwell-client-core,
MIT/Apache, без ассетов): модуль живёт здесь и доезжает до игроков только архивом.