From 83fd1b74d1bd271d3449adb9be0c108697f4c31a Mon Sep 17 00:00:00 2001 From: MpakobecEx Date: Tue, 20 Oct 2026 21:12:37 +0500 Subject: [PATCH] Base version: design docs, card catalog, implementation checklist --- .gitattributes | 31 ++ .gitignore | 56 ++++ DEVELOPMENT_CHECKLIST.md | 620 ++++++++++++++++++++++++++++++++++++ IMPLEMENTATION_CHECKLIST.md | 283 ++++++++++++++++ content/cards-mvp.md | 201 ++++++++++++ plan.md | 283 ++++++++++++++++ 6 files changed, 1474 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 DEVELOPMENT_CHECKLIST.md create mode 100644 IMPLEMENTATION_CHECKLIST.md create mode 100644 content/cards-mvp.md create mode 100644 plan.md diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..f14c6ae --- /dev/null +++ b/.gitattributes @@ -0,0 +1,31 @@ +* text=auto eol=lf + +# Windows-скрипты и solution — CRLF +*.sln text eol=crlf +*.csproj text eol=crlf +*.bat text eol=crlf +*.cmd text eol=crlf +*.ps1 text eol=crlf + +# Unity YAML — трёхсторонний merge +*.unity merge=unityyamlmerge eol=lf +*.prefab merge=unityyamlmerge eol=lf +*.asset merge=unityyamlmerge eol=lf +*.mat merge=unityyamlmerge eol=lf +*.anim merge=unityyamlmerge eol=lf +*.controller merge=unityyamlmerge eol=lf + +# Бинарные +*.png binary +*.jpg binary +*.jpeg binary +*.psd binary +*.tga binary +*.wav binary +*.mp3 binary +*.ogg binary +*.dll binary +*.exe binary +*.zip binary +*.7z binary +*.fbx binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e2e5d4f --- /dev/null +++ b/.gitignore @@ -0,0 +1,56 @@ +# --- Секреты --- +.env +.env.* +!.env.example +appsettings.Production.json +secrets.json + +# --- .NET --- +bin/ +obj/ +[Dd]ebug/ +[Rr]elease/ +x64/ +x86/ +publish/ +*.user +*.suo +*.userprefs +*.cache + +# --- Unity (только папка client) --- +client/[Ll]ibrary/ +client/[Tt]emp/ +client/[Oo]bj/ +client/[Bb]uild/ +client/[Bb]uilds/ +client/[Ll]ogs/ +client/[Uu]ser[Ss]ettings/ +client/[Mm]emoryCaptures/ +client/[Rr]ecordings/ +client/*.csproj +client/*.sln +client/*.unityproj +client/*.pidb +client/*.booproj +client/*.svd +client/*.pdb +client/*.mdb +client/*.opendb +client/*.VC.db +client/.vs/ +client/.idea/ +client/Assets/AddressableAssetsData/*.bin +client/Assets/StreamingAssets/aa/* + +# --- Unity прочее --- +*.unitypackage +crashlytics-build.properties +sysinfo.txt + +# --- ОС и IDE --- +.DS_Store +Thumbs.db +.vs/ +.idea/ +*.swp diff --git a/DEVELOPMENT_CHECKLIST.md b/DEVELOPMENT_CHECKLIST.md new file mode 100644 index 0000000..bba87f9 --- /dev/null +++ b/DEVELOPMENT_CHECKLIST.md @@ -0,0 +1,620 @@ +# CF!! Vanguard Re:Master — чеклист разработки + +> Составлен на основе `plan.md` (283 строки). Версия 1.2 — темп сетов учтён, экономика пыли пересчитана под цикл 1–2 месяца. +> Легенда приоритетов: **P0** — без этого нет продукта, **P1** — нужно к релизу, **P2** — после релиза. +> Метки объёма: `[MVP]` — первая играбельная версия, `[v1]` — публичный релиз, `[later]` — развитие. + +--- + +## 0.0 Зафиксированные решения (ADR) + +| # | Вопрос | Решение | Следствия | +|---|---|---|---| +| 1 | Сетевой транспорт | **Свой протокол: TCP/WebSocket + MessagePack** | `Netcode for GameObjects` **исключён** из стека. Общие DTO в `/shared`, генератор кода для клиента. Сервер — чистый .NET 8. | +| 2 | Объём MVP | **1-й бустер + 2 триальные колоды** | MVP = ядро правил + один сет (94 карты) + две готовые колоды + коллекция + **полный декбилдер** + бустеры. Турниры, команды, территории, косметика, баттлпасс, Draft — после MVP. | +| 3 | Описание карт | **C#-скрипты + статы в YAML** | 1 карта = 1 файл-скрипт + запись в YAML-сете. Контент-мейкер без C# не добавляет карты — учитываем в планировании. | +| 4 | Игровой сервер | **Microsoft Orleans** | GeekServer **исключён**. 1 матч = 1 grain, состояние в Redis/PostgreSQL через persistence-провайдер. | +| 5 | Платформа | **ПК (Windows) first**, мобилка после релиза | MVP-клиент — Windows + WPF-лаунчер. Тач-ввод, Addressables-доставка и сторы — веха после v1. | +| 6 | Сайд-дек / BO3 | **Убрать сайд-дек, BO1** | §13 плана правим: сайд-дек и «main + side ≤ 4 копий» удаляются. Валидация колоды проще. | +| 7 | Контент MVP | **TD01 «Blaster Blade», TD02 «Dragonic Overlord», BT01 «Descent of the King of Knights»** | Это **базовая эра 2011 г.**, а не D-серия. Меняет список механик, зон и триггеров. | +| 8 | Декбилдер | **Полный** | Поиск/фильтры, валидация, сохранение, шаринг — всё в MVP. | +| 9 | Онбординг | **Туториал → выбор триал-колоды + 5 токенов** | **1 токен = 1 бустер.** Токены универсальные, не только Draft. Новичок сразу собирает колоду из бустера. | +| 10 | Арты | **Официальные** | Юридический риск максимальный. Нужен план на takedown и правило «не храним арты на своём CDN без нужды». | +| 11 | Ride Deck / Persona Ride | **Нет в Classic** | Подтверждает базовую эру. Движок проектируется без них. | +| 12 | Локализация | **EN + RU** | Оба языка с первого дня, не «потом переведём». | +| 13 | Листы TD01/TD02 | **Получены** (вики + архив) | 50 карт каждая, 17/14/12/7 по грейдам. См. `content/cards-mvp.md`. | +| 14 | Бан-лист | **Банов в MVP нет** | Barcgal играется. Механизм `CardLegality` проектируем, но список пустой. | +| 15 | Параллельные арты | **Отдельные принты в коллекции**, привязка к базовому номеру карты (S02 → BT01/002) | Коллекция различает арты и **не имеет лимитов**. Лимит 4 копий — только при сборке колоды. | +| 16 | Название формата | **Classic** вместо «Standard» | Однозначно отделяет базовую эру от современного Standard. Переименовать в плане, коде и UI. | +| 17 | Лимиты коллекции | **Нет** — любое число копий и артов | 4 копии по имени — правило **декбилдера**, а не инвентаря. Duplicate protection — мягкий приоритет, а не жёсткий кап. | +| 18 | Правило дублей | Ключ — **`printingId`** (номер + арт), порог **4**; замена **внутри редкости**; иначе карта в коллекцию | Дубль разбирается в пыль **вручную** игроком, не автоматически. | +| 19 | Веса слота R+ | **R 70 % / RR 22 % / RRR 7 % / SP 1 %** | ~21 R, ~6,6 RR, ~2,1 RRR, ~0,3 SP на коробку из 30 бустеров. Крутится в `pack_odds.yaml`. | +| 20 | Пыль | **Сквозная валюта крафта**, не привязана к сету | Накопленное тратится на карты нового сета в день выхода. При цикле 1–2 месяца инфляция в норме (13–27 % сета за цикл). | +| 21 | Темп сетов | **1–2 месяца на сет** (80 карт) | Требует 1,3–1,8 карты в день **через приёмку**. Контент, а не движок, становится критическим путём проекта. | + +### 0.0.1 Контент MVP — точный объём + +Проверено по официальному карт-листу: + +| Продукт | Уникальных карт | Принтов | Особенности | +|---|---|---|---| +| **BT01** Descent of the King of Knights | 80 (BT01/001–080) | 92 | +12 параллельных артов (BT01/S01–S12) | +| **TD01** Blaster Blade (Royal Paladin) | 16 | 16 | 7 карт **только** в TD01 (Brigitte, Bors, Tristan, Starlight Unicorn, Morgana, Stardust Trumpeter, Epona) | +| **TD02** Dragonic Overlord (Kagero) | 16 | 16 | 7 карт **только** в TD02 (Goku, Yaksha, Berserk Dragon, Aermo, Joka, Undeux, Rakshasa) | +| **Итого** | **94 уникальные карты** | **124 принта** | 10 кланов | + +**Полный каталог с триггерами, редкостями и точными листами триал-дек — `content/cards-mvp.md`.** + +Новые факты из вики: +- Редкости BT01: **RRR ×8, RR ×12, R ×20, C ×40** + **SP ×12**. +- В сете ровно **по 4 триггера каждого типа**: Critical, Draw, Stand, Heal. +- **Стартовый вэнгард — grade 0 без триггера** (Stardust Trumpeter в TD01, Undeux в TD02). +- **Draw-триггеры имеют 5000 щита, остальные триггеры — 10000.** Даёт бесплатный инвариант для валидатора. + +**Кланы BT01:** Royal Paladin, Oracle Think Tank, Kagero, Nubatama, Tachikaze, Nova Grappler, Spike Brothers, Dark Irregulars, Granblue, Megacolony. + +**Два вывода для модели данных:** +1. **`printingId` ≠ `nameId`.** BT01/002EN, TD01/005EN и BT01/S02EN — один и тот же «Blaster Blade». В коллекции это три разные записи без ограничений по количеству, а в деке лимит 4 копии **по имени** — иначе игрок соберёт 4 обычных + 4 параллельных = 8. +2. **Параллельные арты уже есть в первом сете** — вопрос §26 плана «нужны ли альтернативные арты» закрыт. 12 штук, каждый привязан к базовому номеру (`S02 → BT01/002`). Коллекция хранит арты раздельно, лимиты — по базовой карте. + +### 0.0.2 Дельта правил: план писался под современный Vanguard + +Это главная содержательная правка. Для базовой эры §4 плана даёт лишнее и не даёт нужного. + +**Убрать из MVP:** +- Триггеры **Front** и **Over** — появились в V- и D-эре. В этом сете только **Critical, Draw, Stand, Heal**. +- Зоны **G Zone** и **Bind** — в 2011 их нет. +- Состояние **Lock** — появилось в 2013 (Link Joker). +- Понятие **Nation** — в 2011 только **Clan**. Поле `nation` оставить пустым/на будущее. +- **Ride Deck, Persona Ride** — подтверждено вами. +- **Limit Break, Break Ride, Legion, Stride, Imaginary Gift** — всё после MVP. + +**Добавить в MVP (в плане отсутствует):** +- **Маллиган** в начале партии. В плане его нет вообще. +- **Стартовый вэнгард**: игрок выбирает grade 0 из колоды и выставляет его в начале игры. +- **Урон лицом вниз.** Damage-карты лежат рубашкой вверх; **Counter Blast** переворачивает одну лицом вверх. Значит, у damage-карты есть флаг `faceUp`, а не только счётчик. Плюс эффекты «turn all cards in your damage zone face up» (Embodiment of Victory, Aleph). +- **Twin Drive!!** — количество drive-чеков это модифицируемое свойство вэнгарда, а не константа: Dragonic Overlord умеет его терять. +- **Событие «атака попала»** отдельно от «атака объявлена»: половина карт сета триггерится на hit. +- **Boost как событие** с источником и целью: «when this unit boosts…» — массовая формулировка в сете. +- **События фаз**: начало main phase, начало close step боя, начало end phase — на них завязаны конкретные карты. +- **Sentinel** как ключевое слово с лимитом **4 карты в колоде** (не 4 копии по имени). +- **Heal-триггер** с лимитом **4 карты в колоде**. +- **Restraint** (не может атаковать) и «cannot [Stand] during next stand phase». +- **Атака в задний ряд той же колонки** (Wyvern Strike, Tejas) — нестандартная цель атаки. + +**Ловушка контента — решена.** В официальном карт-листе тип триггера не виден в тексте (только иконка на арте), но в вики-таблицах есть отдельная колонка «Trigger». Все 16 триггеров BT01 и раскладка по триал-декам уже выгружены в `content/cards-mvp.md`. OCR не нужен. + +**Что это дало проекту:** минус одна параллельная сетевая технология, минус один серверный фреймворк, минус неопределённость по объёму контента. Оценка MVP сократилась с «3–4 года» до реалистичного среза. + +--- + +## 0. Результаты анализа плана + +### 0.1 Что в плане хорошо +- Есть ясная базовая установка: авторитарный сервер, тонкий клиент, скрытая информация. Это правильный фундамент для ККИ. +- Экономика просчитана и внутренне непротиворечива: баттлпасс 2 500 + 21 000 + 35 000 = **58 500 XP**, «15 бустеров каждый 5-й уровень» = 15 бустеров. Совпадает. +- Разделение мета-сервера и игрового сервера, Redis для сессий, PostgreSQL для durable-данных — здоровая схема. +- Явно зафиксирован отказ от монетизации, голосового чата, ботов — меньше поверхности атаки и меньше работы. +- Есть раздел «что не обговорено» — редкость для дизайн-документов. + +### 0.2 Противоречия и блокеры (решать до кода) + +| # | Где | Проблема | Что делать | +|---|---|---|---| +| 1 | §2 | Игровой сервер — «TCP, MessagePack», но клиент — «Netcode for GameObjects». NGO — это Unity-рантайм (NetworkObject/NetworkBehaviour/Unity Transport). Он **не может** работать против standalone .NET 8 сервера. | ✅ **Решено:** свой протокол TCP/WebSocket + MessagePack, NGO убран. | +| 2 | §2 | «MessagePack / MemoryPack» — не выбрано. MemoryPack быстрее, но генерирует код и хуже дружит с межъязыковыми клиентами. | ✅ **Решено:** MessagePack. | +| 3 | §2 vs §1, §14 | «ScriptableObject (данные карт)» при том, что карты живут в БД, а логика — на сервере. Два источника истины. | SO — только визуал (префаб, арт, звук, VFX) по ключу `cardId`. Статы и текст — только с сервера. | +| 4 | §4 | «Все механики заложены сразу» (Limit Break → Break Ride → Legion → Stride → V → D) при объёме остальных разделов. | ✅ **Решено:** MVP = ядро + базовая D-эра на одном сете. Остальные эры — модули после MVP. | +| 5 | §3 | Редкости «Common/Uncommon/Rare/**Legendary**/RRR» — смешаны две разные таксономии (западная и официальная Vanguard). | Ввести официальную шкалу (C/R/RR/RRR/SP/…), «Legendary» убрать или явно определить как внутренний класс. | +| 6 | §13 | «Сайд-дек 5 карт» — в Vanguard сайд-деков нет, матчи BO1. | ✅ **Решено:** сайд-дек убран, формат BO1. | +| 7 | §21 | Тестовый матч «локальный, без сервера» → второй экземпляр движка правил на клиенте → неизбежное расхождение. | Туториал гонять через тот же серверный движок (локальный инстанс/ offline-режим сервера), либо принять риск и покрыть golden-тестами. | +| 8 | §21 | `TutorialStep`/`TutorialGameManager` — скриптованный туториал, а не «матч». | Решить: скрипт-события поверх реального движка или фейковая сцена. Рекомендую первое. | +| 9 | §2, §24 | Redis — единственное хранилище состояния матча. Потеря/эвикция = потерянный матч. | Redis с AOF + периодический снапшот матча в PostgreSQL + идемпотентное восстановление. | +| 10 | §11 vs §6 | «Casual — матчмейкинг просто по онлайну, без критериев» → новичок против топ-1. | Минимальный скрытый MMR даже в казуале (широкий диапазон), иначе отток новичков. | +| 11 | §24 | Реконнект 0/2/10/30 с, и что дальше — не сказано. | Политика: после N минут отсутствия — авто-поражение/пауза. См. блок 12. | +| 12 | §7 vs весь план | Тайм-контроль есть только в турнирах (35 мин). В обычном матче — нет. | Ввести таймер хода (rope) для всех матчей. | +| 13 | §13 | Нет правил конструирования колоды: размер, лимит копий по имени, лимит Heal-триггеров, Ride Deck (D). | Отдельный артефакт «Deck Construction Rules» на каждый формат. | +| 14 | §4 | В списке механик нет: Sentinel/Perfect Guard, Counter Charge, Soul Charge, Retire, Resist, Quintet Wall, Ultimate Stride, Persona Ride (есть только в D), Imaginary Gift-маркеров как объектов и т. д. | Полная инвентаризация механик по официальным правилам → Rules Reference. | +| 15 | §11 | Draft Run: 10 бустеров → колода. Правила сборки драфт-колоды (размер, триггеры, базовые карты) не заданы. | Определить формат (Sealed vs Draft), размер колоды, выдачу базовых триггеров. | +| 16 | §12 | «Готовые колоды за Gold» — легальны ли они в ранкеде, можно ли их менять. | Зафиксировать: это продукт-набор карт, колода собирается декбилдером. | +| 17 | §16 | Права модераторов описаны, инструментов — нет. | Нужна веб-админ-панель (блок 23). | +| 18 | весь план | Нет RNG-аудита (шаффл, drive/damage check) и реплеев. Без этого невозможны разбор жалоб, баланс и баг-репорты. | Server-side seeded RNG + журнал интентов + реплей (блок 8). | +| 19 | §10 | «Сброс 1-го числа» — таймзона не указана. | Зафиксировать UTC + отображать локально. | +| 20 | §15 | «Гайды… если нет лайков за неделю — удаляется» — неясно, чей лайк и зачем. | Переформулировать (например, авто-скрытие в «непопулярное»). | +| 21 | §4 | Триггеры перечислены как Critical/Draw/Stand/Heal/**Front/Over** — двух последних в 2011 не существует. | Только Critical, Draw, Stand, Heal (см. §0.0.2). | +| 22 | §4, §24 | Нет **маллигана**, **стартового вэнгарда** и **урона лицом вниз**. | Добавлено в блок 6. | +| 23 | §3 | Один `CardData.id` — не различает принт и имя карты. | Ввести `printingId` + `nameId` (см. §0.0.1). | + +### 0.3 Крупные пробелы, которых в плане нет вообще +1. **Реплеи и детерминизм** — без них нет ни модерации, ни балансной аналитики, ни отладки. +2. **Админ-панель** (баны, муты, тикеты, бан-листы, выдача валюты, просмотр логов). +3. **Античит и мультиаккаунты** — только упомянуто в «не обговорено». +4. **Тайм-контроль обычных матчей.** +5. **Правила конструирования колод по форматам.** +6. **Пайплайн контента** — откуда берутся карты, как валидируются, как выкатываются сеты. +7. **Версионирование протокола и совместимость клиент/сервер.** +8. **Rate limiting / защита API / DDoS.** +9. **Наблюдаемость** (метрики, трейсы, алерты) — упомянута только как «мониторинг». +10. **Юридический риск** — фан-проект с чужими артами; нужен план на случай takedown и правила по ассетам. + +### 0.4 Главные риски +- **R1. Правила.** Vanguard — это не «магия с реаргардами». Check Timing, одновременные триггеры, «may»-эффекты, порядок разрешения — самая дорогая часть проекта. Недооценка = переписывание ядра. +- **R2. Объём.** Раздел «все механики сразу» + 6 очередей + турниры + команды + территории — это 3–4 года для команды из 1–3 человек. +- **R3. Контент.** Карты надо не только описать данными, но и **заскриптовать**. 1 карта со сложным эффектом = 0,5–2 дня. +- **R4. Пустой онлайн.** 6 ранкед-очередей при маленькой аудитории = никто ни с кем не играет. Нужен план консолидации очередей. +- **R5. Юридический.** Bushiroad может потребовать прекратить использование артов/названий. + +--- + +## 1. Дорожная карта (вехи и гейты) + +| Веха | Содержание | Гейт (Definition of Done) | +|---|---|---| +| **M0. Решения и каркас** | ADR (уже готовы), Rules Reference v0, схема карт, репозиторий, docker, auth | `docker compose up` поднимает API + PostgreSQL + Redis + Orleans silo; логин/регистрация работают | +| **M1. Ядро правил (headless)** | Состояние, зоны, фазы, боевой цикл, Check Timing, RNG, реплеи | **Все 94 карты** (BT01 + TD01 + TD02) доигрывают матч в консольном раннере; реплей воспроизводится бит-в-бит | +| **M2. Сетевой матч** | Протокол, Orleans grain, скрытие информации, Unity-клиент с серым UI, реконнект | Два игрока по сети играют полный матч триал-колодами; убийство клиента на любом шаге восстанавливается | +| **M3. MVP-продукт** | Пайплайн контента, коллекция, минимальный декбилдер, триал-колоды, бустеры, Gold, XP, 1–2 очереди, базовый туториал | Новый игрок: логин → триал-дека → матч → бустер → собрал колоду из своих карт → сыграл ранкед | +| **M4. Мета и соцслой `[v1]`** | Ранкед/MMR/сезоны, баттлпасс, квесты, достижения, профиль, друзья, чат, лаунчер | Сезон отрабатывает полный цикл: старт → матчи → награды → сброс | +| **M5. Турниры и команды `[v1]`** | Турнирный движок, 3×3, снимки колод, архив, команды, территории, админка | Турнир на 8 человек проходит автоматически от регистрации до публикации архива | +| **M6. Мобилка и полировка `[later]`** | Тач-UI, Addressables-доставка, производительность, сторы, полный туториал | Билд проходит чеклист релиза (блок 26) | + +> Рекомендация: **не начинать с UI**. Серый Unity-клиент на M2 — нормально. Красота — на M6. + +--- + +## 2. Блоки + +### Блок 1. Проектные артефакты и решения `[MVP]` + +- [x] **P0** ADR-0001: сетевой транспорт — **TCP/WebSocket + MessagePack**, NGO исключён. +- [x] **P0** ADR-0002: игровой сервер — **Microsoft Orleans**, GeekServer исключён. +- [x] **P0** ADR-0003: способности карт — **C#-скрипты + статы в YAML**. +- [x] **P0** ADR-0004: объём MVP — **1-й бустер + 2 триальные колоды**. +- [ ] **P0** ADR-0005: архитектура презентации на клиенте (серверный event-stream → очередь анимаций). +- [ ] **P0** ADR-0006: схема идентификаторов карт и версионирование контента. +- [ ] **P0** ADR-0007: где живёт истина по правилам (сервер) и как клиент валидирует ввод «мягко». +- [ ] **P0** Написать **Rules Reference v0**: зоны, фазы, шаги боя, окна Check Timing, порядок разрешения. Источник — официальные Comprehensive Rules, с указанием версии. +- [ ] **P0** Инвентаризация механик: таблица «механика → эра → нужна ли в MVP → модуль». +- [ ] **P0** Правила конструирования колоды: ровно 50 карт, ≤4 копий по имени, ≤4 Sentinel, ≤4 Heal. Ride Deck отсутствует. +- [ ] **P1** Глоссарий-артефакт: ключевые слова RU/EN + плейсхолдеры (`{cb}`, `{sb}`, `{cc}`, `{sc}`) с точным описанием подстановки. +- [ ] **P1** Дизайн-док по UX-флоу: главное меню → коллекция → декбилдер → очередь → матч → результат. +- [ ] **P1** Реестр рисков с владельцами (R1–R5). +- [ ] **P1** Правила ассетов: что можно использовать, что рисуем сами, что плейсхолдер. План на случай takedown. + +### Блок 2. Инфраструктура, окружения, CI/CD `[MVP]` + +- [ ] **P0** Структура репозитория: `/server` (solution), `/client` (Unity), `/launcher`, `/shared` (контракты), `/content` (данные карт), `/docs`, `/ops`. +- [ ] **P0** `Directory.Build.props` + `global.json` (.NET 8, `LangVersion`, nullable, analyzers). +- [ ] **P0** `docker-compose.yml`: postgres:16-alpine, redis:7-alpine (+ AOF), nginx, meta-api (ASP.NET Core), game-silo (Orleans). +- [ ] **P0** Сети `backend`/`frontend`, volumes `postgres_data`/`redis_data`, `.env.example` без секретов. +- [ ] **P0** Healthcheck-эндпоинты и `depends_on: condition: service_healthy`. +- [ ] **P0** CI: сборка, `dotnet test`, линтеры, сборка Unity-клиента (хотя бы ночью), сборка лаунчера. +- [ ] **P0** Три окружения: `dev` (локально), `staging`, `prod`; разные секреты, одинаковые образы. +- [ ] **P1** Миграции EF Core: только через `dotnet ef migrations`, применять в CI-шаге, а не руками. +- [ ] **P1** Версионирование образов по git SHA, теги `latest` только в dev. +- [ ] **P1** Наблюдаемость: Serilog (структурные логи) + OpenTelemetry (трейсы/метрики) + Prometheus/Grafana. +- [ ] **P1** Алерты: падение сервиса, рост 5xx, лаг матч-акторов, заполнение диска. +- [ ] **P1** Бэкапы Postgres: ежедневный полный + WAL-архив, проверка восстановления раз в месяц. +- [ ] **P1** Секреты: не в git, в `.env` на сервере / Vault, ротация паролей БД. +- [ ] **P2** Load-test сценарий (k6) на 500 одновременных матчей. + +### Блок 3. Аккаунты, аутентификация, безопасность `[MVP]` + +- [ ] **P0** Модель `User`: id (uuid), login, email (опц.), password hash (BCrypt, cost ≥ 12), created_at, status, roles. +- [ ] **P0** Регистрация/логин/refresh. JWT: короткий access (15 мин) + refresh (30 дней) в Redis с ротацией. +- [ ] **P0** Отдельный короткоживущий **game ticket** для подключения к игровому серверу (не переиспользовать access-токен). +- [ ] **P0** Единая авторизация для HTTP API, SignalR-хаба и TCP-игрового соединения. +- [ ] **P0** Валидация ввода, нормализация логина, защита от перебора (rate limit + lockout). +- [ ] **P0** Роли и политики: `Player`, `Moderator`, `Admin`; авторизация на уровне эндпоинтов **и** сервисов. +- [ ] **P1** Восстановление пароля (email) или явное решение «без email» (тогда — только смена по старому паролю). +- [ ] **P1** 2FA (TOTP) для админов — обязательно, для игроков — опционально. +- [ ] **P1** Привязка устройств/сессий, список активных сессий, «выйти везде». +- [ ] **P1** Анти-мультиаккаунт: эвристики по IP/устройству, флаги для админа (не автобан). +- [ ] **P1** Аудит-лог действий админов/модераторов (кто, что, когда, над кем). +- [ ] **P2** OAuth (Discord/Steam) — если нужен вход без пароля. + +### Блок 4. Данные карт и контент-пайплайн `[MVP]` + +- [x] **P0** Полный каталог на 94 карты / 124 принта составлен — `content/cards-mvp.md` (BT01 + TD01 + TD02, с триггерами и редкостями). +- [ ] **P0** Точные листы TD01 и TD02 **получены** (50 карт, 17/14/12/7 по грейдам) — см. `content/cards-mvp.md`. Сверить с официальным продуктом при первой возможности. +- [ ] **P1** `CardLegality` с датами действия — таблица проектируется сразу, но в MVP пустая (банов нет). +- [ ] **P1** Зафиксировать раскладку редкостей BT01 (C / R / RR / RRR / SP) — на ней держатся бустер и duplicate protection. +- [ ] **P0** Разделить `printingId` (`BT01/002EN`, `TD01/005EN`, `BT01/S02EN`) и `nameId` (стабильный слаг имени). Лимит копий и поиск «по имени» считаются по `nameId`, арт и цена — по `printingId`. +- [ ] **P0** `CardData` под базовую эру: `printingId`, `nameId`, `setId` (BT01/TD01/TD02), `cardType` (Normal Unit | Trigger Unit), `grade`, `power`, `shield`, `critical`, `triggerType` (null | Critical | Draw | Stand | Heal), `clan`, `nation` (nullable, на будущее), `rarity`, `isParallel` + `artVariant` (для S01–S12), `isSentinel`, `isRestraint`, `name_en/ru`, `text_en/ru`, `flavor_en/ru`, `imageKey`, `artist`, `errataVersion`. +- [ ] **P0** **Не выводить `shield` из грейда или триггера** — в сете встречаются триггерные юниты и с 5000, и с 10000 щита. Значение всегда из данных. +- [ ] **P0** Триггеры и редкости выгружены из вики-таблиц — см. `content/cards-mvp.md`. Сверить выборочно 8–10 карт с иконками на артах. +- [ ] **P0** Добрать силу и щит для 14 карт, которых нет в BT01 (только TD01/TD02) — с карточных страниц. +- [ ] **P0** Нормализация текстов: в официальной БД есть опечатки (`Soul-Chage`, `begininng`, `cannnot`, `Own(VC/RC)`). Хранить `text_source` (как в источнике) и `text_normalized` (после правки), показывать игроку нормализованный. +- [ ] **P0** Структура способностей: `AbilityData { id, type, triggerEvent, conditionIds[], costIds[], effectIds[], targetSpec, optional, source }` + ссылки на скрипт. +- [ ] **P0** Разделить «статы» (в БД, сервер) и «визуал» (Addressables-ключ на клиенте). Клиент получает метаданные по API, арт — по `cardId`. +- [ ] **P0** Источник истины для контента: YAML/CSV в git (`/content/sets/BT01/…`, `/content/sets/TD01/…`), а не ручное редактирование БД. +- [ ] **P1** Валидатор контента: обязательные поля, корректность `legionMateId`, ссылки на существующие скрипты, дубликаты `cardId`, парность `_ru/_en`. +- [ ] **P1** Импортёр: `content → БД` (идемпотентный, с версией сета и откатом). +- [ ] **P1** Экспортёр для клиента: каталог карт + манифест Addressables-ключей. +- [ ] **P1** Легальность: таблица `CardLegality { cardId, format, eraId, banned, limit, condition }` с датами действия. +- [ ] **P2** Версионирование сетов и «снимок меты» на дату (для архива турниров). + +### Блок 5. Локализация `[MVP]` + +- [ ] **P0** Unity Localization: String Tables `en`/`ru`, ключи по схеме `ui.menu.play`, runtime-переключение без перезагрузки сцены. +- [ ] **P0** Сервер: параметр `lang` в запросах карт/глоссария, fallback `en`. +- [ ] **P1** Текстовый шаблонизатор карт: `{cb}`, `{sb}`, `{cc}`, `{sc}` → локализованный термин из глоссария; поддержка ``, ``, переносов. +- [ ] **P1** Проверка шрифтов TMP: кириллица, fallback-атлас, авто-фит текста на карте. +- [ ] **P1** Псевдолокализация (длинные строки) для проверки вёрстки. +- [ ] **P1** Лаунчер: RESX `ru`/`en`, общий глоссарий с сервером (единый CSV как источник). +- [ ] **P2** Экспорт строк для внешних переводчиков (CSV/XLIFF) и импорт обратно. + +### Блок 6. Игровое ядро: состояние, фазы, базовые действия `[MVP]` — **критический путь** + +- [ ] **P0** `GameState`: игроки, зоны, флаги хода, счётчики, RNG-состояние, версия/sequence. +- [ ] **P0** Зоны MVP: Vanguard Circle, Rear-Guard (3 колонки × 2 ряда; VG занимает центр переднего ряда, остальные 5 слотов — RC), Guardian, Soul, Damage, Drop, Trigger, Deck, Hand. **Без** G Zone, Bind и Ride Deck — в этой эре их нет. +- [ ] **P0** Модель юнита: `instanceId`, `printingId`, состояние (Stand/Rest), модификаторы силы/крита, временные эффекты с длительностью. **Без** Lock — появился позже. +- [ ] **P0** Модель Damage: карты лежат **лицом вниз**. Флаг `faceUp` у каждой карты, а не просто счётчик урона. Counter Blast = перевернуть одну лицом вверх; отдельные эффекты переворачивают весь damage-зон. +- [ ] **P0** Фазы: Mulligan → Stand → Draw → Ride → Main → Battle → End. Явные шаги внутри Battle: attack declare → guard → drive check → damage check → close step. +- [ ] **P0** Старт партии: выбор стартового grade 0 из колоды → выставление в Vanguard Circle → **маллиган** (замена любого числа карт из руки) → начало первого хода. +- [ ] **P0** Базовые действия: Ride, Call, Move, Attack, Intercept, Guard, Boost, Drive Check, Damage Check, Stand/Rest, Retire, Draw, Discard, Shuffle. +- [ ] **P0** `driveCheckCount` — изменяемое свойство вэнгарда (Twin Drive!! = 2), а не константа: Dragonic Overlord умеет его терять. +- [ ] **P0** Результат боя как явное событие: `Hit` / `Guarded` / `Missed`. Триггеры «when this unit's attack hits» — массовая формулировка сета. +- [ ] **P0** Boost как событие с источником и целью (нужно для «when this unit boosts…» и для модификаторов «boosted unit gets +N»). +- [ ] **P0** Counter Blast / Soul Blast / Soul Charge как первоклассные операции (с проверкой доступности). Counter Charge — позже, в сете его нет. +- [ ] **P0** Валидация действий: сервер отвечает `Rejected(reason)` на нелегальный интент, состояние не меняется. +- [ ] **P0** Машина состояний «чей сейчас ввод»: явный список легальных интентов для каждого игрока (нужен и для UI, и для античита). +- [ ] **P0** Юнит-тесты на каждое правило из Rules Reference (не на карты — на механику). +- [ ] **P1** Тайм-контроль хода (rope): 90 с на ход, 5 мин банк, авто-end. +- [ ] **P1** Обработка «нет легальных действий» (принудительный пас), защита от дедлоков. +- [ ] **P1** Лимит глубины рекурсии эффектов и защита от бесконечных циклов. + +### Блок 7. Игровое ядро: способности и Check Timing `[MVP]` — **критический путь** + +- [ ] **P0** Шина событий в `GameActor`: типизированные события (`UnitRode`, `UnitCalled`, `AttackDeclared`, `TriggerRevealed`, `DamageTaken`, `UnitRetired`, …). +- [ ] **P0** Три обработчика: `Continuous` (пересчёт слоёв модификаторов), `Activated` (по действию игрока), `Triggered` (по событию). +- [ ] **P0** **Check Timing**: собрать сработавшие триггеры → отсортировать по правилам (обязательные/опциональные, владелец, порядок выбора) → разрешить по одному → повторить, пока очередь не пуста. +- [ ] **P0** Модель стоимости (`costs[]`): CB, SB, discard, rest self, retire self, put into soul, return to deck, «pay if you want» — с откатом, если оплата невозможна. +- [ ] **P0** Модель целей (`targetSpec`): авто-выбор по фильтру, выбор игроком, «до N», «ровно N», валидация на сервере. +- [ ] **P0** Модификаторы силы/крита как слои с длительностью (до конца боя / хода / пока на поле) — без ручного пересчёта в скриптах карт. +- [ ] **P0** Golden-тесты Check Timing: сценарии с 3+ одновременными триггерами и порядком разрешения. +- [ ] **P1** «Resist» и запрет выбора — как фильтр в `targetSpec`, а не как спецкод в картах. +- [ ] **P1** Логирование цепочки разрешения (для отладки и реплеев): «событие → сработавшие → порядок → результат». +- [ ] **P2** Инструмент-визуализатор цепочки для отладки карт. + +### Блок 8. Детерминизм, RNG, реплеи `[MVP]` — **критический путь** + +- [ ] **P0** Серверный PRNG с явным seed на матч; шаффл детерминированный (Fisher–Yates поверх seeded-потока). +- [ ] **P0** Разделить потоки RNG: шаффл / drive check / damage check / случайные эффекты карт — независимые счётчики (иначе правка одной карты ломает реплеи). +- [ ] **P0** Журнал интентов: `[seq, playerId, intent, timestamp]` + итоговый seed → матч воспроизводим. +- [ ] **P0** Реплей-раннер: `GameState + intents + seed → финальное состояние`, сравнение хешей. В CI на каждый коммит ядра. +- [ ] **P1** Хранение реплеев: последние N матчей в горячем хранилище, остальное — в S3/файлах с сжатием. +- [ ] **P1** Проверяемая честность бустеров: публикация хеша сида до открытия, раскрытие после (опционально, но дёшево и снимает много жалоб). +- [ ] **P1** Лог «почему действие отклонено» для саппорта. +- [ ] **P2** Просмотр реплея в клиенте (шаг вперёд/назад) — сильный инструмент для комьюнити. + +### Блок 9. Механики по эрам `[MVP → later]` + +Порядок реализации = порядок возрастания сложности, не порядок выхода сетов. + +> **Правило MVP:** в первую версию входят **только** механики из 94 карт MVP. Сначала матрица «карта → механика», из неё — список модулей. Ванильных карт без текста много, они не тянут за собой новых модулей. + +- [ ] **P0** Ядро (Ride/Call/Boost/Intercept/Guard/Drive/Damage, Stand/Rest, Counter Blast, Soul Blast, Soul Charge, Retire). +- [ ] **P0** Триггеры: **только Critical, Draw, Stand, Heal**. Front и Over появятся в V/D-эре — не реализуем сейчас. +- [ ] **P0** Sentinel (Perfect Guard): нуллификация атаки, лимит **4 карты в колоде** — отдельно от лимита копий по имени. +- [ ] **P0** Heal-триггер: лимит **4 карты в колоде**, отдельная проверка в валидаторе. +- [ ] **P0** Ключевые слова и ограничения: Restraint (не может атаковать), «cannot [Stand] during next stand phase», «cannot be hit», «cannot boost this unit». +- [ ] **P0** Атака в задний ряд той же колонки (Wyvern Strike, Tejas) — нестандартная цель атаки, ломает наивную модель «атакую только передний ряд». +- [ ] **P1** Каждая механика — отдельный модуль с фича-флагом по формату (включение/выключение на уровне матча). +- [ ] **P2** `[later]` Limit Break (4+ урона) + Break Ride (+10000, тот же клан). +- [ ] **P2** `[later]` Legion (Leader + Mate, Seek Mate). +- [ ] **P2** `[later]` G-эра: Stride, Heart Card, G Guardian, G Zone. +- [ ] **P2** `[later]` V-эра: Imaginary Gift Force/Accel/Protect, Front-триггер. +- [ ] **P2** `[later]` D-эра: Ride Deck, Persona Ride, Over Trigger. +- [ ] **P1** Тесты: один и тот же набор базовых тестов прогоняется для каждого формата. + +### Блок 10. Скрипты карт и тестирование контента `[MVP]` + +- [ ] **P0** Шаблон новой карты для зафиксированного формата (**C#-скрипт + запись в YAML**): 1 файл = 1 карта, обязательные `// rule:` комментарии со ссылкой на официальный текст. +- [ ] **P0** Глоссарий примитивов, собранный из фактических текстов сета: `Draw`, `Retire` (одна/все/до N), `Search→call`, `Search→hand`, `Search→ride`, `Stand` (себя/выбранного/всех), `Soul-Charge`, `Soul-Blast`, `Counter-Blast`, `Flip damage face-up`, `Put into soul`, `Deck→drop`, `Return to deck + shuffle`, `Hand→deck`, `Discard as cost`, `Opponent discards`, `Look top & reorder`, `Power/Crit modifier`, `Gain/lose keyword`, `Attack back row`, `Cannot stand`, `Cannot be hit`. Всё остальное — комбинации этих примитивов. +- [ ] **P0** Хелперы «поиск по имени» (`nameId`) — в сете много карт, которые ищут/проверяют конкретные имена (Blaster Blade, Llew, Flogal, Tejas, Blue Dust, King of Sword). +- [ ] **P0** План скриптования: сначала обе триал-колоды (играбельно сразу), затем остальные карты BT01 — от простых к сложным. Ванильных карт с «–» в сете много — они закрываются одной строкой данных. +- [ ] **P0** Пропускная способность под темп **1–2 месяца на сет**: 80 карт / 45–60 дней = **1,3–1,8 карты в день**, каждый день, без пауз. Это узкое место проекта, а не движок. Варианты: второй контент-мейкер или буфер из двух готовых сетов до публичного запуска. +- [ ] **P1** Мерить «карты, прошедшие приёмку», а не «карты, написанные»: скрипт + тест + RU/EN + воспроизводимый реплей. +- [ ] **P0** Базовые «кирпичики» (helpers): `DrawCard`, `RetireUnit`, `AddPower`, `ChooseTarget`, `LookTopDeck`, `CallFromDeck` — чтобы 80 % карт были однострочными. +- [ ] **P1** Тест-харнесс для карт: «дано состояние → сыграй карту → ожидай состояние» на DSL/YAML. +- [ ] **P1** Чеклист приёмки карты: текст совпадает с официальным, все ветки эффекта покрыты тестом, RU/EN синхронны, работает в реплее. +- [ ] **P1** Метрика покрытия: сколько карт из сета заскриптовано/протестировано (дашборд). +- [ ] **P1** Fuzz-тест: случайные легальные матчи на 1000 партий, проверка инвариантов (нет отрицательных счётчиков, зоны не теряют карты, total cards = const). +- [ ] **P2** Визуальный редактор/валидатор карт. + +### Блок 11. Игровой сервер и сетевой протокол `[MVP]` + +- [ ] **P0** Спецификация протокола (транспорт выбран: TCP/WebSocket + MessagePack): handshake, auth, версия, ping/pong, реконнект. +- [ ] **P0** Конверт сообщения: `{type, seq, payload}`, типизированные DTO в `/shared`, генерация кода для C#-клиента. +- [ ] **P0** Orleans-модель: `IMatchGrain` (1 матч = 1 grain), `IQueueGrain` на формат, `IUserGrain` (сессия/presence). Последовательная обработка интентов — гонки исключены архитектурно. +- [ ] **P0** Кластеризация: dev — один silo, prod — 2+, Redis как clustering/persistence provider; readiness-проба на silo. +- [ ] **P0** **Проекция состояния под клиента** (fog of war): каждый игрок получает свою руку, свой trigger zone и открытый урон. Никогда не отправлять: порядок колоды, руку противника, **лицом вниз лежащий урон (даже владельцу)**, seed. +- [ ] **P0** Авторитет: клиент отправляет только намерения (`PlayCard`, `Attack`, `PassGuard`), сервер валидирует и рассылает события. +- [ ] **P0** Последовательность событий (`eventSeq`) — клиент просит `resync` при разрыве. +- [ ] **P1** Пауза/возобновление матча при `OnApplicationPause` (мобилка) с лимитом. +- [ ] **P1** Версионирование протокола: `minClientVersion`, вежливый отказ с сообщением «обновите клиент». +- [ ] **P1** Rate limiting на интенты (античит: не больше N действий/сек). +- [ ] **P1** Graceful shutdown: активные матчи дописываются в снапшот, игроки получают «сервер перезагружается». + +### Блок 12. Реконнект и устойчивость матча `[MVP]` + +- [ ] **P0** Политика: 0 / 2 / 10 / 30 с → затем экран «не удалось», матч живёт ещё N минут. +- [ ] **P0** Явные правила: сколько ждём (например, 5 мин), что происходит по истечении (авто-поражение / ничья), как это влияет на XP и рейтинг. +- [ ] **P0** Сессия в Redis с TTL ≥ длительности матча + запас; продление на каждом интенте. +- [ ] **P0** Снапшот матча в PostgreSQL при завершении и раз в N интентов; восстановление актора после рестарта. +- [ ] **P0** Индикатор «Переподключение…», кнопка «Отменить» (добровольная сдача с подтверждением). +- [ ] **P1** Реконнект **только** при неожиданном обрыве: явный выход = сдача, без «сохранения» матча. +- [ ] **P1** Тест: убить соединение на 20 разных шагах матча, проверить восстановление. +- [ ] **P1** Обработка дублирующихся интентов (idempotency по `intentId`). + +### Блок 13. Матчмейкинг, очереди `[MVP]` + +- [ ] **P0** MVP: **2 очереди** — Classic Ranked и Classic Casual. Остальные 4 (V/D) включаются конфигом без правки кода — реестр очередей data-driven, не копипаста. +- [ ] **P0** Приватные матчи через приглашение — отдельный путь, минуя очередь. +- [ ] **P0** Алгоритм: скрытый MMR, расширяющееся окно (например, ±100 → ±600 за 60 с), регион/пинг. +- [ ] **P0** Консолидация: если очередь пуста N секунд — предложить «играть в другой формат» (иначе 6 очередей убьют онлайн). +- [ ] **P0** Проверка легальности колоды перед постановкой в очередь; выбор колоды (не «последняя»). +- [ ] **P1** Казуал: широкое окно MMR + защита первых 10 матчей новичка. +- [ ] **P1** «Вес колоды» для матчмейкинга — **пересмотреть**: влияет на мету и воспринимается как несправедливость. Сделать опциональным/выключенным. +- [ ] **P1** Приватные матчи: приглашение → принятие → выбор колоды → матч; без XP, но с записью в историю. +- [ ] **P2** Региональные шарды и межрегиональный матчмейкинг. + +### Блок 14. Клиент: каркас `[MVP]` + +- [ ] **P0** Unity 2022 LTS **или** Unity 6 — зафиксировать отдельным решением (в 6 закрытых вопросов не входило); render pipeline — URP. +- [ ] **P0** Слои: `Core` (сеть, состояние), `Meta` (меню/коллекция), `Battle` (сцена боя), `UI`, `Audio`, `Localization`. +- [ ] **P0** **State-машина приложения** + DI (VContainer/Zenject или ручной composition root) — без синглтонов-свалок. +- [ ] **P0** Клиентское зеркало состояния: `ClientGameState` собирается **только** из событий сервера; никаких локальных правок правил. +- [ ] **P0** Очередь презентации: события → анимации строго последовательно; скип/ускорение; корректный «догон» после реконнекта. +- [ ] **P0** Addressables: каталог карт, арт, звук; ключ = `cardId`; загрузка/выгрузка по требованию. +- [ ] **P1** Input System: одна схема, два набора привязок (тач / мышь+клавиатура). +- [ ] **P1** Настройки: громкость, язык, качество, лимит FPS, мьют эмоций, приватность. +- [ ] **P1** Экран ошибок/офлайна, повтор запросов, таймауты API. +- [ ] **P1** Логи клиента с отправкой в саппорт (кнопка «отправить лог»). + +### Блок 15. Клиент: боевая сцена и UX `[MVP]` + +- [ ] **P0** Экран старта партии: выбор стартового grade 0 из колоды → **маллиган** (отметить любые карты, подтвердить) → превью руки противника без содержимого. +- [ ] **P0** Раскладка поля: Vanguard Circle, 2 колонки × (front/back), Guardian, рука, Damage (лицом вниз), Soul, Drop, Trigger. +- [ ] **P0** Отображение силы/крита с модификаторами (подсветка баффов), доступных действий, «почему нельзя». +- [ ] **P0** Ввод: тап/клик на карту → контекстное меню действий → выбор цели (подсветка валидных целей). +- [ ] **P0** Анимации: ride, call, attack, drive check (раскрытие), damage check, retire, boost, guard. DOTween + очередь. +- [ ] **P0** Ход противника: таймлайн «что произошло» + лог боя (текстовый), чтобы не терять контекст. +- [ ] **P0** Таймер хода, индикатор «переподключение», кнопка сдачи с подтверждением. +- [ ] **P1** Эмоции: панель, ID → ассет, кулдаун 2–3 с, локальный мьют per-match. +- [ ] **P1** Экран результата: победа/поражение, XP, прогресс заданий, рейтинг ±, кнопка «ещё матч». +- [ ] **P2** Настройка скорости анимаций, «пропустить всё». +- [ ] **P2** Режим наблюдателя / просмотр реплея. + +### Блок 16. Колоды и коллекция `[MVP]` + +- [ ] **P0** Декбилдер: поиск/фильтры (клан, грейд, редкость, тип Normal/Trigger, наличие арта), счётчики, валидация в реальном времени. +- [ ] **P0** Валидация на **сервере** (клиентская — только подсказка): ровно 50 карт, ≤4 копий **по `nameId`** (не по принту!), ≤4 карт с Sentinel, ≤4 карт с Heal-триггером, формат, бан-лист. +- [ ] **P0** **Разделить инвентарь и колоду:** коллекция не ограничивает количество копий и артов вообще; лимит 4 по имени действует только в момент сборки/сохранения колоды. +- [ ] **P0** Отдельный тест-кейс: 4× обычный Blaster Blade + 4× параллельный (S02) должен отклоняться валидатором, при этом обе карты спокойно лежат в коллекции в любом количестве. +- [ ] **P0** Коллекция: каждый арт — отдельная запись (BT01/002 и BT01/S02 видны раздельно), привязка к базовому номеру, счётчик копий без верхней границы, «сколько в колодах», блокировка разбора. +- [ ] **P0** Лимиты: 30 колод на формат + 10 турнирных; понятные ошибки при превышении. +- [ ] **P1** Инвариант данных: у grade 0 щит 5000 **только** у Draw-триггеров, у остальных 10000. Автопроверка на всех 94 картах ловит ошибки разметки. +- [ ] **P1** Импорт/экспорт по коду колоды + ссылка; шаринг. +- [ ] **P1** Публичные колоды: просмотры, лайки, «гайд» (описание). +- [ ] **P1** Защита от разбора: карта в колоде/турнирном снимке заблокирована; снятие после турнира. +- [x] ~~Сайд-дек~~ — **решение: убран**, формат BO1. Пункт «main + side ≤ 4 копий» из §13 плана удалить. +- [ ] **P2** «Вес колоды», статистика колоды (winrate), рекомендации. + +### Блок 17. Экономика `[MVP]` + +> **MVP-объём:** Gold, пыль, бустеры, duplicate protection, покупка триал-колод. Баттлпасс, квесты и Draft Run — после MVP. XP всё равно пишем с первого дня, иначе прогресс не восстановить задним числом. + +- [ ] **P0** **Журнал транзакций** (double-entry): каждая операция — запись с `idempotencyKey`, баланс — вычисляемый, не хранимый «на честном слове». +- [ ] **P0** Валюты: Gold, пыль, **токены** (универсальные — обмениваются на любой бустер). Все изменения — только через сервис экономики. +- [ ] **P0** Структура бустера: **5 карт** = 4 карты редкости **C** + 1 слот **«R и выше»**. Генерация слотовая, а не «5 случайных по редкости» — иначе гарантия не выполняется. +- [ ] **P0** Веса слота R+: **R 70 % / RR 22 % / RRR 7 % / SP 1 %** → в `pack_odds.yaml`, с валидацией «сумма = 100 %» и «строгое убывание». +- [ ] **P0** SP-принты (12 шт.) — отдельный исход в слоте R+, заменяет базовую карту. Для duplicate protection это отдельный принт. +- [ ] **P0** Бустеры: серверный рандом по весам, лог открытий (кто/когда/что/сид). +- [ ] **P0** Duplicate protection: ключ — **`printingId`** (номер + арт), порог **4 копии**. Слот выбирается только из принтов, которых у игрока меньше 4. +- [ ] **P0** Замена — **внутри той же редкости**, иначе поедут шансы слота. +- [ ] **P0** Если **вся редкость** насыщена (все принты по 4), а другие ещё нет — перебрасываем редкость с перенормировкой весов. Иначе все 20 R собираются раньше, чем RRR, и дальше каждый R-бросок становится «мёртвым». +- [ ] **P0** Если насыщен весь слот R+ — карта выдаётся как дубль и уходит в коллекцию. + +> **Что дают веса** (коробка = 30 бустеров): +> | Редкость | Вес | На коробку | Копий до 4× | Бустеров, если добирать только эту редкость | +> |---|---|---|---|---| +> | R | 70 % | ~21 | 80 | ~114 | +> | RR | 22 % | ~6,6 | 48 | ~218 | +> | RRR | 7 % | ~2,1 | 32 | ~457 | +> | SP | 1 % | ~0,3 | — | вне прогресса сета | +> +> Защита от дублей не ускоряет добор **редкой** редкости: она лишь убирает повторы внутри неё. Поэтому хвост сета — это RRR. +> Точечно нужная карта добывается **пылью**, а не открытием: после насыщения C каждый бустер даёт ~20 пыли, крафт RRR = 400 → 20 бустеров на одну. Это и есть надёжный путь к турнирной колоде. +- [ ] **P0** Дубль **не разбирается автоматически**: игрок сам превращает его в пыль, с предупреждением, если карта стоит в колоде. Коллекция при этом не ограничена. +- [ ] **P0** SP **не учитывать** в прогрессе «собрано X/Y карт сета» и в достижениях за коллекцию — иначе сет не закрывается никогда. SP — косметический параллельный принт. +- [ ] **P0** Пыль под реальные редкости: разбор **C 5 / R 10 / RR 25 / RRR 100**, крафт ×4 — **20 / 40 / 100 / 400**. SP не крафтится (только из бустеров). В плане был «Uncommon» — в BT01 это R. +- [ ] **P0** Пыль — **сквозная валюта, не привязана к сету**: накопленное тратится на крафт карт нового сета в день его выхода. При смене сета пыль не сгорает и не конвертируется. +- [ ] **P1** Инфляция пыли при темпе сетов 1–2 месяца: за цикл накапливается ~800–1600 пыли против 6 000 на полный плейсет, то есть **13–27 %**. Это здоровый диапазон — крафт нового сета можно открывать сразу, задержка не нужна. +- [ ] **P2** Если темп сетов замедлится до 4+ месяцев, вернуться к вопросу задержки крафта: тогда доля доходит до 50 %+ и новинки теряют смысл. +- [ ] **P0** Цены MVP: бустер 100 Gold, 1 токен = 1 бустер. Draft Run 1000 Gold — после MVP. +- [ ] **P1** `[v1]` Баттлпасс: 75 уровней / 30 дней, XP-пороги 250/700/1000, награды каждые 5 уровней, Draft-пропуск на 40 и 75, переполнение 50 Gold/уровень. +- [ ] **P0** XP за матчи: 150/75/100, Draft 200/100, турниры 500/250/1000/2000 (начисление писать с первого дня). +- [ ] **P0** Gold за матчи: **30 / 15 / 20** (победа / поражение / ничья). Мнемоника — **Gold = XP / 5**: одна константа в конфиге, а не два независимых баланса. +- [ ] **P0** Gold **не** начисляется: за сдачу, АФК, приватные матчи. Только после подтверждённого сервером завершения. +- [ ] **P1** Бонус первой победы дня: **+20 Gold**. Компенсирует то, что квесты дают только XP, и держит ежедневный вход осмысленным. +- [x] **P0** Размер бустера подтверждён: **5 карт**, из них 1 гарантированно R или выше. + +> **Проверка темпа** (5 карт в бустере = 4 C + 1 R+, 50 % побед, 5 матчей в день): +> - средний доход за матч — 22,5 Gold, то есть ~22 % бустера; +> - день — ~132 Gold (с бонусом первой победы) ≈ **1,3 бустера**; +> - неделя — ~9 бустеров = 36 C + 9 R+; +> - месяц — ~40 бустеров = **~160 C** (ровно 40 карт × 4 — полный C-сет) и **~40 R+**; +> - **узкое место — не коммоны, а R+:** 40 карт × 4 = 160 копий, то есть полный R+ закрывается за ~160 бустеров ≈ **4 месяца**; +> - **на один сет при темпе 1,5 месяца (~60 бустеров):** ~100 % C и ~37 % R+. Новинка ощущается свежей, но R+ остаётся хвостом. + +- [ ] **P1** `[v1]` Квесты: 3 ежедневных (1/3/5 матчей → 300 XP), 1 недельный (20 матчей → 2000 XP), сброс по UTC. +- [ ] **P0** Засчитывание матча: доигран, без сдачи, без АФК; приватные — не засчитываются. +- [ ] **P2** `[later]` Draft Run: вход 1000 Gold → 10 бустеров, 5 побед → +5 бустеров, 10 побед → +5 токенов, без пыли, без лимита поражений. +- [ ] **P2** `[later]` Правила драфт-колоды (размер, триггеры, базовые карты) — **сейчас не определены**. +- [ ] **P0** Онбординг: после туториала игрок выбирает **одну из двух триал-колод** (бесплатно) и получает **5 токенов**; токен обменивается на любой бустер. Выбор нельзя отменить — предусмотреть подтверждение. +- [ ] **P0** Покупка второй триал-колоды за Gold. +- [ ] **P1** Анти-дюп и защита от повторного применения `idempotencyKey`; лимит операций в секунду. +- [ ] **P1** Открытые вопросы: сгорает ли пыль в конце сезона, есть ли лимит на токены, есть ли лимит Gold. +- [ ] **P2** «Инфляция Gold»: что ещё продавать, чтобы валюта не обесценивалась. + +### Блок 18. Рейтинг, сезоны, прогрессия, достижения `[v1]` + +- [ ] **P0** MMR: скрытый, Elo/Glicko-2, отдельный на каждый формат (3 значения), K-фактор с учётом новичков. +- [ ] **P0** Ранги: 6 тиров × 5 дивизионов, Master без дивизионов; звёзды ±1, 5 звёзд = +1 дивизион. +- [ ] **P0** Защита от падения: Bronze/Silver — нет, Gold/Platinum/Diamond — до пола тира, Master — свободно. +- [ ] **P0** Сброс сезона 1-го числа (UTC): Master→Platinum 1, Diamond→Gold 1, Platinum→Silver 1, Gold→Bronze 1. +- [ ] **P1** Награды за ранг (Gold, пыль, Master — рубашка + титул). +- [ ] **P1** Бонусные звёзды при высоком MMR. +- [ ] **P1** Грейды аккаунта 0–5, XP аккаунта, награды (Gold, пыль, слоты колод, косметика, доступ к режимам). +- [ ] **P1** Достижения: категории, разовые/прогрессивные/скрытые, условия — данными, а не кодом. +- [ ] **P2** Сезонные лидерборды с античитом (ручная проверка топ-100). + +### Блок 19. Турниры `[v1]` + +- [ ] **P0** Турнирный движок: создание, регистрация, фиксация колод, Swiss-пары, Top Cut, тай-брейкеры (OMW%, GW% и т. п.). +- [ ] **P0** Таймер раунда 35 мин — **на сервере**; при истечении Double Loss; синхронизация часов клиента. +- [ ] **P0** Снимок колоды при регистрации (immutable), защита карт от разбора до конца турнира. +- [ ] **P0** Автоматический репорт результата, ручной репорт с подтверждением соперником, разрешение конфликтов админом. +- [ ] **P0** 3×3: капитаны видят **кланы** колод соперников, выбирают порядок скрыто (commit-reveal или одновременная блокировка выбора). +- [ ] **P0** 3×3: счёт по парам 2:1/3:0, техническое поражение только в конкретной паре, а не всей команде. +- [ ] **P1** История: архив всех турниров, публикация снимков колод после завершения, архив меты. +- [ ] **P1** «Эра как модификатор пула» — отдельный флаг турнира поверх формата. +- [ ] **P1** Промо-события: турниры к сету, награды по местам, «Неделя клана», «Двойной опыт», «Легендарный Draft», «Ретро-турнир» — как конфигурируемые модификаторы. +- [ ] **P2** Наблюдение за турниром (spectator), стрим-оверлей. + +### Блок 20. Социалка и присутствие `[v1]` + +- [ ] **P0** SignalR-хаб: аутентификация, presence (Redis `online:{userId}` с TTL), подписка на каналы. +- [ ] **P0** Друзья: заявки, принятие, удаление, чёрный список, онлайн-статус. +- [ ] **P0** Чат: общий, командный, личный; история в БД (удалённые сообщения **не удаляются физически**). +- [ ] **P0** Модерация чата: мут (15 мин / 1 ч / 24 ч / 7 дней / навсегда), удаление сообщения, лог. +- [ ] **P1** Эмоции: ID вместо текста, кулдаун, глобальный мьют в настройках; отправка своих не блокируется. +- [ ] **P1** Приглашение в матч из чата/списка друзей. +- [ ] **P1** Rate limiting и антиспам (флуд, капс, ссылки). +- [ ] **P2** Фидбек-вкладка: отзывы о мете и предложения, маршрутизация в тикеты. + +### Блок 21. Команды и территории `[v1]` + +- [ ] **P0** Команда: полное название, тег 2–5 символов (уникальный), иконка; префикс `[TAG]` в нике везде (чат, матч, профиль, лидерборд). +- [ ] **P0** Типы: открытая (заявки) / закрытая (приглашения); роли Leader/Officer(1–3)/Member; матрица прав. +- [ ] **P0** Передача лидерства только офицеру, кулдаун 30 дней от последней передачи. +- [ ] **P0** Лимит участников по уровню команды (1–10), XP команды. +- [ ] **P1** Территории: 6 регионов, точки (столица/ключевая/аутпост), очки контроля за командные победы. +- [ ] **P1** Недельный цикл: расчёт владельца, награды (Gold, пыль, командный XP, рейтинг), лимит 3 территории, убывающая отдача. +- [ ] **P1** Планировщик (Hangfire/Quartz): недельный и месячный джобы, идемпотентные, с восстановлением после падения. +- [ ] **P1** Командный лидерборд. +- [ ] **P2** Визуальная карта наций в клиенте. + +### Блок 22. Профиль, косметика, обучение `[v1]` + +- [ ] **P0** Профиль: ник + префикс, аватар, рамка, титул; статистика по режимам, любимый клан, рейтинг, история турниров, достижения, публичные колоды. +- [ ] **P0** Приватность: скрыть статистику/историю/достижения/колоды (проверка на сервере, а не в UI). +- [ ] **P0** Косметика: типы (карт-бэки, аватары, рамки, эмоции, эффекты поля, титулы, музыка/звуки), источники, инвентарь, экипировка. +- [ ] **P0** Косметика **не влияет на геймплей** — зафиксировать тестом (никаких статов из косметики). +- [ ] **P1** Обучение: `TutorialGameManager` + `TutorialStep` (SO) поверх реального движка; шаги Ride → Call → Boost → Attack → Drive/Damage Check; «Пропустить». +- [ ] **P1** Экскурсия по клиенту: подсветка меню, коллекции, декбилдера, бустеров, заданий, баттлпасса, настроек, друзей; повторный запуск из настроек. +- [ ] **P1** Продвинутые туториалы по механикам — формат каждого пока не определён. +- [ ] **P2** Обучение старым эрам (LB, Legion, Stride, V, D). + +### Блок 23. Модерация и админ-панель `[v1]` + +- [ ] **P0** Роли: Admin (логи чата/матчей/транзакций/IP, баны, назначение модераторов, тикеты) / Moderator (только чат). +- [ ] **P0** Веб-админка: список пользователей, поиск, бан/разбан, мут, история действий. +- [ ] **P0** Тикеты: жалобы, апелляции, баг-репорты, пожелания; маршрутизация по типу, SLA, статусы. +- [ ] **P0** Просмотр логов: чат (включая удалённые), транзакции, матчи (+реплей), IP — **с аудитом доступа**. +- [ ] **P1** Управление контентом: бан-лист, лимиты карт, условные баны (`нельзя X если есть Y`), патчноуты. +- [ ] **P1** Выдача валюты/косметики вручную (компенсации) — с обязательным логом. +- [ ] **P1** Инструменты баланса: winrate по картам, популярность, статистика матчей, голосования. +- [ ] **P2** Автоматические детекторы абуза (слив матчей, win-trading, мультиаккаунт). + +### Блок 24. Лаунчер и доставка клиента `[v1]` + +- [ ] **P0** WPF-лаунчер: логин, проверка версии, прогресс загрузки, кнопка «Играть», новости, настройки (язык, папка). +- [ ] **P0** Манифест версий: `clientVersion → files{hash,size,url}`; дифф-патчи (Game Launcher Asset Store). +- [ ] **P0** CDN/S3 для патчей, подпись манифеста, проверка хешей после загрузки, откат при повреждении. +- [ ] **P1** Принудительное обновление при `minClientVersion`; сообщение «обновитесь». +- [ ] **P1** Самообновление лаунчера. +- [ ] **P1** Установщик (Inno Setup/MSIX), юзер-специфичная установка без прав админа. +- [ ] **P2** Каналы `stable`/`beta`, возможность откатить версию. + +### Блок 25. Мобильная адаптация и производительность `[v1]` + +- [ ] **P0** Canvas Scaler + Safe Area на всех экранах, проверка на вырезах и планшетах. +- [ ] **P0** Input System: тап, долгое нажатие, drag, свайп — одна схема, два набора привязок. +- [ ] **P0** ASTC-сжатие текстур, атласы UI, стриминг аудио, Addressables-группы по сценам. +- [ ] **P0** Target FPS: 30 (мобилка) / 60 (десктоп); Adaptive Performance. +- [ ] **P0** `OnApplicationPause` → пауза матча (с серверной валидацией, чтобы не абузили). +- [ ] **P1** Мониторинг connectivity, UI офлайн-состояния, повтор запросов. +- [ ] **P1** Бюджеты: draw calls, память, размер билда; профилирование на минимальном устройстве. +- [ ] **P1** Мобильная доставка контента через Addressables + CDN (вместо лаунчера). +- [ ] **P2** Настройки батареи/тепла (лимит FPS, качество эффектов). + +### Блок 26. QA, релиз, эксплуатация `[v1]` + +- [ ] **P0** Тест-план: юнит (правила), интеграционные (API), e2e (полный матч), нагрузочные (матчмейкинг). +- [ ] **P0** Набор «золотых матчей»: 50 реплеев, которые обязаны воспроизводиться после каждого изменения ядра. +- [ ] **P0** Тестовые аккаунты и «песочница» с бесконечной валютой для QA. +- [ ] **P1** Чеклист релиза: миграции применены, бэкап снят, версия клиента в манифесте, патчноут опубликован, алерты включены. +- [ ] **P1** Rollback-план: как откатить сервер и клиент (клиент — только вперёд, поэтому сервер обязан поддерживать N-1 версию клиента). +- [ ] **P1** Поддержка: шаблоны ответов, форма баг-репорта с логами, публичная страница статуса. +- [ ] **P1** Метрики продукта: DAU, retention D1/D7, среднее число матчей, конверсия в платные валюты (внутренние), длина очередей. +- [ ] **P1** Релизный цикл сета под темп 1–2 месяца: контент заморожен за 2 недели до релиза, плейтест, патчноут, архив меты, пересмотр бан-листа. + +--- + +## 3. Открытые вопросы + +### 3.1 Закрыто +1. ~~Сетевой стек~~ → TCP/WebSocket + MessagePack. +2. ~~Формат описания карт~~ → C#-скрипты + YAML. +3. ~~Серверный фреймворк~~ → Orleans. +4. ~~Платформа~~ → ПК-first. +5. ~~Сайд-дек~~ → убран, BO1. +6. ~~Объём MVP~~ → 94 карты: BT01 + TD01 + TD02. +7. ~~Декбилдер~~ → полный, в MVP. +8. ~~Онбординг~~ → туториал → триал-колода на выбор + 5 токенов. +9. ~~Арты~~ → официальные. +10. ~~Ride Deck / Persona Ride~~ → не нужны, базовая эра. +11. ~~Локализация~~ → EN + RU с первого дня. +12. ~~Листы TD01/TD02~~ → получены из вики, лежат в `content/cards-mvp.md`. +13. ~~Токены~~ → 1 токен = 1 бустер. +14. ~~Бан-лист~~ → банов в MVP нет. +15. ~~Параллельные арты~~ → отдельные принты, привязка к базовому номеру карты. +16. ~~Название формата~~ → **Classic**. +17. ~~Лимиты коллекции~~ → **нет**, 4 копии по имени — только правило декбилдера. +18. ~~Таймер хода~~ → 90 с на ход, банк 5 мин, авто-end по истечении. +19. ~~Gold за матч~~ → 30 / 15 / 20 (победа / поражение / ничья), правило **Gold = XP / 5**. +20. ~~Размер бустера~~ → **5 карт**, из них 1 гарантированно R или выше. +21. ~~Веса слота R+~~ → **R 70 / RR 22 / RRR 7 / SP 1**. + +### 3.2 Блокирующие MVP +Открытых вопросов нет. Все решения по MVP зафиксированы в §0.0 и §0.0.1. + +### 3.3 Контент и правила +18. Таксономия редкостей BT01: какие редкости там реально есть (C/R/RR/RRR/SP?) и как в неё вписываются S01–S12. +19. Источник `triggerType` для 94 карт: ручная разметка, OCR по иконкам или decklog. +20. Итоговый список механик MVP — после матрицы «карта → механика». +21. Нужны ли анимированные карты и эффекты в MVP (влияет на бюджет клиента). + +### 3.4 Экономика +22. Сгорает ли пыль в конце сезона; есть ли лимит на токены. +23. Что ещё продавать за Gold, чтобы не было инфляции. +24. Гринд баттлпасса: 58 500 XP за 30 дней ≈ 10+ матчей в день. Оставляем? + +### 3.5 Техника и эксплуатация +25. Таймзона сбросов (сезон/квесты/территории) — предлагаю UTC. +26. Хранение истории матчей и реплеев: как глубоко, что видно игроку. +27. Логи и аналитика: что логируем, срок хранения, PII. +28. Бэкапы: RPO/RTO, проверка восстановления. +29. Бюджет на сервер: VPS или облако? Это определяет потолок онлайна. +30. Что происходит после 30 с реконнекта и через N минут отсутствия. +31. Политика версий: сколько старых версий клиента поддерживаем. + +### 3.6 Продукт +32. Юридический план на случай takedown (арты официальные — риск высокий). +33. Сторы: Google Play / App Store / только APK (после ПК-релиза). +34. Модерация фидбека: кто читает, есть ли голосование. +35. «Вес колоды» в матчмейкинге — оставляем или убираем. +36. Маркетинг: сайт, Discord, соцсети — кто и когда. diff --git a/IMPLEMENTATION_CHECKLIST.md b/IMPLEMENTATION_CHECKLIST.md new file mode 100644 index 0000000..e1c8074 --- /dev/null +++ b/IMPLEMENTATION_CHECKLIST.md @@ -0,0 +1,283 @@ +# Базовая версия — чеклист реализации + +Практический план: что писать, в каком порядке и когда считать готовым. +Дизайн-решения и обоснования — в `DEVELOPMENT_CHECKLIST.md`. Данные карт — в `content/cards-mvp.md`. + +--- + +## 0. Границы базовой версии + +**Входит:** +- Платформа: Windows. Unity + WPF-лаунчер. +- Формат: `Classic`, один сет — BT01 + TD01 + TD02 (94 карты). +- Аккаунт, коллекция, полный декбилдер, магазин бустеров и триал-дек. +- 2 очереди: Classic Ranked и Classic Casual. Матч 1×1, BO1. +- Полный игровой цикл: матч → результат → Gold/XP → бустер → карта в коллекцию. +- Базовый туториал + выдача триал-деки и 5 токенов. +- Локализация RU + EN. +- Лаунчер с проверкой версии и докачкой. + +**Не входит:** турниры, команды, территории, чат, друзья, баттлпасс, квесты, Draft, косметика, мобилка, реплеи в клиенте (только серверная запись). + +### Стек (зафиксирован) + +| Слой | Технология | +|---|---| +| Сервер | .NET 8, ASP.NET Core, Orleans, EF Core, PostgreSQL 16, Redis 7 | +| Протокол матча | TCP + MessagePack, кадры с префиксом длины | +| Мета-API | HTTP/JSON, JWT | +| Клиент | Unity 6 LTS, URP, Addressables, Input System, Unity Localization, DOTween | +| Лаунчер | WPF, .NET 8, CommunityToolkit.Mvvm | +| Контейнеры | Docker Compose, nginx | + +### Оценка (один разработчик, полный день) + +| Этап | Дни | +|---|---| +| 1. Репозиторий и каркас | 2–3 | +| 2. Контент-пайплайн | 4–6 | +| 3. Игровое ядро | 20–30 | +| 4. Скрипты 94 карт | 15–25 | +| 5. Сеть и матч | 8–12 | +| 6. Мета-API | 10–14 | +| 7. Клиент: каркас и меню | 15–20 | +| 8. Клиент: боевая сцена | 20–30 | +| 9. Туториал | 5–7 | +| 10. Лаунчер | 5–7 | +| 11. QA и сборка | 7–10 | +| **Итого** | **~110–165** | + +--- + +## 1. Репозиторий и каркас `[2–3 дня]` + +- [ ] `git init`, ветка `master`. +- [ ] `.gitignore`: Unity (`Library/`, `Temp/`, `Logs/`, `UserSettings/`, `Builds/`), .NET (`bin/`, `obj/`), WPF (`publish/`), секреты (`.env`, `appsettings.Production.json`). +- [ ] Отдельно игнорировать только Unity-генерируемые `*.csproj`/`*.sln` в `/client`, чтобы не убить `server/*.sln`. +- [ ] `.gitattributes`: `* text=auto eol=lf`, `*.unity`, `*.prefab`, `*.asset` → `merge=unityyamlmerge`, `*.png`/`*.wav` → `binary`. +- [ ] Структура: + ``` + /server + Vanguard.sln + /src/Vanguard.Shared контракты, DTO, MessagePack + /src/Vanguard.Rules чистое ядро правил, без Orleans и БД + /src/Vanguard.Rules.Cards 94 скрипта карт + /src/Vanguard.Content загрузчик и валидатор контента + /src/Vanguard.Meta.Api ASP.NET Core + /src/Vanguard.Game.Silo Orleans + /src/Vanguard.Cli headless-раннер матчей + /tests/Vanguard.Rules.Tests + /client Unity-проект + /launcher WPF + /content/sets/BT01 YAML + /content/sets/TD01 + /content/sets/TD02 + /ops docker-compose, nginx, скрипты + /docs + ``` +- [ ] `Directory.Build.props`: `net8.0`, `Nullable=enable`, `TreatWarningsAsErrors` для `Rules`. +- [ ] `docker-compose.yml`: postgres, redis (AOF), meta-api, game-silo, nginx. +- [ ] `docker compose up` поднимает БД и пустой API; healthcheck отвечает. +- [ ] CI: сборка solution + `dotnet test` на push. + +**Готово, когда:** `docker compose up` + `dotnet run` дают живой `/health`. + +--- + +## 2. Контент-пайплайн `[4–6 дней]` + +- [ ] Схема YAML: `printingId`, `nameId`, `setId`, `cardType`, `grade`, `power`, `shield`, `critical`, `triggerType`, `clan`, `rarity`, `isSentinel`, `isRestraint`, `name_en/ru`, `text_en/ru`, `flavor_en/ru`, `imageKey`. +- [ ] Импорт `card-mvp.md` → три YAML-файла (BT01, TD01, TD02). 94 записи, 124 принта. +- [ ] Валидатор: + - [ ] `sum(deck) = 50`, `≤4` по `nameId`, `≤4` Sentinel, `≤4` Heal. + - [ ] Инвариант щита: grade 0 → 5000 **только** у Draw, иначе 10000. + - [ ] Уникальность `printingId`, наличие `nameId` в обеих локалях. + - [ ] Ссылка SP-принта на существующий базовый номер. +- [ ] Загрузчик: YAML → in-memory каталог, используется и сервером, и тестами. +- [ ] Сид в PostgreSQL: `cards`, `printings`, `sets`, `card_legality` (пустая). +- [ ] Экспорт каталога для клиента (JSON + список Addressables-ключей). + +**Готово, когда:** `dotnet run -- validate` печатает «94 cards, 124 printings, 0 errors». + +--- + +## 3. Игровое ядро `[20–30 дней]` — критический путь + +Пишется **как чистая библиотека**: без Orleans, без БД, без сети. Вход — состояние + интент, выход — новое состояние + события. + +- [ ] `GameState`: игроки, зоны, фазы, счётчики, `rngState`, `sequence`. +- [ ] Зоны: Vanguard, Rear-Guard (3 колонки × 2 ряда), Guardian, Soul, Damage, Drop, Trigger, Deck, Hand. +- [ ] Юнит: `instanceId`, `printingId`, `standState`, модификаторы, временные эффекты. +- [ ] Damage: список карт с `faceUp`, скрыт от обоих игроков. +- [ ] Фазы: Mulligan → Stand → Draw → Ride → Main → Battle → End. +- [ ] Старт: выбор grade 0 → выставление → маллиган. +- [ ] Действия: Ride, Call, Move, Attack, Intercept, Guard, Boost, Drive, Damage, Stand/Rest, Retire, Draw, Discard, Shuffle. +- [ ] `driveCheckCount` как модифицируемое свойство. +- [ ] Результат боя: `Hit` / `Guarded` / `Missed` как событие. +- [ ] Событийная шина и три обработчика: Continuous / Activated / Triggered. +- [ ] Check Timing: сбор → сортировка → разрешение по одному → повтор. +- [ ] Стоимости: CB, SB, discard, rest self, retire self, put to soul, return to deck. +- [ ] Цели: авто по фильтру и выбор игроком, «до N» / «ровно N». +- [ ] Слои модификаторов силы/крита с длительностью. +- [ ] Валидация интентов: `Rejected(reason)` без изменения состояния. +- [ ] Список легальных интентов на каждый момент (для UI и античита). +- [ ] Детерминированный RNG: отдельные потоки для шаффла, drive, damage, эффектов. +- [ ] Журнал интентов + seed → реплей. +- [ ] Защита от бесконечных циклов и дедлоков. + +**Готово, когда:** два бота случайными легальными интентами доигрывают 1000 партий без падений, инварианты (сумма карт, счётчики ≥ 0) держатся, реплей воспроизводится бит-в-бит. + +--- + +## 4. Карты `[15–25 дней]` + +- [ ] Хелперы: `Draw`, `Retire`, `SearchCall`, `SearchHand`, `SearchRide`, `Stand`, `SoulCharge`, `SoulBlast`, `CounterBlast`, `FlipDamage`, `ToSoul`, `ToDeck`, `Discard`, `LookTop`, `ModifyPower`, `ModifyCrit`, `GainKeyword`. +- [ ] Шаблон карты: 1 файл = 1 `nameId`, комментарий с официальным текстом и ссылкой. +- [ ] Порядок: TD01 → TD02 → BT01 (сначала ванильные, потом со способностями). +- [ ] По каждой карте: скрипт + тест «дано → сыграл → ожидаю». +- [ ] RU/EN тексты для всех 94. +- [ ] Сила/щит для 14 карт, которых нет в BT01. +- [ ] Дашборд покрытия: `scripted / tested / total`. + +**Готово, когда:** 94/94 заскриптованы, 94/94 покрыты тестом, приёмка пройдена по чеклисту карты. + +--- + +## 5. Сеть и матч `[8–12 дней]` + +- [ ] Спецификация протокола в `docs/protocol.md`: кадры, handshake, версия, ping/pong, reconnect. +- [ ] `Vanguard.Shared`: DTO интентов и событий, MessagePack-резолвер. +- [ ] `IGameTicketService`: одноразовый тикет на подключение (TTL 60 с). +- [ ] TCP-сервер: accept, handshake, auth по тикету, привязка к grain. +- [ ] `IMatchGrain`: 1 матч = 1 grain, последовательная обработка. +- [ ] Проекция состояния под клиента: своя рука, свой trigger, открытые зоны; **ничего** о колоде, руке противника и закрытом уроне. +- [ ] `eventSeq` + `resync` по запросу. +- [ ] Реконнект: 0 / 2 / 10 / 30 с, затем экран «не удалось». +- [ ] Политика отсутствия: 5 минут → авто-поражение. +- [ ] Таймер хода: 90 с + банк 5 мин. +- [ ] Снапшот матча в PostgreSQL + восстановление grain после рестарта. +- [ ] Idempotency по `intentId`. + +**Готово, когда:** два клиента играют полный матч по сети; kill процесса клиента на любом шаге восстанавливается. + +--- + +## 6. Мета-API `[10–14 дней]` + +- [ ] Модель `User`, BCrypt, JWT (access 15 мин + refresh 30 дней, ротация). +- [ ] Эндпоинты: `register`, `login`, `refresh`, `logout`, `me`. +- [ ] Коллекция: `GET /collection` (сгруппировано по принтам), счётчики без верхней границы. +- [ ] Колоды: CRUD, `validate`, лимит 30 на формат, шаринг по коду. +- [ ] Магазин: `GET /store`, `POST /buy/booster`, `POST /buy/trialdeck`, `POST /redeem/token`. +- [ ] Генератор бустера: слот `4 × C` + `1 × R+` (R 70 / RR 22 / RRR 7 / SP 1). +- [ ] Duplicate protection по `printingId`, замена внутри редкости, переброс редкости при её насыщении. +- [ ] Экономика: журнал транзакций, `idempotencyKey`, баланс вычисляемый. +- [ ] Пыль: разбор C 5 / R 10 / RR 25 / RRR 100, крафт ×4, SP не крафтится. +- [ ] Онбординг: выбор триал-деки + 5 токенов, флаг «выбор сделан». +- [ ] Матчмейкинг: 2 очереди, скрытый MMR, расширяющееся окно. +- [ ] Результат матча: начисление Gold 30/15/20 и XP 150/75/100, запись в историю. +- [ ] Rate limiting на все мутирующие эндпоинты. + +**Готово, когда:** новый игрок через API проходит путь регистрация → триал-дека → токены → бустеры → колода → очередь. + +--- + +## 7. Клиент: каркас и меню `[15–20 дней]` + +- [ ] Unity 6 LTS, URP, Addressables, Input System, Localization, DOTween. +- [ ] Сцены: `Boot`, `Meta`, `Battle`. +- [ ] Composition root + DI (VContainer), без синглтон-свалок. +- [ ] `ApiClient` (HTTP/JSON), `MetaState`, обработка офлайна и таймаутов. +- [ ] `GameConnection` (TCP + MessagePack), очередь событий, `ClientGameState`. +- [ ] Экраны: логин/регистрация, главное меню, профиль (минимальный), магазин, коллекция, декбилдер, очередь, настройки. +- [ ] Декбилдер: фильтры (клан, грейд, редкость, тип, арт), счётчики, валидация в реальном времени. +- [ ] Коллекция: арты раздельно, «сколько в колодах», разбор в пыль с подтверждением. +- [ ] Магазин: бустер (анимация открытия), триал-деки, обмен токенов. +- [ ] Локализация RU/EN, переключение на лету. +- [ ] Настройки: язык, звук, FPS, мьют. + +**Готово, когда:** все мета-экраны работают против реального сервера, язык переключается без перезапуска. + +--- + +## 8. Клиент: боевая сцена `[20–30 дней]` + +- [ ] Поле: Vanguard, 2 колонки × 2 ряда, Guardian, рука, Damage, Soul, Drop, Trigger. +- [ ] Старт: выбор grade 0, маллиган. +- [ ] Ввод: клик/тап → контекстное меню → подсветка валидных целей. +- [ ] Отображение силы/крита с модификаторами и подсветкой баффов. +- [ ] Очередь анимаций строго по событиям сервера: ride, call, attack, drive, damage, retire, boost, guard. +- [ ] Скип/ускорение анимаций. +- [ ] Экран противника: таймлайн и текстовый лог боя. +- [ ] Таймер хода, индикатор реконнекта, кнопка сдачи. +- [ ] Экран результата: победа/поражение, Gold/XP, кнопка «ещё матч». +- [ ] Догон состояния после реконнекта (resync + переигрывание очереди). + +**Готово, когда:** полный матч играется мышью без консоли и без рассинхрона с сервером. + +--- + +## 9. Туториал `[5–7 дней]` + +- [ ] `TutorialStep` как ScriptableObject: текст, подсветка, ожидаемое действие. +- [ ] Сценарий: Ride → Call → Boost → Attack → Drive Check → Damage Check → победа. +- [ ] Работает поверх реального движка (локальный матч), не фейковая сцена. +- [ ] Кнопка «Пропустить». +- [ ] По завершении: выбор триал-деки + 5 токенов + флаг онбординга. + +**Готово, когда:** новый аккаунт с нуля доходит до первой своей колоды без подсказок извне. + +--- + +## 10. Лаунчер `[5–7 дней]` + +- [ ] WPF, MVVM, тёмная тема, окно 1000×640. +- [ ] Логин через тот же API, сохранение refresh-токена в DPAPI. +- [ ] `version.json`: `clientVersion`, `minVersion`, список файлов `{path, hash, size, url}`. +- [ ] Проверка хешей локальных файлов, докачка только изменённого. +- [ ] Прогресс, скорость, пауза, возобновление. +- [ ] Кнопка «Играть» запускает клиент с game-тикетом. +- [ ] Обработка «сервер недоступен», «нет места», «файл повреждён → перекачать». +- [ ] Самообновление лаунчера. +- [ ] Установщик (Inno Setup), установка без прав администратора. + +**Готово, когда:** чистая машина → установка → логин → докачка → запуск игры. + +--- + +## 11. Сборка и QA `[7–10 дней]` + +- [ ] Скрипт сборки: сервер (docker), клиент (Unity batchmode), лаунчер (dotnet publish), манифест, загрузка на CDN. +- [ ] Тестовый стенд: БД с бесконечной валютой, аккаунты QA. +- [ ] Прогон «золотых матчей»: 50 реплеев воспроизводятся после каждого изменения ядра. +- [ ] E2E: регистрация → туториал → бустеры → колода → матч → награда. +- [ ] Проверка скрытой информации: дамп трафика не содержит руки и колоды противника, закрытого урона и seed. +- [ ] Проверка реконнекта на 20 разных шагах. +- [ ] Профилирование клиента: 60 FPS на средней машине. +- [ ] Инструкция запуска для нового разработчика (README). + +--- + +## 12. Финальный коммит + +- [ ] `.gitignore` актуален, в индексе нет `Library/`, `bin/`, `obj/`, `.env`, билдов. +- [ ] Нет секретов и ключей в истории (`git log -p | Select-String "password|secret|key"`). +- [ ] `git add -A` и один коммит: + ``` + git commit -m "Base version: server, client, launcher" + ``` +- [ ] Тег: `git tag v0.1.0-base`. +- [ ] Дальше — обычная история, по одному смыслу на коммит. + +--- + +## Гейты (не двигаться дальше, пока не выполнено) + +| Гейт | Критерий | +|---|---| +| **G1. Ядро** | 1000 случайных партий без падений, реплей бит-в-бит | +| **G2. Контент** | 94/94 карт заскриптованы и протестированы | +| **G3. Сеть** | Полный матч по сети, реконнект на любом шаге | +| **G4. Мета** | Регистрация → колода → ранкед матч → награда | +| **G5. Клиент** | Полный цикл мышью, без консоли | +| **G6. Доставка** | Чистая машина → установка → игра | diff --git a/content/cards-mvp.md b/content/cards-mvp.md new file mode 100644 index 0000000..fe1e6df --- /dev/null +++ b/content/cards-mvp.md @@ -0,0 +1,201 @@ +# Контент MVP — BT01 + TD01 + TD02 + +Источники: +- Официальный карт-лист: `en.cf-vanguard.com` (тексты, сила, щит) +- Wiki (через web.archive.org, прямой доступ к fandom закрыт): списки сетов, редкости, триггеры + +Итог: **94 уникальные карты, 124 принта**. 10 кланов. + +--- + +## 1. BT01 «Descent of the King of Knights» + +80 карт: **RRR ×8, RR ×12, R ×20, C ×40** + **SP ×12** (параллельные арты). + +| № | Карта | G | Клан | Триггер | Редкость | +|---|---|---|---|---|---| +| BT01/001 | King of Knights, Alfred | 3 | Royal Paladin | — | RRR + SP | +| BT01/002 | Blaster Blade | 2 | Royal Paladin | — | RRR + SP | +| BT01/003 | Barcgal | 0 | Royal Paladin | — | RRR + SP | +| BT01/004 | Dragonic Overlord | 3 | Kagero | — | RRR + SP | +| BT01/005 | Embodiment of Victory, Aleph | 3 | Kagero | — | RRR | +| BT01/006 | CEO Amaterasu | 3 | Oracle Think Tank | — | RRR + SP | +| BT01/007 | Battle Sister, Cocoa | 1 | Oracle Think Tank | — | RRR + SP | +| BT01/008 | Asura Kaiser | 3 | Nova Grappler | — | RRR + SP | +| BT01/009 | Demon Slaying Knight, Lohengrin | 3 | Royal Paladin | — | RR | +| BT01/010 | Solitary Knight, Gancelot | 3 | Royal Paladin | — | RR + SP | +| BT01/011 | Flash Shield, Iseult | 1 | Royal Paladin | — | RR | +| BT01/012 | Future Knight, Llew | 0 | Royal Paladin | **Critical** | RR | +| BT01/013 | Vortex Dragon | 3 | Kagero | — | RR + SP | +| BT01/014 | Dragon Knight, Aleph | 2 | Kagero | — | RR | +| BT01/015 | Wyvern Guard, Barri | 1 | Kagero | — | RR | +| BT01/016 | Lizard Soldier, Conroe | 0 | Kagero | — | RR | +| BT01/017 | Maiden of Libra | 2 | Oracle Think Tank | — | RR + SP | +| BT01/018 | Battle Sister, Mocha | 2 | Oracle Think Tank | — | RR | +| BT01/019 | Battle Sister, Chocolat | 1 | Oracle Think Tank | — | RR | +| BT01/020 | Juggernaut Maximum | 3 | Spike Brothers | — | RR | +| BT01/021 | Knight of Silence, Gallatin | 2 | Royal Paladin | — | R | +| BT01/022 | Dragon Knight, Nehalem | 2 | Kagero | — | R | +| BT01/023 | Wyvern Strike, Tejas | 2 | Kagero | — | R | +| BT01/024 | Embodiment of Spear, Tahr | 0 | Kagero | **Critical** | R | +| BT01/025 | Oracle Guardian, Apollon | 3 | Oracle Think Tank | — | R | +| BT01/026 | Oracle Guardian, Wiseman | 2 | Oracle Think Tank | — | R | +| BT01/027 | Lozenge Magus | 0 | Oracle Think Tank | **Heal** | R + SP | +| BT01/028 | Mr. Invincible | 3 | Nova Grappler | — | R | +| BT01/029 | Brutal Jack | 2 | Nova Grappler | — | R | +| BT01/030 | King of Sword | 2 | Nova Grappler | — | R | +| BT01/031 | Queen of Heart | 1 | Nova Grappler | — | R | +| BT01/032 | Battleraizer | 0 | Nova Grappler | **Stand** | R + SP | +| BT01/033 | Tyrant, Deathrex | 3 | Tachikaze | — | R | +| BT01/034 | Assault Dragon, Blightops | 2 | Tachikaze | — | R | +| BT01/035 | Stealth Dragon, Voidmaster | 3 | Nubatama | — | R | +| BT01/036 | Demon Eater | 3 | Dark Irregulars | — | R | +| BT01/037 | Monster Frank | 3 | Granblue | — | R | +| BT01/038 | Commodore Blueblood | 2 | Granblue | — | R | +| BT01/039 | Hell Spider | 3 | Megacolony | — | R | +| BT01/040 | Bloody Hercules | 2 | Megacolony | — | R | +| BT01/041 | Covenant Knight, Randolf | 2 | Royal Paladin | — | C | +| BT01/042 | Little Sage, Marron | 1 | Royal Paladin | — | C | +| BT01/043 | Lake Maiden, Lien | 1 | Royal Paladin | — | C | +| BT01/044 | Wingal | 1 | Royal Paladin | — | C | +| BT01/045 | Weapons Dealer, Govannon | 0 | Royal Paladin | **Draw** | C | +| BT01/046 | Flogal | 0 | Royal Paladin | **Stand** | C | +| BT01/047 | Yggdrasil Maiden, Elaine | 0 | Royal Paladin | **Heal** | C | +| BT01/048 | Embodiment of Armor, Bahr | 1 | Kagero | — | C | +| BT01/049 | Dragon Monk, Gojo | 1 | Kagero | — | C | +| BT01/050 | Wyvern Strike, Jarran | 1 | Kagero | — | C | +| BT01/051 | Dragon Dancer, Monica | 0 | Kagero | **Draw** | C | +| BT01/052 | Lizard Soldier, Ganlu | 0 | Kagero | **Stand** | C | +| BT01/053 | Dragon Monk, Genjo | 0 | Kagero | **Heal** | C | +| BT01/054 | Oracle Guardian, Gemini | 1 | Oracle Think Tank | — | C | +| BT01/055 | Weather Girl, Milk | 1 | Oracle Think Tank | — | C | +| BT01/056 | Oracle Guardian, Nike | 0 | Oracle Think Tank | **Critical** | C | +| BT01/057 | Dream Eater | 0 | Oracle Think Tank | **Draw** | C | +| BT01/058 | Miracle Kid | 0 | Oracle Think Tank | **Draw** | C | +| BT01/059 | Hungry Dumpty | 2 | Nova Grappler | — | C | +| BT01/060 | Tough Boy | 1 | Nova Grappler | — | C | +| BT01/061 | Screamin' and Dancin' Announcer, Shout | 1 | Nova Grappler | — | C | +| BT01/062 | Clay-doll Mechanic | 1 | Nova Grappler | — | C | +| BT01/063 | Shining Lady | 0 | Nova Grappler | **Critical** | C | +| BT01/064 | Lucky Girl | 0 | Nova Grappler | **Stand** | C | +| BT01/065 | Ring Girl, Clara | 0 | Nova Grappler | **Heal** | C | +| BT01/066 | Sonic Noa | 1 | Tachikaze | — | C | +| BT01/067 | Ironclad Dragon, Shieldon | 0 | Tachikaze | — | C | +| BT01/068 | Stealth Beast, Chigasumi | 2 | Nubatama | — | C | +| BT01/069 | Stealth Dragon, Dreadmaster | 1 | Nubatama | — | C | +| BT01/070 | Stealth Beast, Hagakure | 0 | Nubatama | — | C | +| BT01/071 | Blue Dust | 2 | Dark Irregulars | — | C | +| BT01/072 | Nightmare Baby | 1 | Dark Irregulars | — | C | +| BT01/073 | Rock the Wall | 0 | Dark Irregulars | — | C | +| BT01/074 | Highspeed, Brakki | 2 | Spike Brothers | — | C | +| BT01/075 | Wonder Boy | 1 | Spike Brothers | — | C | +| BT01/076 | Redshoe, Milly | 0 | Spike Brothers | — | C | +| BT01/077 | Dandy Guy, Romario | 1 | Granblue | — | C | +| BT01/078 | Guiding Zombie | 0 | Granblue | — | C | +| BT01/079 | Karma Queen | 1 | Megacolony | — | C | +| BT01/080 | Madame Mirage | 0 | Megacolony | — | C | + +**SP-принты (12):** 001, 002, 003, 004, 006, 007, 008, 010, 013, 017, 027, 032. + +--- + +## 2. TD01 «Blaster Blade» (Royal Paladin) — 50 карт + +G0 17 / G1 14 / G2 12 / G3 7. + +| Код | Кол-во | Карта | G | Триггер | +|---|---|---|---|---| +| TD01/001 | 4 | Crimson Butterfly, Brigitte | 3 | — | +| TD01/002 | 1 | Knight of Conviction, Bors | 3 | — | +| TD01/003 | 2 | Solitary Knight, Gancelot | 3 | — | +| TD01/004 | 4 | Knight of Silence, Gallatin | 2 | — | +| TD01/005 | 1 | Blaster Blade | 2 | — | +| TD01/006 | 3 | Knight of the Harp, Tristan | 2 | — | +| TD01/007 | 4 | Covenant Knight, Randolf | 2 | — | +| TD01/008 | 4 | Little Sage, Marron | 1 | — | +| TD01/009 | 2 | Wingal | 1 | — | +| TD01/010 | 4 | Starlight Unicorn | 1 | — | +| TD01/011 | 4 | Knight of Rose, Morgana | 1 | — | +| TD01/012 | 1 | Stardust Trumpeter | 0 | — (стартовый вэнгард) | +| TD01/013 | 4 | Bringer of Good Luck, Epona | 0 | Critical | +| TD01/014 | 4 | Yggdrasil Maiden, Elaine | 0 | Heal | +| TD01/015 | 4 | Weapons Dealer, Govannon | 0 | Draw | +| TD01/016 | 4 | Flogal | 0 | Stand | + +--- + +## 3. TD02 «Dragonic Overlord» (Kagero) — 50 карт + +G0 17 / G1 14 / G2 12 / G3 7. + +| Код | Кол-во | Карта | G | Триггер | +|---|---|---|---|---| +| TD02/001 | 2 | Dragonic Overlord | 3 | — | +| TD02/002 | 1 | Dragon Monk, Goku | 3 | — | +| TD02/003 | 4 | Demonic Dragon Berserker, Yaksha | 3 | — | +| TD02/004 | 4 | Dragon Knight, Nehalem | 2 | — | +| TD02/005 | 4 | Berserk Dragon | 2 | — | +| TD02/006 | 4 | Wyvern Strike, Tejas | 2 | — | +| TD02/007 | 4 | Embodiment of Armor, Bahr | 1 | — | +| TD02/008 | 2 | Dragon Monk, Gojo | 1 | — | +| TD02/009 | 4 | Flame of Hope, Aermo | 1 | — | +| TD02/010 | 2 | Demonic Dragon Madonna, Joka | 1 | — | +| TD02/011 | 2 | Wyvern Strike, Jarran | 1 | — | +| TD02/012 | 1 | Lizard Runner, Undeux | 0 | — (стартовый вэнгард) | +| TD02/013 | 4 | Dragon Dancer, Monica | 0 | Draw | +| TD02/014 | 4 | Lizard Soldier, Ganlu | 0 | Stand | +| TD02/015 | 4 | Dragon Monk, Genjo | 0 | Heal | +| TD02/016 | 4 | Demonic Dragon Mage, Rakshasa | 0 | Critical | + +--- + +## 4. Выводы для движка и данных + +### Триггеры +- В сете ровно **4 триггера каждого типа** на набор: 4 Critical, 4 Draw, 4 Stand, 4 Heal. +- В BT01: Critical — 012, 024, 056, 063; Draw — 045, 051, 057, 058; Stand — 032, 046, 052, 064; Heal — 027, 047, 053, 065. +- **Стартовый вэнгард — grade 0 без триггера.** В TD01 это Stardust Trumpeter, в TD02 — Lizard Runner, Undeux. В BT01 таких 8: 003, 016, 067, 070, 073, 076, 078, 080. + +### Щит +- **Draw-триггеры имеют 5000 щита, все остальные триггеры — 10000.** Проверено по официальному карт-листу (Govannon, Monica, Dream Eater, Miracle Kid = 5000; Llew, Tahr, Nike, Shining Lady, Flogal, Ganlu, Lucky Girl, Elaine, Genjo, Clara = 10000). +- Grade 0 без триггера — 10000. +- Для карт G1–G3 щит задаётся индивидуально (Sentinel — 0). + +Это даёт инвариант для валидатора: у grade 0 `shield == 5000` тогда и только тогда, когда `triggerType == Draw`. + +### Редкости +- BT01: RRR ×8, RR ×12, R ×20, C ×40, SP ×12. +- SP — это те же карты, что и RRR/RR/R, отдельным принтом. Раскладка SP: 001, 002, 003, 004, 006, 007, 008, 010, 013, 017, 027, 032. + +### Состав триал-дек +- Обе колоды: **17 / 14 / 12 / 7** по грейдам 0/1/2/3 — удобный ориентир для будущих пресетов. +- В каждой: 16 триггерных юнитов + 1 нетриггерный grade 0. + +### Расхождения в источниках (учесть в пайплайне) +- Wiki пишет «7 карт эксклюзивны» для TD01, но перечисляет 5. По факту эксклюзивны **7**: Brigitte, Bors, Tristan, Starlight Unicorn, Morgana, Stardust Trumpeter, Epona. +- Для TD02 wiki пишет «2 эксклюзивные», перечисляет 3. По факту **7**: Goku, Yaksha, Berserk Dragon, Aermo, Joka, Undeux, Rakshasa. +- Утверждения в текстах wiki проверять по таблицам карт, а не наоборот. + +--- + +## 5. Структура бустера + +- **5 карт в упаковке.** +- **1 карта гарантированно R или выше**, остальные 4 — C. +- Значит генерация слотовая: `4 × C` + `1 × взвешенный R+`. Гарантия должна выполняться всегда, поэтому нельзя генерировать «5 карт по редкости» и потом проверять. +- SP-принты (12 шт.) — отдельный исход в слоте R+. Для duplicate protection это отдельный принт: 4 обычных Blaster Blade не блокируют выпадение SP02. +- Веса слота R+: **R 70 % / RR 22 % / RRR 7 % / SP 1 %** (порядок убывания соблюдён, сумма 100 %). +- На коробку из 30 бустеров: ~21 R, ~6,6 RR, ~2,1 RRR, ~0,3 SP. + +**Правило дублей:** ключ — `printingId` (номер + арт), порог 4 копии. Если принт уже в 4 экземплярах — замена внутри той же редкости. Если заменять нечем — карта падает в коллекцию, и игрок сам разбирает её в пыль. + +Следствие для темпа: за ~40 бустеров набирается 160 C (это ровно 40 карт × 4, то есть полный C-сет), но только ~40 карт слота R+. R+ — долгий хвост: 40 карт × 4 = 160 копий ≈ 160 бустеров. + +--- + +## 6. Что ещё нужно достать + +- [ ] Сила и щит для 14 карт, которых нет в BT01 (TD01/001–002, 006, 010–013; TD02/002–003, 005, 009–010, 012, 016) — брать с карточных страниц. +- [ ] Русские названия и тексты для всех 94 карт. +- [ ] Веса слота R+ (R / RR / RRR / SP) — официальные пропорции BT01 или наше решение. +- [ ] Какие 3 карты в каждой триал-деке идут фойловыми. diff --git a/plan.md b/plan.md new file mode 100644 index 0000000..562c442 --- /dev/null +++ b/plan.md @@ -0,0 +1,283 @@ +# CF!! Vanguard — Сводка наработок проекта + +## 1. Общие принципы +- Фанатский проект без монетизации. Никаких реальных денег, твёрдой валюты, премиум-баттлпасса. Дисклеймер: "Fan-made project. Not affiliated with Bushiroad. All card names and art belong to their respective owners." +- Авторитарный сервер. Вся игровая логика на сервере. Клиент — тонкий терминал. Клиент просит, сервер решает. +- Скрытая информация. Клиент A не получает данных о руке клиента B. +- Два языка: русский и английский. UI — Unity Localization, карты — поля в БД. +- Единый стек C#: клиент (Unity), сервер (ASP.NET Core + GeekServer), лаунчер (WPF). +- Движок: Unity (финальный выбор). Не UE. + +## 2. Архитектура и технологии + +### Мета-сервер (Backend API) +- C# .NET 8, ASP.NET Core Web API +- JWT + BCrypt, Entity Framework Core, PostgreSQL +- Redis (сессии, очереди, онлайн-статус) +- SignalR (чат, уведомления) + +### Игровой сервер +- C# .NET 8, GeekServer (Actor-модель) +- TCP, MessagePack +- Redis (состояние матча) + +### Клиент +- Unity 2022 LTS / Unity 6 +- Netcode for GameObjects (игровой протокол) +- SignalR Client (социалка) +- MessagePack / MemoryPack +- ScriptableObject (данные карт) +- DOTween / корутины (анимации) +- Unity Localization (UI) +- Input System (тач + клавиатура/мышь) + +### Лаунчер +- C# + WPF (.NET 8) +- Game Launcher (Asset Store) — бинарные дифф-патчи +- CDN / S3 для патчей +- RESX для локализации + +### Docker +- postgres:16-alpine, redis:7-alpine +- meta-server, game-server, nginx +- Сети: backend (internal), frontend +- Volumes: postgres_data, redis_data +- .env с паролями, nginx как reverse proxy (HTTP + TCP stream) + +### Подключение агента +- OpenCode + MCP Unity (CoderGamester) или unity-open-mcp +- Node.js 18+, Unity 2021.3+ (или Unity 6 для официального MCP) + +## 3. Модель данных карт +CardData: id, grade, power, critical, shield, nation, clan, rarity (Common/Uncommon/Rare/Legendary/RRR), name_ru/name_en, effect_text_ru/effect_text_en (с rich-text тегами и плейсхолдерами {cb}, {sb}), flavor_ru/flavor_en, skills[] (type, trigger, conditions, costs, effects, target), imageUrl. + +Глоссарий: ключевые слова Vanguard (Ride, Call, Drive Check, Damage Check, Counter Blast, Soul Blast, Stand, Rest, Guard, Boost, Intercept) на двух языках. Тексты карт используют плейсхолдеры, подставляемые из глоссария. + +## 4. Механики Vanguard (все заложены сразу, карты выпускаются постепенно) + +Базовые: Ride, Call, Boost, Intercept, Guard, Drive Check, Damage Check. Зоны: Vanguard Circle, Rear-Guard, Guardian, Soul, Damage, Drop, Bind, Trigger, G Zone. Состояния: Stand/Rest, Lock. Фазы: Stand, Draw, Ride, Main, Battle, End. + +Триггеры: Critical, Draw, Stand, Heal, Front, Over Trigger. + +По эрам: +- Limit Break (4+ урона) +- Break Ride (тот же клан, +10000) +- Legion (Leader + Mate, Seek Mate) +- Stride / G Era (Heart Card, G Guardians) +- V Series (Imaginary Gift: Force/Accel/Protect) +- D Series (Ride Deck, Persona Ride, Over Trigger) + +Реализация: каждая механика — отдельный модуль. Rule Engine применяет все, карты определяют активные. Check Timing обрабатывает порядок срабатывания. + +## 5. Способности карт +AbilityData: type (Continuous/Activated/Triggered), triggerEvent, conditions[], costs[], effects[], target. Три обработчика. Шина событий в GameActor. Check Timing: событие → проверка триггеров → разрешение → повтор. Условия — отдельные Evaluate()-объекты. + +## 6. Режимы игры + +Варианты (форматы): Standard (делится на Эры), V, D. + +Ранкед и Казуал — по вариантам (6 очередей). Рейтинг — по вариантам (3 MMR у игрока). Эры — только для турниров и ивентов (модификатор пула). Standard Ranked по умолчанию — весь пул Standard. + +Casual: матчмейкинг просто по онлайну, без критериев. + +Приватные матчи: приглашение друга → принятие → выбор колоды → матч. XP не начисляется, результат в историю пишется. + +Бот: пока не делаем. + +## 7. Турниры + +Одиночные: 1х1, Swiss + Top Cut, 35 мин/раунд, Double Loss при таймауте. Фиксация колод при регистрации. + +Командные 3х3: три параллельных 1х1. Капитаны видят составы и кланы колод соперников (не полный состав), выбирают порядок. Победа по сумме очков (2:1 или 3:0). Техническое поражение только в конкретной паре. + +История: все турниры хранятся. После завершения снимки колод становятся публичными (полный состав), карты разблокируются. Архив меты. + +## 8. Команды + +Структура: полное название, короткий тег (2-5 символов), иконка. Префикс [TAG] в никнейме везде. + +Типы: открытая (заявки) / закрытая (только приглашения). + +Роли: +- Лидер: полный контроль, переименование, иконка, назначение офицеров, кик, передача лидерства, роспуск. +- Офицеры (1-3): одобряют заявки, приглашают. Не могут переименовать, сменить иконку, удалить. +- Участники: играют, общаются, выходят. + +Передача лидерства: только офицеру, раз в 30 дней (кулдаун от последней передачи). + +Лимит участников: зависит от уровня команды (1-10). + +## 9. Командные награды и территории + +Карта наций: 6 регионов (United Sanctuary, Dragon Empire, Star Gate, Zoo, Megalanica, Dark Zone). Территории внутри регионов (столица, ключевые точки, аутпосты). + +Очки контроля: за победы в командных матчах. В конце цикла (неделя) команда с наибольшим количеством очков становится владельцем. + +Награды: Gold, пыль, командный XP, рейтинг. Лимит 3 территории на команду. Убывающая отдача за длительное владение. + +Командный рейтинг: глобальный лидерборд команд. + +## 10. Рейтинговая система + +Сезон = месяц. Совпадает с баттлпассом. Сброс 1-го числа. + +Ранги: 6 тиров × 5 дивизионов (Bronze, Silver, Gold, Platinum, Diamond, Master). Master — без дивизионов, только MMR и лидерборд. + +Звёзды: победа +1, поражение -1. 5 звёзд = +1 дивизион. Защита от падения: Bronze/Silver — нет, Gold/Platinum/Diamond — до пола тира, Master — свободно. + +Сброс: Master → Platinum 1, Diamond → Gold 1, Platinum → Silver 1, Gold → Bronze 1, Silver/Bronze — без изменений. + +Награды за ранг: Gold, пыль, Master — эксклюзивная рубашка + титул. + +MMR: скрытый, для матчмейкинга. Бонусные звёзды при высоком MMR. + +## 11. Экономика + +Валюты: Gold (основная), пыль (за разбор карт), токены Draft (за 10 побед в Draft Run). + +Цены: бустер = 100 Gold. Draft Run = 1000 Gold (10 бустеров). + +Пыль: +- Разбор: Common 5, Uncommon 10, Rare 25, RRR 100. +- Крафт: ×4 (Common 20, Uncommon 40, Rare 100, RRR 400). +- Фойл: ×2. +- Объединение 4 обычных → 1 фойл (альтернатива). +- Разбор любой карты, включая последнюю копию — с предупреждением. +- Карты в колодах (включая турнирные снимки) защищены от разбора до конца турнира. + +Duplicate Protection: работает для покупных бустеров (общая коллекция). Бонусные бустеры внутри Draft Run — только в рамках захода (лимит 4 копии). Бустеры за токены — стандартная защита. + +Баттлпасс: 75 уровней / 30 дней. XP на уровень: 1-10 = 250, 11-40 = 700, 41-75 = 1000. Всего ~58 500 XP. Награды: 15 бустеров (каждый 5-й уровень), пропуск Draft на уровнях 40 и 75, ~4000 Gold, ~1200 пыли. Переполнение (76+) — только 50 Gold/уровень. + +XP за матчи: победа 150, поражение 75, ничья 100. Draft Run: победа 200, поражение 100. Турниры: участие 500, победа 250, Top Cut 1000, чемпион 2000. + +Ежедневные задания: 3 задания, 1/3/5 матчей → 300 XP каждое. Еженедельное: 20 матчей → 2000 XP. Только XP. + +Засчитывается матч: сыгран до конца, без сдачи, без АФК. Приватные матчи — не засчитываются. + +Draft Run: вход 1000 Gold → 10 бустеров. Duplicate Protection только в рамках захода (лимит 4 копии). 5 побед → +5 бустеров. 10 побед → +5 токенов на любой бустер. Без пыли. Без лимита поражений. + +## 12. Источники карт +Готовые колоды: фиксированный набор, покупка за Gold. +Бустеры: рандом по редкости, серверный рандом. Логи открытий. +Фойлы: is_foil в коллекции, визуальный вариант. +Промокарты: за ачивки/события. + +## 13. Колоды +Лимиты: 30 обычных колод на вариант (Standard, V, D) + 10 турнирных. Турнирные привязаны к эре/турниру. + +Сайд-дек: 5 карт, отдельная секция. Валидация: main + side ≤ 4 копий одной карты. + +Шаринг: код колоды + ссылка. + +Оценка: просмотры + лайки. Опционально «вес колоды» для матчмейкинга. + +Защита от разбора: карты в колодах заблокированы. После турнира — разблокированы. + +## 14. Локализация +UI: Unity Localization, String Tables (en/ru), runtime switching. +Карты: поля _ru/_en в БД, сервер отдаёт по параметру lang. +Глоссарий: ключевые слова Vanguard на двух языках. +Лаунчер: RESX или общий CSV. + +## 15. Социалка +Друзья: добавление, удаление, чёрный список, онлайн-статус (Redis). +Чат: общий, командный, с друзьями (SignalR). +Эмоции: ID вместо текста, кулдаун 2-3 сек, мьют (локальный per-match + глобальный в настройках). Отправка своих не блокируется. +Гайды: публикация колоды + описание, лайки. Если нет лайков за неделю — удаляется. +Фидбек: отдельная вкладка для отзывов о мете и предложений. + +Голосовой чат: убран полностью. + +## 16. Модерация +Администратор: доступ к логам (чат, матчи, транзакции, IP), разбор обращений, глобальные баны, назначение модераторов. +Модератор: только чат — удаление сообщений, мут (временный/постоянный). Не видит логи матчей, IP, транзакций. + +Мут: временный (15 мин, 1 час, 24 часа, 7 дней) или постоянный. Не блокирует игру, только чат. +Логи: удалённые сообщения не исчезают из БД. +Обращения: жалобы, апелляции, баг-репорты, пожелания. Маршрутизация по типу. + +## 17. Профиль игрока +Видно другим: ник + префикс, аватар, рамка, титул. Статистика по режимам (игры, % побед), любимый клан, рейтинг, история турниров и ивентов, достижения, публичные колоды. + +Настройки приватности: скрыть статистику, историю, достижения, колоды. + +## 18. Косметика +Типы: карт-бэки, аватары, рамки профиля, эмоции, эффекты поля, титулы, музыка/звуки. + +Источники: достижения, турниры, баттлпасс, промо-события, рейтинг. + +Ключевое: не влияет на геймплей. + +## 19. Достижения +Категории: победы, кланы, коллекция, турниры, Top Cut, механики, Draft Run, социальное, прогрессия. + +Типы: разовые, прогрессивные (с уровнями), скрытые. + +## 20. Прогрессия аккаунта +Грейды: 0 (Новичок) → 5 (Легенда). XP за матчи, достижения, турниры. + +Награды: Gold, пыль, слоты колод, косметика, доступ к режимам. + +## 21. Обучение +Тестовый матч: скриптованный, локальный, без сервера. TutorialGameManager, TutorialStep (ScriptableObject). Покрытие Ride → Call → Boost → Attack → Drive/Damage Check. Кнопка «Пропустить». + +Экскурсия по клиенту: ClientTourManager, подсветка элементов (меню, коллекция, декбилдер, бустеры, задания, баттлпасс, настройки, друзья). Повторный запуск из настроек. + +Продвинутые туториалы: по каждой ключевой механике (отдельно). + +## 22. Промо-события +Турниры к выходу сета: участие, награды по местам (промо, рубашки, титулы, бустеры). +Другие: «Неделя клана», «Двойной опыт», «Легендарный Draft», «Ретро-турнир». + +## 23. Мета и баланс +Инструменты: бан карты, ограничение количества, условный бан (нельзя X если есть Y). +Источник: фидбек игроков, статистика матчей, голосования. +Объявления: патчноуты в отдельной вкладке. + +## 24. Реконнект +- Автоматический через OnClientDisconnectCallback. +- Экспоненциальная задержка: 0, 2, 10, 30 сек. +- Индикатор «Переподключение...», кнопка «Отменить». +- Таймаут на ход при обрыве. +- Реконнект только при неожиданном обрыве. +- Сессия в Redis с TTL на время матча. + +## 25. Мобильная адаптация +- Input System: тап, долгое нажатие, drag, свайп. Одна схема с двумя наборами привязок. +- Canvas Scaler, Safe Area. +- ASTC-сжатие текстур, атласы UI, стриминг аудио. +- Target Frame Rate: 30 (мобила), 60 (десктоп). +- Adaptive Performance. +- Мониторинг connectivity, UI офлайн-состояния. +- OnApplicationPause → пауза матча. +- Лаунчер: десктоп — WPF, мобила — Addressables (CDN). + +## 26. Что ещё не обговорено (отметить в чеклисте) +- Полный список кланов и наций (какие войдут, сколько). +- Анимированные карты / альтернативные арты. +- Особые версии карт (SP, RRR+, Parallel). +- Инфляция Gold (что ещё купить). +- Сброс пыли (сгорает ли в конце сезона). +- Токены Draft (копятся ли, лимит). +- Полный список косметики (сколько предметов). +- Хранение косметики (где лежит, как выбирается). +- Обмен косметикой. +- Версионирование клиента (что если не обновился). +- Логи и аналитика (что логируем, где храним). +- Бэкапы (как часто, где). +- Мониторинг (как следим за сервером). +- Масштабирование (что если игроков в 10 раз больше). +- Сторы (Google Play / App Store / только APK). +- Размер клиента (как оптимизируем). +- Батарея и нагрев (конкретные настройки). +- Продвинутые туториалы (формат каждого). +- Обучение старым эрам. +- Частота балансных правок. +- Бан-лист (где смотреть). +- Фидбек-система (модерация, голосование). +- Приватность профиля (что скрыть). +- История матчей (как глубоко хранится). +- Статистика по картам (winrate, популярность). +- Безопасность (мультиаккаунты, читы, 2FA). +- Маркетинг и комьюнити (сайт, Discord, соцсети). \ No newline at end of file