VanguardReMaster/.opencode/skills/vanguard-remaster/SKILL.md

258 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.

---
name: Vanguard ReMaster
description: Use for any work in the Vanguard ReMaster repository: server rules and card abilities, battle mechanics, WebSocket/MessagePack protocol and networking, Unity client UI/3D board, tests, deployment, or project structure questions.
---
# Vanguard ReMaster
Рабочие правила для этого репозитория. Сначала прочитать
`docs/SESSION-STATE.md`, затем `docs/HANDOFF.md` и
`docs/client-visual-direction.md`.
## 1. Структура проекта
```text
content/
sets/*.yaml карты, принты, тексты, флаги механик
translations/ru.yaml русские тексты и токены
keywords.yaml ключевые слова
docs/ протокол, правила, состояние, аудит способностей
server/
src/Vanguard.Shared/ DTO, MessagePack-контракты, union-типы, проекции
src/Vanguard.Content/ загрузка YAML, каталог карт, валидация контента
src/Vanguard.Rules/ чистое игровое ядро: состояние, команды, события
src/Vanguard.Server/ ASP.NET, auth, meta, matchmaking, Orleans, WebSocket
src/Vanguard.Cli/ validate, stats, translations, stress, deck
tests/Vanguard.Rules.Tests/ тесты правил и карт
tests/Vanguard.Server.Tests/тесты сервера и инфраструктуры
client/Assets/Vanguard/
Runtime/Api/ HTTP-клиент и DTO мета-API
Runtime/Auth/ JWT, refresh, TokenStore
Runtime/State/ PlayerState и клиентское состояние
Runtime/Meta/ каталог карт, коллекция, колоды
Runtime/Battle/ GameConnection, InputLockService, анимации
Runtime/Board3D/ 3D-поле, карты, слоты, зоны, drag&drop
Runtime/UI/ uGUI-экраны и оверлеи
Runtime/App/ сцены, SceneFlow, bootstrap
Editor/ сборка сцен, Windows standalone, smoke-проверки
Tests/Editor/ Unity EditMode тесты
```
Ключевые границы:
- `Vanguard.Rules` — чистая библиотека. Не добавлять в неё сеть, БД,
Orleans, Unity или файловый I/O.
- `Vanguard.Shared` — контракты. `netstandard2.1`-сборка используется Unity,
поэтому там только DTO/сериализация без ссылки на `Vanguard.Rules`.
- `Vanguard.Server` зависит от `Rules`, `Shared`, `Content`; обратной
зависимости быть не должно.
- Клиент не является источником истины: правила, скрытая информация и
случайность остаются на сервере.
## 2. Сетевой код
### Протокол
- Транспорт матча — WebSocket `/game`, бинарные MessagePack-фреймы.
- Контракты и union-номера — только в `Vanguard.Shared/Protocol`.
Не дублировать DTO в клиенте или сервере.
- Изменение union-номера или `[Key(n)]` — ломающее изменение протокола.
Добавлять новые поля/типы, не переиспользовать старые номера.
- `ProtocolConstants.Version` и `MaxFrameBytes` — общие ограничения.
Сервер обязан отклонять неизвестную версию и слишком большой фрейм.
- `ClientIntent` содержит `intentId` (GUID) и `basedOnSeq`; повтор с тем же
`intentId` должен быть идемпотентным. Результат — `ServerIntentResult`.
- `eventSeq` монотонно растёт. Клиент не применяет события старше
`LastSeq`; для пропуска — `ClientResync` и полный snapshot.
- Сервер присылает `ServerHello`, `ServerSnapshot`, `ServerEvents`,
`ServerIntentResult`, `ServerClock`, ping/pong и `ServerError`.
- Скрытые данные (колода, рука противника, закрытый урон, seed) не уходят
клиенту. Проверять каждую новую view/проекцию.
### Сервер
- HTTP-концы и realtime разделены: REST — мета/аутентификация/матчмейкинг,
WebSocket — только матч.
- `GameWebSocketEndpoint` принимает соединение; `MatchConnectionRegistry`
связывает игрока с матчем; `MatchGrain` хранит авторитарное состояние и
обрабатывает интенты.
- Команда проходит: intent → проверка `basedOnSeq`/идемпотентности →
`GameEngine.Execute` → события → snapshot/clock.
- Нельзя доверять клиентским `slot`, `printingId`, `damageIndex` и спискам
целей: сервер заново проверяет их по своему состоянию.
- Ошибки — через `ServerIntentResult`/`ServerError`, не через исключения
наружу. Внутренние исключения логировать, наружу отдавать код/причину.
- Reconnect/resume: новый сокет с `resumeFromSeq`; при невозможности —
полный snapshot. Не пересоздавать матч из-за обрыва.
### Клиент
- Вся сеть — в `GameConnection`: connect, send, receive loop, ping,
pending intents, seq, reconnect. UI не работает с сокетом напрямую.
- `BattleView`/`MatchGrain`-клиентская часть только читает `MatchView` и
отправляет `IntentPayload`; не менять локально авторитарное состояние.
- Пока `_sending`/`_snapshotPending`/reconnect overlay активны, ввод
блокируется; stale input locks освобождаются по состоянию UI.
- После `SnapshotReceived`/`ServerHello` перерисовать поле из snapshot, а не
накапливать догадки по событиям.
- Все сетевые операции — async + `CancellationToken`; не блокировать
Unity-поток.
## 3. Способности карт
Источник текста — `content/sets/*.yaml` (`textEn`). Каждая текстовая
способность должна иметь ровно одну реализацию: `CardScript`, общий движок
или флаг данных. Текст без реализации — баг. Полный аудит:
`docs/ability-implementation.md`.
### AUTO
`AbilityDefinition`:
```csharp
new AbilityDefinition
{
Trigger = AbilityTrigger.OnPlace,
Condition = ctx => ...,
Optional = true,
CanPay = ctx => ...,
Pay = ctx => ...,
Resolve = ctx => ...
}
```
- `Trigger` — только реальное окно события: `OnPlace`, `OnRideOver`,
`OnAttackDeclared`, `OnAttackHit`, `OnBoost`, `OnBoostedHit`,
`OnDriveCheck`, `OnGuard`, `OnRetireFromRC`, `OnOpponentRetire`,
`OnLeaveGuardian`, `OnPhaseStart`, `OnDamageTaken`.
- Проверять зону источника: `IsVanguardOf`, `IsRearGuardOf`, `IsInHand`.
`OnPlace` не должен срабатывать от VC, если текст требует RC.
- `Optional` + `CanPay`/`Pay` — для «you may pay the cost». Без оплаты
эффект не выполняется.
- Несколько целей/карт — только `AskUnitChoice`, `AskUnitMultiChoice`,
`AskHandCardChoice`, `AskEmptyRearGuardChoice`.
- AUTO не должен напрямую менять UI; сервер меняет состояние и добавляет
события. Клиент показывает оверлей подтверждения для optional.
### ACT
`ActivatedAbility`:
- `CanActivate` — только проверка условий; оплата и эффект в `Resolve`
(или `Pay` для AUTO).
- Флаги: `RestsSelf`, `RetiresSelf`, `RequiresDiscard`, `FromHand`,
`FromDropZone`.
- ACT из руки/дропа попадает в `LegalCommands` только в разрешённой фазе;
клиент показывает действие у карты, не отдельным глобальным списком.
- Стоимость (discard, Counter Blast, field units) выбирается по объектам
в руке/уроне/поле; подтверждение — кнопка панели «Действия».
- `RetiresSelf`/`RestsSelf` не должны активироваться из руки/дропа, если
текст этого не разрешает.
### CONT
- Сила/критика — `ContinuousAbility` с `Applies`, `PowerBonus`,
`CriticalBonus`.
- Ограничения, которые не выражаются бонусом, — отдельный движок:
`RestrictionType` (`CannotAttack`, `CannotBoost`, `CannotStand`,
`CannotIntercept`, `CannotBeChosen`) или `CardScript.CanBeBoosted`.
- CONT пересчитывается из состояния каждый раз; не хранить вычисленную
силу как истину.
### Общие механики
- **Sentinel** — данные `sentinel`; общий Guard + сброс карты того же клана.
Отдельный `CardScript` не нужен.
- **Heal/Trigger** — данные `trigger`; общий движок drive/damage check.
- **Tejas** — `snipeBackRow` в данных + общий движок атаки заднего ряда.
- **Restraint** — флаг `restraint` + `IsRestraint`; ACT снимает через
`RestraintOverride`.
- **Boost/Intercept** — `hasBoost`/`hasIntercept` и общий движок.
- **Perfect Guard** — Sentinel + optional discard cost; nullify до конца боя.
### Клиентские правила способностей
- ACT/AUTO выбираются кликом по карте/цели, а не списком кнопок.
- OPTIONAL AUTO — модальный оверлей «Активировать»/«Пропустить».
- Сброс для стоимости — клик по карте в руке.
- Counter Blast — клик по карте урона; доступные/выбранные подсвечиваются.
- Подтверждение оплаты — в панели «Действия»; отмена — правый клик.
- Карта в руке с ACT: клик открывает меню действий с валидными пунктами
(Призвать/Сменить/Активировать/Подробнее). Смена/призыв также работают
драгом.
## 4. Новые карты и изменения правил
Порядок:
1. Добавить/исправить карту в `content/sets/*.yaml`; текст — `textEn`.
2. Найти все AUTO/ACT/CONT/ключевые механики в тексте.
3. Реализовать в `CardScripts` или общем движке; не оставлять дублей.
4. Добавить хотя бы один тест на триггер/активацию/условие.
5. Проверить `CardScripts.Default` целиком: нет ли карты с текстом без
скрипта.
6. Прогнать серверные тесты, `validate`, `translations check`.
7. Для клиентских изменений — Unity EditMode, `SCENE_SMOKE`,
`PLAYMAT_SMOKE`, пересобрать standalone.
## 5. Игровые механики
- **Ride**: hand → VC; ранг не выше текущего +1. Старый Vanguard уходит в
soul. Первый ход: без Battle.
- **Call**: hand → RC. Занятый RC разрешён: старый юнит уходит в Drop.
Правило действует также для call из soul/deck и выбора круга.
- **Move**: RC ↔ RC только в своей колонке; занятые круги меняются местами.
- **Attack**: только передний ряд; цель — Vanguard или передний RC
противника. Tejas может бить задний ряд своей колонки.
- **Boost**: стоящий юнит заднего ряда → передний юнит/VC той же колонки;
сила бустера добавляется до конца боя.
- **Guard**: карты из руки, ранг не выше ранга своего Vanguard. Выбор
карт/перехватчиков — по объектам; одна кнопка «Не защищаться»/
«Защищаться». Щит учитывается сразу; после подтверждения — Guardian.
- **Intercept**: подходящий RC; атака переносится на него.
- **Drive/Damage Check**: только по правилам; триггеры/Heal — общий движок.
- **Counter Blast/Soul Blast/Soul Charge/Counter Charge** — общие методы
`GameEngine`, не дублировать в скриптах.
- **Победа**: 6 damage; после 4+ heal уходит в drop. Проверять инварианты
зон и уникальность `instanceId`.
## 6. Клиентские взаимодействия
- Выбор — клик/драг по карте, кругу, зоне или полю. Кнопки — только
глобальные действия и подтверждения.
- Drag-жесты:
- передний ряд → карта противника: attack;
- задний ряд → передний ряд/VC: boost;
- рука → RC: call;
- рука → VC: ride.
- Панель «Действия» не показывает полный список смен; действия берутся из
меню выбранной карты и валидных интентов.
- Информационные оверлеи не перехватывают клики и не дублируют выбор
кнопками.
## 7. Проверки и окружение
```powershell
# сервер — только на ssh home / Docker, не локальным dotnet
ssh home "docker run --rm -v ~/VanguardReMaster-test:/src -w /src \
mcr.microsoft.com/dotnet/sdk:10.0 dotnet test server/Vanguard.slnx"
# validate / translations
ssh home "docker run --rm ... dotnet run --project server/src/Vanguard.Cli -- validate"
ssh home "docker run --rm ... dotnet run --project server/src/Vanguard.Cli -- translations check"
```
Клиент: Unity **6000.6.6f1**, URP. Не открывать второй Unity с тем же
проектом; batch-проверки — в копии без `Library`. Тесты:
```powershell
& 'C:\Program Files\Unity\Hub\Editor\6000.6.6f1\Editor\Unity.exe' `
-batchmode -nographics -projectPath <copy> `
-runTests -testPlatform EditMode -testResults <copy>\TestResults.xml
```
Smoke: `Vanguard.Editor.SceneSmoke.Run` и `RunPlaymat`. Windows build:
`Vanguard.Editor.StandaloneBuild.BuildWindowsCli`.
После блока: тесты, `validate`, `translations check`, коммит и push.