VanguardReMaster/.opencode/skills/vanguard-remaster/workflow-and-decisions.md

195 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Решения, процесс и антипаттерны
## 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]** Камера в покое фиксирована по центру поля с небольшим наклоном;
временно двигается только `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. Процесс разработки
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 недоступна; спорные места не считаются
восстановленными.