🗡️ Эйрон — Wiki

Changelog

Крестик закрытия рюкзака и кеш клиента

Профессии: лестница практики втрое короче, ранги V и VI дороже

Сумка: 60 слотов, продажа и выброс, клетчатое окно

Подземелья и рейды: отряды, живой бой, ступени, фазы боссов, сеты и Осколки

Крафт: оружие, шлем, тело, перчатки и сапоги

Профессии: разбор предметов, слова силы рун в бою, узлы на карте

Журнал изменений (свежие сверху).

Клан: экраны по референсу и баффы за казну

Профессии: у каждого материала появилось дело

Клан: зачем вступать и зачем быть активным

Начертания: Рунное дело переехало из таверны в здание

Рунное дело: профессия в таверне, верхняя градация S

Профессии: экономика добычи, один гейт прогресса, узлы и шкуры

Иконки интерфейса: 108 рисунков вместо эмодзи, листами

Окно персонажа: иконка надетого предмета больше не выезжает из слота

Лор: панель зоны переехала в раздел героя

Зоны охоты: одиннадцать вместо тринадцати

Таверна: свой фон, фоны профессий и стилизация перековки с заказом

Кузня: фон нарисован и подключён

Задания: экран читается по состояниям

Кланы: создание без экипировки, заявки и передача лидерства

Кузня: живая проверка экранов и третий стухший текст

Экран охоты: компактно на телефоне

Аллея героев: значок класса и фракция в рейтинге

Кузня: фон, и только гнёзда под руны

Экран героя: статы по нажатию на надетую шмотку

Начертание: статы рун, блок рун у предмета и фон здания

Фарм: зоны охоты по уровням, фракционные территории и авто-PvP

Заточка снаряжения: уровни +1…+16, четыре свитка и риск разбития

Задания: 5 ежедневных, 15 еженедельных и 100 разовых

Иконки интерфейса вместо эмодзи: 39 картинок подключены (наборы A, B, E, G, I)

Иконки профессий: 53 картинки, рюкзак без эмодзи

Арена: правки владельца — без 2D, без прокрутки, модели сверху

Арена: данные на экранах, двое бойцов в 3D и возврат к соперникам

Фоновая музыка: базовая тема и отдельные сцены для фарма и арены

Чат: цвет текста по вкладке

Падение сервера от одного запроса клиента

Чары: под класс, проклятия, резонанс и заказы

Перевод живых игроков. Чары на предметах не пересчитываются: id, слоты и поле charms те же, charmMaster ни у кого не выставлен. Но эффект у части ролей изменился — у танков, хилеров и кастеров клетка таблицы другая; это осознанное следствие чар под класс, а не ошибка переноса. У кого профессия не enchanting, чары продолжают работать: статы считает getEquipBonus по классу владельца, профессия нужна только чтобы накладывать новые. Рецептов у чародеев не было ни у кого — их покупают заново, компенсации нет.

Как проверял. game/server/test/charms.js — 18 разделов: 26 рецептов и цены тиров, разбор id (проклятия и надстройки чарой не считаются), вся таблица ролей по пяти группам, проклятия, отказы, бросок 50% с потерей, замена, «Двойная» и «Мастерская», резонанс, статы четырёх слоёв вместе, заказы (резерв, провал, снятие, полная сумка) и чистка профиля при загрузке.

Живая проверка — deploy/test-charms.cjs на боевом сервере: каталог чар, покупка пяти рецептов (200 + 200 + 3000 + 7000 + 13000), отказы, предпросмотр («Оберег D» танку — броня 21.25, здоровье 76.5), наложение с бруском 50% и подсчётом списаний, вторая чара с резонансом (+5%), мастерская (+20%) и заказ, купленный вторым персонажем со своим мечом. Снимки — models/чары-1-сетка.png и models/чары-2-попап.png.

Что показал первый живой прогон. Восемь пунктов «провалились», и все — из-за самого теста: human_knight оказался танком, а тест ждал от «Мощи» бойцовских 12.75 вместо танковых 6.38 + 8.5 брони. Тест стал роль-зависимым и считает ожидание из таблицы ТЗ, а заказ теперь пробуется несколько раз — бросок 50% живой.

Сверка перевода — deploy/compare-charm-stats.mjs: считает вклад чар по старой и новой формуле для каждого персонажа. По живой базе (44 профиля) чар нет ни у кого, потерь и флагов мастерской нет — перевод ничего не пересчитывает. Этой же утилитой сверяется копия базы перед выкатом.

Две ошибки в числах, которые поймал расчётом. 0.3 × 3.4 × 1.25 = 1.275, но в JavaScript 1.275 * 100 равно 127.49999999999999, поэтому крит «Мощи D» — 1.27, а не 1.28 (в первой редакции ТЗ стояло 1.28). И суммирование чар округляет каждую отдельно — как руны, — поэтому две «Мощи A» дают крит 0.76, а не 0.75.

Про сумку. Новый предел сумки ломал возвраты: addItem при полной сумке отказывает, а вызывающие этот отказ не смотрят. В заказах и покупках это закрыто: снятие заказа при полной сумке отказывает и не теряет резерв, а покупка вещи не проходит, пока нет места. Заказ услуги места не требует — вещь остаётся у покупателя, меняется только чара на ней. Остальные вызывающие addItem (награды, дроп, переплавка) стоит проверить отдельно: их отказ по-прежнему никто не смотрит.

Автофарм: два вида, оплата Эйронами, работа без вкладки

Как проверял. Новый game/server/test/autofarm.js — 10 разделов: цена 2 и 8 (в том числе при неполной энергии и без вылазки), списание и невозврат для вида А, возврат 4 💎 за две несыгранные вылазки и защита от двойного возврата, цепочка chain → chain → chain → stop с полной длительностью каждой вылазки, отказы (нет Эйронов, нет энергии, мёртв, арена, повторный запуск), остановка при смерти, итоги и разбор сохранения. Весь остальной набор сервера прогнан — autohunt-check.js, combat-check.js, combat-pacing.js, pvp-check.js, professions.js, arena-pacing.js, sp-economy.js зелёные; пять тестов (алхимия, чары, ковка, ювелир, миграция профессий) красные до этой правки и к фарму отношения не имеют.

Живая проверка — выкачено на боевой 03.10.2026. deploy/test-autofarm.cjs: вид А списал 2 💎, набил бои в открытой вкладке и после закрытия вкладки не возобновился (это проверяется только на сервере с базой); вид Б списал 8 💎 и с закрытой вкладкой за 26 секунд добавил 3 победы, 27 золота и 75 опыта; остановка вернула 6 💎 за три несыгранные вылазки и оставила итог в профиле, «Закрыть» его убрало; повторный запуск при идущем фарме и запуск без Эйронов отклонены с текстом «Нужно 8 Эйронов». deploy/test-farm-restart.cjs: systemctl restart game посреди оплаченного фарма — в журнале «автофарм после перезапуска: продолжают 1, закрыто с возвратом 0», сессия поднялась с тем же планом, число побед выросло с 2 до 5, Эйроны второй раз не списались. Снимки интерфейса с боевого — models/autofarm-1-buttons.png и models/autofarm-2-running.png. Временные герои проверок удалены (deploy/clean-test-heroes.cjs).

Вылазки: ×5/×10 — это HP и награда, а не урон

Как проверял. В combat-check.js добавлен раздел 0: 60 000 сэмплов сверяются с формулой (HP, урон, броня, опыт, золото, SP) плюс проверка шансов (героик 4,93%, мифик 0,94%) и показательная таблица 30 уровня — обычный hp 1440 / урон 130, героик 7200 / 195, мифик 14400 / 325. В combat-pacing.js добавлен замер редкости с колонкой «было» — той же формулой, что стояла до правки. combat-check.js заодно перекодирован из двойной перекодировки в нормальный UTF-8 (файл читался как «Р СџРЎР‚Р ВµР В·Р ВµРЎР‚Р С”Р В°…»).

Живая проверка — выкачено на боевой 03.10.2026, deploy/test-rarity.cjs: бой доведён до исхода, и сразу после него «Атака» и «Авто» вернули { ok: false, silent: true, error: 'Нет активного боя' }; статы сверены на 185 обычных, 15 героических и 4 мифических мобах — ноль расхождений (героик 2 уровня: hp 900 / урон 27 — это ×5 HP и ×1,5 урона, а не ×5 на всё).

Соседней сессии стоит знать. Пять тестов сервера красные до этой правки и к вылазкам отношения не имеют: alchemy.js падает на Cannot access 'ALL_RECIPES' before initialization (строка 87 использует константу, объявленную ниже), а charms.js, forge-crafting.js, jewelry.js, professions-migration.js — на p.forge.<профессия> (поле forge у игрока, судя по последним коммитам, переехало). combat-debug.js остался в двойной перекодировке: его текст восстанавливается не полностью (25 символов теряются), поэтому не трогал.

Профессии открываются с 10 уровня

Как проверял. game/server/test/forge-crafting.js, charms.js, professions-migration.js, alchemy.js, jewelry.js — зелёные. Отказы по уровню теперь считаются от самого порога (FORGE_LEVEL_REQ - 1, CHARM_LEVEL_REQ - 1, PROFESSION_LEVEL_REQ - 1): в professions-migration.js стоял зашитый 10 уровень, который после правки стал проходным и проверил бы не отказ, а успех. Остальные тесты сервера (бой, автобой, SP, арена, PvP) прогнаны — зелёные. tsc сервера и tsc && vite build клиента проходят.

Три живые проверки в deploy/ тоже ждали пятидесятого уровня: test-charms.cjs (порог чар в каталоге), test-capital.cjs (учитель не берёт новичка) и test-capital-rules.cjs (отказ до порога). В последней заодно починены id профессий — там со времён свёртки ремёсел остались dagger и staff, то есть проверка «профессия изучена» падала бы на «такой профессии нет», а не на уровне.

Наложение чар: четвёртый слой статов

Попутно починен аукцион — и это не мой слой. Лот переносил только заточку и forge, а камни ювелира лежат в своём поле gems: продажа предмета с камнем стирала камень у покупателя. Теперь лот везёт и камни, и чары — одна функция applyLotLayers на покупку и на снятие, чтобы они не разъехались, — а в списке лотов предмет с чарой помечен ✨. Соседней сессии стоит знать: её камни ехали через аукцион с потерей.

Как проверял. game/server/test/charms.js — 13 разделов: структура и разбор id, совпадение списка слотов с FORGEABLE_SLOTS и GEM_SLOTS, вся таблица чисел на градации D, отказы (без профессии, до 50 уровня, свиток, чужие id, нехватка материалов, нехватка золота) с проверкой, что при отказе ничего не списано, наложение и замена, предпросмотр, статы четырёх слоёв вместе на дроповом предмете, чистка мусорных id при загрузке, неизменность заточки и аукцион. Остальные тесты сервера (ювелир, алхимия, ковка, миграция) прогнаны — зелёные.

Числа проверил расчётом, а не на глаз, и это спасло: 0.3 × 3.4 × 1.25 = 1.275, но в JavaScript 1.275 * 100 равно 127.49999999999999, поэтому округление даёт 1.27, а не 1.28. В первой редакции ТЗ стояло 1.28 — тест упал бы на «моих же» числах.

Живая проверка — deploy/test-charms.cjs, два временных героя на боевом сервере: каталог чар, отказ без профессии (золото не списано), учитель за 3000, отказ в свиток, предпросмотр (20 вариантов, D «Мощь» = атака 12.75 и крит 1.27), наложение A на дроповый меч (+3.75 атаки, крит 25.6 → 25.9), замена на D «Оберег» за 40 000 и продажа предмета вторым персонажем — чара пережила. Временные герои удалены.

Грабли окружения. vite build в этой сессии падает: esbuild не может удалить свой временный файл вне рабочего каталога («Access is denied»). Сборка проходит, если направить TEMP/TMP внутрь рабочего каталога, — на выкате делал так.

Зоны охоты: по одной оспариваемой на диапазон

Пришлось поправить расстановку. Сначала я просто убрал лишние зоны, и пять оставшихся оказались в одной колонке по центру карты — маркеры налезли друг на друга и читались как одна каша. Развёл их «ромбом» вокруг центра, а на карте вылазок сделал маркеры компактнее: значок и диапазон уровней в одну строку вместо двух. Причина не в расстановке, а в пропорциях: карта горизонтальная, на вертикальном экране ей достаётся около 200 пикселей высоты, а маркеров четырнадцать.

Снимки — models/вылазки.png и models/вылазки-оспариваемые.png.

Закрытые разделы в подменю больше не теряются

«Арена 3х3», «Подземелья» и «Рейды» сливались с картой за подменю — их было почти не видно.

Причина оказалась в одной строке: у закрытых плиток стоял opacity: 0.5 на всей плитке. Прозрачность в CSS гасит элемент целиком вместе с его собственной подложкой — панель становилась полупрозрачной и сквозь неё просвечивал экран. Чем темнее фон за подменю, тем хуже было видно.

Починил так: подложку наоборот сделал плотнее (почти непрозрачной), а гашу только содержимое — иконку, подпись и текст. Иконка вдобавок обесцвечивается, поэтому закрытая плитка отличается от открытой не только яркостью. Замок и «с 60 ур.» остались яркими.

Вкладка «Бой»: подменю, вылазки на весь экран, арена в один экран

Чего не вышло. Карта в «Вылазках» занимает всю ширину, но не всю высоту: она горизонтальная (соотношение 1.75:1), а экран телефона вертикальный. Растянуть её на всю высоту можно только обрезкой, а маркеры расставлены в процентах от картинки — при обрезке часть локаций уехала бы за край и стала недоступной. Размытая копия закрывает остальной экран, так что пустого места не видно, но честно: это не «карта на весь экран», а «карта на всю ширину, экран занят ею же». Чтобы сделать по-настоящему на весь экран, нужна карта вертикальной компоновки — это новый арт.

Как проверял. Написал deploy/shot-tabs.cjs: он заводит временного героя, открывает игру в headless-Chrome и после каждого раздела замеряет прокрутку — scrollHeight против clientHeight. Это и есть требование «всё в один экран», и на глаз его не проверить. Во всех четырёх разделах прокрутка равна нулю. Снимки — models/бой-подменю.png, models/вылазки.png, models/арена.png.

Разделы героя уехали в подменю

Ошибка, которую стоит помнить. Первый снимок подменю показал иконки разделов крошечными засечками, и я решил, что старые иконки под-вкладок не годятся. Начал генерировать новые — и зря потратил 27 кредитов (баланс 554 → 527): процесс остановили, три задачи успели уйти на Meshy, а файлы и записи о них не сохранились. Настоящая причина была другой: снимок делался через 900 мс после открытия подменю, а иконки по 700 КБ в этот момент ещё качались — раньше их никто не запрашивал. С паузой в 5 секунд видно, что иконки на месте и выглядят хорошо. Вывод: перед тем как что-то переделывать, убедись, что проблема настоящая — недогруженная картинка и плохая картинка на снимке выглядят одинаково.

Экран героя: только 3D и крупнее фигурка

Как проверял. Статически тут ничего не докажешь: убрать портрет мало, модель должна реально отрисоваться. Написал deploy/shot-hero-live.cjs — он заводит временного героя, поднимает headless-Chrome с WebGL через SwiftShader, кладёт id героя в localStorage до загрузки приложения (иначе показывается экран создания) и снимает настоящий экран. Видно, что рисуется объёмная модель и что она стала крупнее. Тестовых героев после проверки удалил, снимок — models/экран-героя-3d.png.

Экран героя перестроен

Как проверял. Собрал стенд deploy/stand-hero.html — та же разметка и тот же index.css, что в игре, — и снял его headless-Chrome на 360 и 430 пикселях. Так нашлась вещь, которая иначе всплыла бы при проверке вслепую: headless-Chrome игнорирует --window-size, clientWidth оказался 484 вместо 360, и первый снимок «обрезал» правый край там, где на самом деле всё помещалось. Поэтому ширину стенда задаю в CSS, а не размером окна. Снимок — models/экран-героя.png.

Стенд намеренно лежит в deploy/, а не в public/: всё из public/ уезжает на сервер веб-ассетом, и отладочный файл оказался бы в проде. Проверил — на сервере его нет.

Чат, столица и подземелья

Три новых вкладки нижнего меню догнали содержимым.

Чат. Четыре канала: общий, торговля, клан и группа.

Столица. Своя у каждой стороны: Цитадель Зари у Ордена Света, Чёрный Острог у Легиона Тьмы.

Подземелья (с 60 уровня). Три забега: Склеп Забытых, Кузня Глубин и Багровый Чертог. Внутри две волны обычных мобов и босс, вход стоит 40–50 энергии. Забег целиком решает сервер, здоровье между боями переносится — до босса доживают не все. С боссов падают чертежи тех ремёсел, которые иначе не достать.

Грабли. Выручка продавца записывалась в базу, не дожидаясь ответа покупателю, — и продавец, зашедший сразу после покупки, своих денег не находил. Поймал это интеграционный тест: он проходил через раз, потому что запись обычно успевала. Теперь запись выручки идёт до ответа.

Проверки: 18 правил видимости чата, 18 сценариев чата на живом сервере, 35 правил столицы, 31 сценарий столицы и подземелий, 32 проверки данных подземелий. Все зелёные.

Типы оружия, новое нижнее меню, фоны героя и стопки в рюкзаке

Крупная партия правок интерфейса и одна механика.

Оружие разложено по 13 типам. Раньше привязки не было вообще: маг мог надеть меч, лучник — посох. Теперь у класса и у предмета есть weaponType, и при надевании они должны совпасть. Посох — 12 классов, лук и кинжал — по 4, дальше по нисходящей; щит доступен только тем трём типам, у кого оружие одноручное. Подробная таблица — в 01 — Расы и классы.

Нижнее меню перестроено: Чат · Столица · Бой · Клан · Герой · Меню. «Бой» — третья, круглая и поднятая над полосой, единственная цветная. Рюкзак остался кнопкой в верхней панели, «Герой» — шестой вкладкой.

Экран героя. Полосы здоровья, маны и опыта переехали внутрь сцены и стали по 14 px вместо ~70 px на три полосы — фигурке досталась освободившаяся высота, слоты экипировки сузились с 62 до 56 px. У каждого класса появился свой фон, а пока картинка не загрузилась, сцена держится на градиенте расы.

Рюкзак складывает одинаковые предметы в стопку с числом в углу клетки. Подпись стопки — всё, кроме uid: заточенный или перекованный предмет выходит из неё сам и занимает отдельную клетку.

Сгенерировано 45 картинок через Meshy: 36 фонов героя, 7 иконок меню и 2 панорамы столиц. Промты лежат в models/ПРОМТЫ.md и собираются скриптом deploy/art.cjs из models/classes.json и models/art.json. Фоны в вебе — JPEG по 720 px, все 36 занимают 2 МБ.

Объёмные модели есть у всех 36 классов

Догнал через Meshy оставшиеся 30 классов — эльфы, люди, гномы, нежить и вампиры. Теперь объёмная модель есть у каждого класса в игре.

Все шесть классов орков получили объёмные модели

Прогнал через Meshy всю расу орков — от концепт-арта до скелета с четырьмя действиями. Модели прописаны в heroModels.ts и уже в игре.

Материал модели: почему всё блестело и как починено

Модель выглядела как полированный металл или пластик. Разобрался — материал ломает сам Meshy, на шаге риггинга.

Действия модели подключены к бою, автоповорот убран

Громила-орк в игре: первая модель с анимациями

Класс orc_grinder («Громила», танк орков) получил объёмную модель, сгенерированную с нуля через Meshy — от концепт-арта до скелета и четырёх клипов. Это первая модель в игре, которая не просто крутится, а двигается.

Первая объёмная модель: Грозовой шаман

Присланный FBX «Stormcaller Warchief» превращён в игровой ассет, и на экране героя он теперь не картинкой, а настоящей геометрией — крутится сам, раскручивается пальцем. Подробности конвейера — в wiki/08-объёмные-модели.md.

Экран героя переведён на графику

Пришли 12 ассетов из ТЗ_Экран_Героя.md — они вшиты.

Правка после первой примерки

Полоса здоровья выросла с 6 до 22 px, подпись получила иконку, а под-вкладки на телефоне встали в две строки — всё это съело высоту у модели героя. Что сделано: под-вкладки умещаются в одну строку (на узком экране без подписей), зазор между блоками экрана 10 → 8 px, иконка у подписи 18 → 16 px, а у модели появился max-width: 100% — раньше при высоком портрете height: 100% уводил ширину за край колонки и модель наезжала на слоты.

Боевой интерфейс переведён на графику

Ассеты из ТЗ_Боевой_Интерфейс.md больше не лежат мёртвым грузом — они вшиты в бой, арену, окно итога и экран охоты.

Экран героя стал стартовым, новая верхняя панель

Экран боя: без текста, крупные портреты, круглая кнопка атаки

Риск-предупреждение, дорогая прокачка умений, плавная анимация

Динамика боя, окно итога и уровень без лечения

Длительность боя на арене ≈ 30 секунд

Бой на арене переживает обрыв связи

Отдых убран

Вылазка переживает обрыв связи

Жёсткий каркас экрана

Арена 2.0 — ручной PvP

Автоохота

Боёвка 2.0

Текущая версия

Ранние этапы