Base version: design docs, card catalog, implementation checklist

This commit is contained in:
MpakobecEx 2026-10-20 21:12:37 +05:00
commit 83fd1b74d1
6 changed files with 1474 additions and 0 deletions

31
.gitattributes vendored Normal file
View File

@ -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

56
.gitignore vendored Normal file
View File

@ -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

620
DEVELOPMENT_CHECKLIST.md Normal file
View File

@ -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}` → локализованный термин из глоссария; поддержка `<b>`, `<i>`, переносов.
- [ ] **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, соцсети — кто и когда.

283
IMPLEMENTATION_CHECKLIST.md Normal file
View File

@ -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. Доставка** | Чистая машина → установка → игра |

201
content/cards-mvp.md Normal file
View File

@ -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 карты в каждой триал-деке идут фойловыми.

283
plan.md Normal file
View File

@ -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 тегами <b> и плейсхолдерами {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, соцсети).