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

17 KiB
Raw Blame History

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. Структура проекта

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:

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. Проверки и окружение

# сервер — только на 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. Тесты:

& '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.

8. Триггеры и особые случаи

  • После выбора цели силы у Draw дополнительный эффект (draw 1) разрешается сразу: второй цели на поле нет.
  • Heal после цели силы просит карту только в своей зоне урона и только если серверные условия выполнены (damage ≥ opponent и есть карты). Нет карт/условий — дополнительный эффект не активируется.
  • Tejas бьёт задний ряд своей колонки и с RC, и с VC (центральный задний ряд); проверять snipeBackRow + SameColumn.
  • Если у RC доступен только один круг перемещения в колонке, клиент выполняет перемещение сразу, без выбора цели.
  • Подсветка урона при выборе карты ограничена зоной урона локального игрока.