# Решения, процесс и антипаттерны ## 1. Источники аудита **[C]** Проверены: - `.opencode/skills/vanguard-remaster/SKILL.md` (прежняя версия); - `AGENTS.md`, `docs/SESSION-STATE.md`, `docs/HANDOFF.md`, `docs/game-rules.md`, `docs/ability-implementation.md`, `docs/protocol.md`, `docs/client-visual-direction.md`, `docs/3d-ux-backlog.md`, `docs/translation-rules.md`; - локальная база OpenCode: две сессии Vanguard ReMaster (1259 и 5617 сообщений), включая сообщения пользователя и финальные тексты ассистента; - исходный код server/client, YAML-контент, тесты, `README.md`, `DEVELOPMENT_CHECKLIST.md`, `content/cards-mvp.md`. История доступна локально, но не является «полной истиной»: часть решений могла быть принята вне OpenCode. В спорных местах ниже стоит **[O]**, а не выдуманное правило. ## 2. Подтверждённые решения ### Игра и контент - **[D]** Фанатский ремейк Vanguard, формат **Classic**, BT01 + TD01/TD02, 94 карты / 124 принта. - **[D]** Две триал-деки: TD01 «Blaster Blade», TD02 «Dragonic Overlord»; полный декбилдер. - **[D]** Официальные арты; локализация EN + RU. - **[D]** В коллекции нет лимита копий/артов; 4 копии по имени — только при сборке колоды. - **[D]** Смешивать кланы в колоде разрешено; эффекты с клановыми условиями могут не работать. Это осознанное правило проекта. - **[D]** Сайд-дека нет, матч BO1. - **[D]** Ride Deck/Persona Ride не входят в Classic/текущую игру. - **[D]** Стартовый авангард — grade 0 без триггера; после обучения игрок получает триал-деку на выбор и 5 токенов; 1 токен = 1 бустер. - **[D]** Таймер: 90 с на ход + банк 5 мин; отсутствие/сдача/разрыв — поражение. - **[D]** В первый ход нельзя атаковать и переходить в Battle. - **[D]** Локализация: RU+EN; «ё» разрешена; «усиленная карта»; «уничтожить» оставляем; фазы — Основная/Боевая/Завершающая/Подъёма/Добора/Смены; close step — завершающий шаг; call — призвать; названия зон не переводим; имена карт остаются английскими (`nameRu` — задел). Детали — `docs/translation-rules.md`. ### Бустеры и экономика - **[D]** В бустере 5 карт: 4 C + 1 гарантированная R+. - **[C]** Текущие веса R+: R 70% / RR 22% / RRR 7% / SP 1% (`content/pack_odds.yaml`). Пользователь поручил подобрать вероятности, но точные числа не перепроверял; это конфиг, а не вечный закон. - **[D]** Дубли считаются по `printingId`, порог 4; SP — отдельный принт; при отсутствии замены карта остаётся в коллекции и разбирается в пыль. - **[D]** Gold начисляется за победу и поражение; **[C]** текущая конфигурация — 30 / 15 / 20 (победа/поражение/ничья), Gold = XP / 5. Точные числа — балансный конфиг, можно менять. - **[C]** Бустер 100 Gold, триал-дека 500 Gold; за сдачу/АФК награды не начисляются (`RewardsForForfeit = false` по умолчанию). - **[C]** Пыль: разбор C/R/RR/RRR = 5/10/25/100, крафт ×4; фойл ×2. - **[C]** XP пишется с первого дня; баттлпасс/квесты/Draft — после MVP. ### Клиент и UX - **[D]** 3D-сцена, отдельные сцены Login/Lobby/DeckBuilder/Battle; HUD — uGUI; reference 1920×1080. - **[D]** Анимации описываются для локального игрока. Для противника применяются те же правила, но зеркально с его стороны поля: направление, сторона появления карты, наклон веера и порядок перекрытия зеркальны; камера также отражается относительно центра стола. Отдельные «противнические» анимации не делать, если это не подтверждённое исключение. - **[D]** Камера в покое фиксирована по центру поля с небольшим наклоном; временно двигается только `CameraDirector` в trigger-последовательности. - **[D]** Выбор карты/круга/зоны/цели — клик/драг по объекту; кнопки только для глобальных действий/подтверждений. - **[D]** Guard: карты из руки с grade ≤ grade своего Vanguard; одна кнопка «Не защищаться»/«Защищаться»; выбранные подсвечиваются, щит считается сразу, после подтверждения — в Guardian. - **[D]** Карты противника нельзя драгать; можно выбирать целью, смотреть детали и зоны. Клик по своей карте — меню валидных действий. - **[D]** Зоны открываются общим горизонтальным `CardListView`; сверху — последняя добавленная карта; из списка доступен `CardDetailOverlay`. - **[D]** Call в занятый RC разрешён; старый юнит → Drop. - **[D]** Move: RC↔RC в колонке; занятые меняются; при единственном доступном круге клиент перемещает сразу. - **[D]** Draw: после цели силы сразу тянет карту; Heal: только своя зона урона и только при условиях; Tejas: задний ряд своей колонки. - **[D]** Optional AUTO — оверлей «Активировать/Пропустить»; ACT из руки — меню карты; оплата — по объектам, подтверждение в панели/оверлее. - **[D]** Mulligan: выбор карт в руке; после замены этап завершается автоматически, неактивная кнопка «Закончить муллиган» не остаётся. - **[D]** Панель фаз переименована в «Действия» и содержит guard, подтверждение оплаты и глобальные действия; список всех возможных смен там не показывается. - **[D]** Под картами V/R показываются динамические Power/Critical; верхний `CombatStats` strip — допустимый HUD; world-space Power/Shield — отложенный долг. - **[D]** Отдельный `CostPaymentOverlay` для multi-target стоимости нужен; текущий CB в панели «Действия» — промежуточная реализация. ## 3. Фактическое устройство (не путать с договорённостью) - **[C]** `GameEngine` — авторитарное ядро; `MatchGrain` сериализует интенты; `MatchProjection` скрывает информацию. - **[C]** Multi-target CB уже поддержан `DamageIndexes` в `ActivateAbilityIntent`; старый `DamageIndex` оставлен. - **[C]** `CardMoveAnimator`, `CameraDirector`, `TriggerCheckSequence`, `ArrowDirector` есть; анимации на `Tick`, без UniTask/DOTween. - **[C]** `CardMoveAnimator` имеет таймаут и `CancelCard`; это исправление зависшего `card-move`-лока (коммит `fd21a96`). - **[C]** `DropZoneFaceDown` есть, default `false`. - **[C]** `DeckValidator` не проверяет единство клана; это соответствует решению «смешивать можно». - **[C]** `CombatStats` — верхний uGUI strip; отдельного боевого overlay нет. - **[C]** `docs/protocol.md` ранее указывал неверный порядок отображения урона; фактически сначала открытые, затем закрытые. ## 4. Процесс разработки Формальный двухэтапный процесс (анализ → план → реализация; обе стадии выполняет DeepSeek, но раздельно) описан в `docs/Development/AGENT-WORKFLOW.md`; активный план задачи — в `docs/Development/IMPLEMENTATION_PLAN.md`. Ниже — краткие правила, без дублирования шаблона плана. 1. Прочитай Skill и связанные docs; найди существующую реализацию. 2. Проверь, не решён ли вопрос в коде/истории. Не спрашивай о том, что уже установлено. 3. Если есть существенная неопределённость — `question` с 2–4 вариантами. 4. Вноси минимальные изменения; не трогай несвязанное. 5. Для правил: тест на сценарий; для карт — тест на триггер/активацию; для клиента — EditMode/smoke. 6. Серверные проверки — только `ssh home`/Docker; Unity batch — копия без `Library`. 7. После блока: `validate`, `translations check`, коммит/push. 8. Геймплей вручную проверяет пользователь; не выдавай свой тест за приёмку. ## 5. Антипаттерны | Не делать | Почему | Как делать | |---|---|---| | Список кнопок вместо клика по карте/кругу | Нарушает принятый UX | Подсветка объектов + клик/драг | | `transform.position = ...` в игровой логике | Ломает анимации/локи | `Card3D`/`CardMoveAnimator`/`SetPose` | | Дублировать эффект в нескольких `CardScript` | Расходится поведение | Один `CardScript`, общий движок или флаг | | `OnPlace` без проверки VC/RC | Срабатывает не в той зоне | `IsVanguardOf`/`IsRearGuardOf` | | Менять `GameState` на клиенте | Рассинхрон с сервером | Только `IntentPayload`, рисовать snapshot | | Запускать `dotnet`/Docker локально | Запрещено пользователем | `ssh home`/Docker | | Второй Unity с тем же проектом | Ломает Library/импорт | Копия без `Library` | | Заявлять тест без запуска | Вводит в заблуждение | Указать, что проверено, а что нет | | Самому проходить геймплей как приёмку | Пользователь проверяет лично | Автотесты + передать сценарий | | Менять `Vanguard.Shared` без пересборки DLL | Unity увидит старый контракт | Собрать netstandard2.1 и заменить DLL | | Вечный `InputLock` без таймаута | Блокирует ход/смену | try/finally, timeout, `CancelCard` | | Освобождать карту из пула во время анимации/обмена | Дубли/неактивные карты | Сначала распределить по `InstanceId`, потом release | | Новый универсальный effect/DSL без нужды | Усложняет и дублирует | Расширять существующие `AbilityDefinition` | | Хранить вычисленную силу как истину | CONT пересчитывается | `EffectivePower`/модификаторы | | Показывать все возможные смены в панели | Пользователь это запретил | Действия из меню карты/валидных интентов | | Перехватывать клики информационным оверлеем | Мешает выбору по объекту | `raycastTarget = false`/скрыть ввод | | Считать `printingId` и `nameId` одним | Ломает лимиты/коллекцию | `nameId` — 4 копии, `printingId` — принт | | Perfect Guard даёт щит без оплаты | Неверная защита | Без оплаты 0 щита и нет nullify | | Heal просит цель при невыполненных условиях | Лишний выбор/зависание | Сначала серверная проверка, потом `AskDamageChoice` | ## 6. Известные ошибки и уроки - **Зависший `card-move`-лок** (2026-10-24): карта освобождалась из пула, оставаясь в `_active`; `Tick` больше не вызывался, `InputLock` не снимался. Исправлено таймаутом/`CancelCard`. Не возвращать. - **Дубли/нахлёст при обмене RC**: нельзя освобождать карту из занятого ключа, если она должна переехать в другой ключ. Сначала распределение по `InstanceId`, затем release. - **Conroe** долго не имел AUTO `OnRideOver` — проверять весь текст, а не только очевидные ACT/CONT. - **Barcgal**: при выборе призыва из колоды клиент должен показать оверлей доступных карт; не подставлять первый круг/карту автоматически. - **Ride после хода противника** ломался из-за клиентского ввода/логики, а не только сервера; проверять lock и `LegalIntents`. - **Пустой Mulligan**: после замены карт кнопка «Закончить муллиган» не должна оставаться неактивной; муллиган завершается автоматически. - **Guard дубли**: Guardian и `Battle.GuardCards` могут содержать одну карту; клиент дедуплицирует по `InstanceId`. - **Perfect Guard**: без discard карта даёт 0 щита; не считать Sentinel обычным щитом. - **Trigger check**: карта сначала в Trigger, затем эффект, затем hand/ damage; не переставлять. ## 7. Открытые вопросы - **[O]** Отдельная проверка «авангард исчез» в `GameEngine` отсутствует; нужно ли добавлять — решать пользователю. - **[O]** `OnPlace` + `OnRideOver` при pending-способности `OnPlace`: текущая реализация может не запустить `OnRideOver`; нужно ли чинить — отдельное решение. - **[O]** Multi-target Soul Blast/другие стоимости: протокол и UI ещё не сделаны. - **[O]** `CostPaymentOverlay` требуется, но его форма/`EffectConfig` и момент включения ещё не реализованы; текущий CB — промежуточный. - **[O]** World-space Power/Shield: верхний strip принят как HUD, перенос — долг. - **[O]** `AttackResolutionQueue` и несколько boost-стрелок — будущая архитектура, сейчас бой с одной целью. - **[O]** `DropZoneFaceDown` UX: default `false`; нужен ли другой режим — не решено. - **[O]** Точная легальность формата/banlist и обязательность ровно одного стартового авангарда в `DeckValidator` не выделены. - **[O]** Полная история вне OpenCode недоступна; спорные места не считаются восстановленными.