Проверено вживую в Chrome: лобби -> матч -> бот ходит -> результат ->
статистика в лобби. Полный матч отыгран, оба исхода видел.
Баги, которые собирались и типизировались, но падали только в рантайме:
1. Canvas не менял размер под экран. Своего cc.Widget у него не было,
а alignCanvasWithScreen правит только камеру. Canvas оставался
1280x720, и все растянутые по нему дети — тоже.
2. [...map.values()] превращается сборкой в [].concat(map.values()),
а concat не разворачивает итератор, а кладёт его одним элементом.
Отсюда "view.cancelAnimations is not a function" на первой же уборке
карт. Теперь Array.from.
3. Рубашка козыря рисовалась поверх колоды, а не под ней.
Вёрстка переделана под требование UI.md (4:3 .. 21:9):
- fitWidth даёт постоянную ширину, но не высоту: на 4:3 видно только
±720 юнитов. Кнопка "Leave" на y=840 и низ руки на -860 были за
экраном именно там, где это важнее всего
- рука и панель действий считаются от нижней границы экрана,
"Leave" прибит виджетом к левому верхнему углу
- стол — один ряд до шести карт вместо двух рядов по три: экономит
~215 юнитов высоты, которых на 4:3 нет. Ряд центрируется по тем
картам, что реально на столе
- затемнение под окном результата рисуется непрозрачным спрайтом:
тонировать и без того полупрозрачный спрайт почти ничего не даёт
Инструменты:
- npm run serve — отдаёт сборку и роут /_aspects: игра в трёх
фиксированных iframe-ах 4:3 / 16:9 / 21:9. Движок подгоняет себя
под окно, поэтому iframe — единственный простой способ увидеть
вёрстку в форме, которой окно не имеет
- npm run check:spreads — ловит спред неитерируемого в массив
- npm run check — чистота core, спреды, сцены, типы, тесты
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nodeCreate кладёт узел на слой DEFAULT, а камера Canvas рисует только
UI_2D. Поэтому сборка показывала цвет очистки камеры и больше ничего.
В редакторе это не видно вообще: камера вида сцены рисует все слои,
так что сцена выглядит правильной, а билд — пустым.
- 45 узлов в обеих сценах и префабе переведены на слой UI_2D
- scripts/check-scenes.mjs: headless-проверка сцен и префабов —
слои, наличие камеры у Canvas, видимость UI_2D у камеры,
незаполненные ссылки в наших компонентах. Проверено, что она
действительно падает на подсунутом неверном слое
- npm run check — всё разом (чистота core, сцены, типы, тесты)
- Bootstrap теперь докладывает каждый шаг в лейбл и в консоль
и через 10 секунд уходит в лобби даже если арт не догрузился:
молча висящий загрузчик — худший вид бага в отчёте
- ловушка со слоями описана в docs/CODE_MODE_SETUP.md
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Обе сцены собраны через Code Mode, не руками.
- loader: поднимает модули, прогревает арт карт, уходит в lobby
- lobby: обе UI-ветки в одной сцене (по ARCHITECTURE.md), матч — это
смена видимого корня, а не загрузка сцены
- GameScreen рисует текущее состояние и доводит узлы твинами, а не
проигрывает поток событий: узел карты живёт вместе со своей картой,
поэтому "шестёрка ушла из руки на стол" не требует отдельного случая
- один тап = ход целиком; логика вынесена в core/rules/intent.ts
и покрыта тестами, в том числе проверкой, что она никогда не предложит
ход, который правила отвергнут
- арт карт переехал в assets/resources: .pac — это инструкция упаковки
на сборке, а не рантайм-ассет, и без ссылок на кадры в сборку
не попадало ничего. Теперь все 37 спрайтов в одной текстуре 2048x2048
- клики кнопок подписываются в коде, а не через clickEvents сцены
- строки интерфейса собраны в одном файле
Сборка web-mobile проходит чисто, все ссылки в сцене проверены.
Вживую не запускалось: браузер в этом окружении не поднимает
главный цикл движка. Подробности — в docs/TECHNICAL.md.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- scripts/build-art.mjs: все спрайты игры собираются из исходников,
а не кладутся руками. Лица карт растеризуются из векторов,
рубашка/стол/панели/кнопки нарисованы SVG-ом прямо в скрипте
- 36 лиц 220x320 + рубашка, автоатлас 2048x2048, обрезка выключена
(раскладка рассчитывает на точный размер карты)
- девятипатчевые плитки 120x120 с рамкой 40
- фон стола 540x960, рисуется в 2x — градиенту детализация не нужна,
а памяти под текстуру уходит вчетверо меньше
- исходные векторы перенесены из local/ в art-src/cards
- ATTRIBUTION.md: LGPL-3.0, атрибуция обязательна; там же написано,
что менять, если такая лицензия не подходит для релиза
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
По ARCHITECTURE.md: реестр независимых секций, JSON с ключом на секцию.
Ключи — явные строковые константы, а не имена классов: релизная сборка
минифицирует имена и молча потеряла бы все сохранения.
- ISaveSection, SaveManager, IStorage + MemoryStorage
- CocosStorage: единственный файл модуля, знающий про движок;
при отказе хранилища уходит в память, а не роняет игру
- StatsSection: сыграно/побед/поражений/ничьих, текущая и лучшая серия
- терпимая загрузка: битый JSON, чужая версия, сломанная секция
или отвалившееся хранилище не ломают остальное
- проверка чистоты расширена на modules/ со списком адаптеров
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- PlayerView: проекция состояния, в которой боту видно только то,
что видно живому игроку (своя рука, стол, размеры чужих рук, счётчики)
- BasicBot: политика из GAMEPLAY.md, полностью детерминированная
- Match: обёртка с состоянием и подпиской на события (её же возьмёт UI)
- runMatch: прогон матча без движка и без таймеров
- soak-тесты: 3500 полных матчей (боты и случайные легальные ходы,
2-4 игрока), инварианты состояния проверяются после каждого действия
- npm run stats:bot — распределение исходов самоигры
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Детерминированный конечный автомат: apply(state, action) -> {state, events}.
Весь набор правил из GAMEPLAY.md проверяется headless-тестами.
- карты: Suit/Rank/Card (флайвейт, 36 штук), колода
- GameState + клонирование и сериализация в JSON
- действия (attack/defend/take/pass) и события для слоя отображения
- раздача, выбор первого атакующего, лимиты подкидывания,
взятие карт, добор, выбывание игроков, определение дурака
- 64 теста, strict typecheck
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- docs/PLAN.md — план по этапам, обновляется по мере работы
- docs/TECHNICAL.md — слои, карта папок, контракты, рабочие процессы
- docs/DECISIONS.md — журнал решений, принятых самостоятельно
- разрешение 1080x1920, fit-width (портрет, 4:3..21:9)
- каркас assets/scripts (core / modules / app / ui)
- headless-тесты на vitest + strict typecheck + защита чистоты core
- Rng: детерминированный генератор и перемешивание
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>