tooling/docs: MCP for Unity, процесс DeepSeek и план сцен/3D/анимаций

- opencode.jsonc: MCP for Unity (localhost:8080) и watcher.ignore

- client/Packages/manifest.json: com.coplaydev.unity-mcp v10.3.0

- процесс: реализацию тоже выполняет DeepSeek (AGENTS, AGENT-WORKFLOW, Skill, README)

- план: TASK-2026-019 и бэклог CLIENT-SCENES-3D-ANIMATIONS

- .gitignore: локальный CardArt_4x (251 МБ) не коммитим

- SESSION-STATE и HANDOFF: состояние сессии и запись 2026-10-30
This commit is contained in:
MpakobecEx 2026-10-30 02:34:48 +05:00
parent 26934c9213
commit acd9b09605
12 changed files with 2115 additions and 41 deletions

4
.gitignore vendored
View File

@ -46,6 +46,10 @@ client/Assets/StreamingAssets/aa/*
client/*.log client/*.log
client/TestResults*.xml client/TestResults*.xml
# Локальный 4x-арт (251 МБ, кодом не используется) — не коммитим
client/Assets/Vanguard/Resources/CardArt_4x/
client/Assets/Vanguard/Resources/CardArt_4x.meta
# --- Unity прочее --- # --- Unity прочее ---
*.unitypackage *.unitypackage
crashlytics-build.properties crashlytics-build.properties

View File

@ -49,7 +49,8 @@ description: Use for any work in the Vanguard ReMaster repository: Cardfight!! V
- **После блока:** клиентские EditMode, серверные тесты (на home), `validate`, - **После блока:** клиентские EditMode, серверные тесты (на home), `validate`,
`translations check`, коммит и push. `translations check`, коммит и push.
- **Сначала план, потом код:** новые изменения проходят анализ DeepSeek с - **Сначала план, потом код:** новые изменения проходят анализ DeepSeek с
записью в `docs/Development/IMPLEMENTATION_PLAN.md`, затем реализацию Kimi. записью в `docs/Development/IMPLEMENTATION_PLAN.md`, затем реализацию —
её тоже выполняет DeepSeek, отдельным проходом по готовому плану.
Передача — через файлы и Git, не через память модели. Детали — Передача — через файлы и Git, не через память модели. Детали —
`docs/Development/AGENT-WORKFLOW.md`. `docs/Development/AGENT-WORKFLOW.md`.

View File

@ -64,6 +64,12 @@
- **[D]** 3D-сцена, отдельные сцены Login/Lobby/DeckBuilder/Battle; HUD — - **[D]** 3D-сцена, отдельные сцены Login/Lobby/DeckBuilder/Battle; HUD —
uGUI; reference 1920×1080. uGUI; reference 1920×1080.
- **[D]** Анимации описываются для локального игрока. Для противника
применяются те же правила, но зеркально с его стороны поля: направление,
сторона появления карты, наклон веера и порядок перекрытия зеркальны;
камера также отражается относительно центра стола. Отдельные
«противнические» анимации не делать, если это не подтверждённое
исключение.
- **[D]** Камера в покое фиксирована по центру поля с небольшим наклоном; - **[D]** Камера в покое фиксирована по центру поля с небольшим наклоном;
временно двигается только `CameraDirector` в trigger-последовательности. временно двигается только `CameraDirector` в trigger-последовательности.
- **[D]** Выбор карты/круга/зоны/цели — клик/драг по объекту; кнопки только - **[D]** Выбор карты/круга/зоны/цели — клик/драг по объекту; кнопки только
@ -112,7 +118,8 @@
## 4. Процесс разработки ## 4. Процесс разработки
Формальный двухэтапный процесс DeepSeek → Kimi описан в Формальный двухэтапный процесс (анализ → план → реализация; обе стадии
выполняет DeepSeek, но раздельно) описан в
`docs/Development/AGENT-WORKFLOW.md`; активный план задачи — в `docs/Development/AGENT-WORKFLOW.md`; активный план задачи — в
`docs/Development/IMPLEMENTATION_PLAN.md`. Ниже — краткие правила, без `docs/Development/IMPLEMENTATION_PLAN.md`. Ниже — краткие правила, без
дублирования шаблона плана. дублирования шаблона плана.

View File

@ -40,8 +40,9 @@
## Вопросы ## Вопросы
- Для существенной неопределённости используй специальный инструмент - **Все вопросы задавай только через специальный инструмент `question`.**
`question` (если он недоступен — задай вопрос обычным сообщением). Это относится к любым уточнениям, выборам и открытым вопросам. Обычным
сообщением вопрос задавай только если инструмент недоступен.
- Предлагай 2–4 коротких варианта с рекомендацией и возможность своего ответа. - Предлагай 2–4 коротких варианта с рекомендацией и возможность своего ответа.
- Дождись ответа, прежде чем принимать зависящее от него решение. - Дождись ответа, прежде чем принимать зависящее от него решение.
- Не спрашивай о том, что уже однозначно установлено кодом, документацией или - Не спрашивай о том, что уже однозначно установлено кодом, документацией или
@ -50,22 +51,23 @@
## Двухэтапная разработка ## Двухэтапная разработка
- Любое пожелание, требующее изменения игры/кода, сначала проходит **анализ и - Любое пожелание, требующее изменения игры/кода, сначала проходит **анализ и
план DeepSeek**, затем **реализацию Kimi**. Не начинай код без плана, кроме план**, затем **реализацию**. Оба этапа выполняет DeepSeek, но раздельно:
явно тривиальной правки, которую пользователь разрешил сделать сразу. не начинай код в том же проходе, где анализируешь, кроме явно тривиальной
правки, которую пользователь разрешил сделать сразу.
- Активный план и журнал задач — `docs/Development/IMPLEMENTATION_PLAN.md`; - Активный план и журнал задач — `docs/Development/IMPLEMENTATION_PLAN.md`;
подробный процесс — `docs/Development/AGENT-WORKFLOW.md`. подробный процесс — `docs/Development/AGENT-WORKFLOW.md`.
- DeepSeek на этапе анализа не меняет игровой код, сцены, префабы, SO и - На этапе анализа не меняются игровой код, сцены, префабы, SO и конфиги;
конфиги; можно читать, задавать вопросы и обновлять план/документацию. можно читать, задавать вопросы и обновлять план/документацию.
- Kimi перед реализацией читает `AGENTS.md`, Skill и раздел задачи, проверяет - Перед реализацией прочитай `AGENTS.md`, Skill и раздел задачи, проверь Git и
Git и актуальность файлов/методов. Устаревший план не является разрешением на актуальность файлов/методов. Устаревший план не является разрешением на
изменения. изменения.
- Передача контекста — только через файлы репозитория и Git, не через память - Передача контекста — только через файлы репозитория и Git, не через память
модели. Если агенты в разных ветках/копиях, это фиксируется в плане. модели. Если агенты в разных ветках/копиях, это фиксируется в плане.
- Статусы пунктов: `[ ]`, `[x]`, `[!]`, `[-]`; отдельно указывается - Статусы пунктов: `[ ]`, `[x]`, `[!]`, `[-]`; отдельно указывается
«Реализация» и «Проверка». `[x]` без проверки запрещён. «Реализация» и «Проверка». `[x]` без проверки запрещён.
- Kimi не расширяет объём, не делает необязательный рефакторинг и не удаляет - На этапе реализации не расширяй объём, не делай необязательный рефакторинг и
чужие изменения. При новом существенном решении пользователя — вопрос и не удаляй чужие изменения. При новом существенном решении пользователя —
остановка зависимой части. вопрос и остановка зависимой части.
## Изменения ## Изменения

View File

@ -158,7 +158,8 @@ dotnet run --project server/src/Vanguard.Cli -- translations check
- `IMPLEMENTATION_CHECKLIST.md` — исторический план по блокам и гейтам. - `IMPLEMENTATION_CHECKLIST.md` — исторический план по блокам и гейтам.
- `docs/Development/IMPLEMENTATION_PLAN.md` — активный журнал задач и шаблон - `docs/Development/IMPLEMENTATION_PLAN.md` — активный журнал задач и шаблон
плана. плана.
- `docs/Development/AGENT-WORKFLOW.md` — двухэтапный процесс DeepSeek → Kimi. - `docs/Development/AGENT-WORKFLOW.md` — двухэтапный процесс: анализ → реализация
(обе стадии на DeepSeek).
- `docs/HANDOFF.md` — состояние на конец сессии и порядок следующих шагов. - `docs/HANDOFF.md` — состояние на конец сессии и порядок следующих шагов.
- `docs/protocol.md` — протокол матча: кадры, интенты, события, таймеры, реконнект. - `docs/protocol.md` — протокол матча: кадры, интенты, события, таймеры, реконнект.
- `docs/game-rules.md` — конспект правил и сверка с текущей реализацией. - `docs/game-rules.md` — конспект правил и сверка с текущей реализацией.

View File

@ -1,5 +1,6 @@
{ {
"dependencies": { "dependencies": {
"com.coplaydev.unity-mcp": "https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity#v10.3.0",
"com.unity.addressables": "4.1.1", "com.unity.addressables": "4.1.1",
"com.unity.collab-proxy": "2.13.6", "com.unity.collab-proxy": "2.13.6",
"com.unity.ide.rider": "3.0.40", "com.unity.ide.rider": "3.0.40",

View File

@ -1,9 +1,11 @@
# Процесс DeepSeek → Kimi # Процесс: анализ → реализация (DeepSeek)
Двухэтапный процесс для задач, которые меняют игру, клиент, сервер, контент или Двухэтапный процесс для задач, которые меняют игру, клиент, сервер, контент или
инфраструктуру. Он нужен, потому что анализ и реализация могут выполняться инфраструктуру. Оба этапа выполняет DeepSeek, но в разных проходах и, как
разными моделями и в разных сессиях OpenCode. Общий контекст — только файлы правило, в разных сессиях: сначала фиксируется план, затем по нему пишется код.
репозитория и Git, не память модели. Разделение нужно, чтобы анализ не смешивался с правками, а объём не
расширялся по ходу. Общий контекст — только файлы репозитория и Git, не память
модели.
## 1. Роли ## 1. Роли
@ -18,7 +20,7 @@
- **Не изменяет игровой код, сцены, префабы, ScriptableObject и конфиги.** - **Не изменяет игровой код, сцены, префабы, ScriptableObject и конфиги.**
- Может обновлять только документацию и план. - Может обновлять только документацию и план.
### Kimi — реализация ### DeepSeek — реализация
- Читает `AGENTS.md`, Skill и раздел задачи по её ID. - Читает `AGENTS.md`, Skill и раздел задачи по её ID.
- Проверяет актуальное состояние Git и кода; сверяет план с реальными - Проверяет актуальное состояние Git и кода; сверяет план с реальными
@ -33,10 +35,13 @@
- Оба этапа используют **тот же репозиторий** и те же `AGENTS.md`/Skill. - Оба этапа используют **тот же репозиторий** и те же `AGENTS.md`/Skill.
Отдельные копии правил не создаются. Отдельные копии правил не создаются.
- Оператор выбирает модель в OpenCode. В текущей установке доступны Kimi-модели - Оператор выбирает модель в OpenCode. В текущей установке доступны
(подтверждено каталогом моделей): `opencode/kimi-k3`, DeepSeek-модели (подтверждено каталогом моделей):
`opencode/kimi-k2.7-code`, `opencode-go/kimi-k3.1-pro`, `opencode-go/deepseek-v4.1-flash`, `opencode-go/deepseek-v4-pro`,
`opencode-go/kimi-k2.7-code`. Конкретную модель выбирает пользователь. `deepseek/deepseek-flash`, `deepseek/deepseek-v4-pro`,
`opencode/deepseek-v4.1-flash-free`. Конкретную модель выбирает
пользователь; этапы анализа и реализации могут идти на разных моделях
DeepSeek.
- Передача работы — через: - Передача работы — через:
1. план задачи в `docs/Development/IMPLEMENTATION_PLAN.md`; 1. план задачи в `docs/Development/IMPLEMENTATION_PLAN.md`;
2. текущий Git-коммит/ветку/рабочую копию; 2. текущий Git-коммит/ветку/рабочую копию;
@ -46,7 +51,7 @@
- Если агенты работают в разных ветках/копиях, это должно быть явно указано в - Если агенты работают в разных ветках/копиях, это должно быть явно указано в
плане; не создавай ветки и не делай merge без разрешения. плане; не создавай ветки и не делай merge без разрешения.
## 3. Этап 1 — DeepSeek ## 3. Этап 1 — анализ
1. **Пожелание:** сформулируй ожидаемый результат, ограничения и критерии 1. **Пожелание:** сформулируй ожидаемый результат, ограничения и критерии
готовности. Не пересказывай задачу в плане длиннее, чем нужно. готовности. Не пересказывай задачу в плане длиннее, чем нужно.
@ -63,7 +68,7 @@
зависимости и ожидаемый результат должны быть явными. зависимости и ожидаемый результат должны быть явными.
8. **Стоп:** после анализа не переходи к коду. 8. **Стоп:** после анализа не переходи к коду.
## 4. Этап 2 — Kimi ## 4. Этап 2 — реализация
1. **Подготовка:** прочитай `AGENTS.md`, Skill, нужный раздел плана. 1. **Подготовка:** прочитай `AGENTS.md`, Skill, нужный раздел плана.
2. **Git:** `git status`, `git log -1`, при необходимости `git diff`; не 2. **Git:** `git status`, `git log -1`, при необходимости `git diff`; не

View File

@ -0,0 +1,409 @@
# Клиент: сцены, 3D и анимации — план доработок
**Дата:** 2026-10-30
**Анализ:** DeepSeek выполнен; код, сцены, префабы, SO и конфиги не менялись.
**Решения:** зафиксированы 2026-10-30 (раздел 8).
**Статус:** реализация не начата.
**Связанные документы:** `docs/client-visual-direction.md`,
`docs/3d-ux-backlog.md`, `docs/Development/ANIMATION-INVENTORY.md`,
`docs/Development/IMPLEMENTATION_PLAN.md` (TASK-2026-001…018).
Документ — приоритизированный бэклог по трём направлениям: разбиение на сцены,
3D-наполнение каждой сцены, оптимизация анимаций. Он не заменяет
`IMPLEMENTATION_PLAN.md`: при старте работ пункты переносятся туда отдельными
TASK с полями «Реализация» и «Проверка» по `AGENT-WORKFLOW.md`.
Первый блок (A1 + C1–C3) оформлен как `TASK-2026-019` в
`docs/Development/IMPLEMENTATION_PLAN.md`; код ещё не менялся.
## 0. Границы
- без новых пакетов: DOTween/UniTask запрещены (решение 2026-10-24);
- правила, сервер, протокол и YAML-контент не меняются;
- `CardMoveAnimator` остаётся единственным планировщиком перемещений карт;
- сцены и префабы меняются только в рамках конкретного пункта, не попутно;
- геймплей вручную проверяет пользователь.
## 1. Текущее состояние (факты)
### 1.1 Сцены
| Сцена | В `.unity` | Создаётся кодом | 3D-содержимое |
|---|---|---|---|
| `Boot` | камера, свет, Global Volume | контроллера и UI нет; `VanguardApp` из `RuntimeInitializeOnLoadMethod` | нет |
| `Login` | контроллер, камера, свет | Canvas + `LoginView`, `SceneEnvironment` | пол, 14 кубов (колонны, неон, задник, стол, 4 карты, 2 панели), 2 point light |
| `Lobby` | контроллер, камера, свет | Canvas + `MetaView`/очередь, `SceneEnvironment` | пол, 2 колонны, 2 стола, стена, световая полоса |
| `DeckBuilder` | контроллер, камера, свет | Canvas + `DeckBuilderView`, `SceneEnvironment` | пол, 3 куба |
| `Battle` | контроллер, камера, свет | Canvas + `BattleView`, `SceneEnvironment`, `GameBoard3D` | 4 фоновых куба + процедурное поле |
| `GameBoard3D` | камера/свет из конфига, `PlaymatSandbox` | песочница поля без сервера | поле целиком |
Переходы: `SceneFlow.LoadAsync` → `LoadSceneAsync(Single)`, ожидание `isDone`,
без fade, прогресса и `allowSceneActivation` (`SceneFlow.cs:63-80`).
`GameBoard3D.unity` отсутствует в Build Settings; константа `PlaymatSandbox`
метода загрузки не имеет.
### 1.2 3D и ассеты
- Окружение одно на все сцены: `SceneEnvironment.Build(SceneTheme)` +
`switch` по теме (`SceneEnvironment.cs:22`, декорации `:110-159`).
- Камера и свет заданы дважды: `SceneBuilder` пишет их в `.unity`, а
`SceneEnvironment` перезаписывает в рантайме другими значениями
(FOV 60 в сцене против 46/52 в рантайме; интенсивность света 0.9 против 1.15).
- Каждый примитив фона получает собственный `Material` в рантайме
(`SceneEnvironment.cs:177-202`); ассетов `.mat` в проекте нет.
- Ассеты: 246 `.jpg` арта карт в `Resources/CardArt`, 0 `.fbx`, 0 `.mat`,
0 `.shader`, 0 `.anim`, 0 `.controller`, 4 prefab-оверлея.
- Addressables `4.1.1` установлен, но `Addressables.*` в коде не используется.
- `AudioListener` есть только в `Boot` (в остальных сценах — 0).
- Global Volume есть только в `Boot`; в сценах-экранах нет
`UniversalAdditionalCameraData` — нужна визуальная проверка пост-обработки.
- `LODGroup`, baked-свет и occlusion отсутствуют; материалы и меши стола
создаются процедурно (`BoardMaterials`, `BoardMeshLibrary`).
### 1.3 Анимации
Уже сделано и переделывать не нужно: пул `Card3D`, `sharedMaterial` +
`MaterialPropertyBlock`, кеши мешей и текстур, единый `Tick` из
`GameBoard3D.Update`, FPS-независимая интерполяция (экспонента и smoothstep
на `Time.unscaledDeltaTime`), отсутствие `Update` на каждой карте.
Проблемы, подтверждённые кодом:
- `UpdateBoostArrows` создаёт `new List<(Vector3, Vector3)>` каждый кадр, а
`BattleCard` — `Players.FirstOrDefault(lambda)` 2–4 раза за кадр; якоря
стрелок ищутся линейным перебором `_cards.Values`.
- Стрелки обновляются дважды за кадр: `_arrowDirector.Tick` и
`UpdateAttackArrowPoints` (`GameBoard3D.Update`), плюс
`AttackArrowView.ApplyColor` создаёт массив `new[] { _shaft, _head }`.
- `UpdateHandView` пересчитывает позы обеих рук каждый кадр и вызывается
повторно из `Render`, hover и колбэков (`GameBoard3D.cs:183, 511, 607,
732, 1163, 2230, 2372`) — в одном кадре возможен двойной `SetPose`.
- Строки в кадре: таймер и банк (`BattleView.UpdateClock`), `Describe()` у
`InputLockService` при активном локе, `LocalizedText` для каждого лейбла.
- Hover/drag используют `Physics.RaycastAll` с сортировкой через лямбду и
`GetComponentInParent` на каждый хит.
- `AnimationConfig` содержит ~15 полей, которые код не читает
(`ANIMATION-INVENTORY.md`, раздел I): риск неверной настройки.
## 2. Сводка приоритетов
| ID | Что | Приоритет | Зачем |
|---|---|---|---|
| A1 | Загрузочная сцена и fade | P0 | убрать вспышку при смене сцены |
| A2 | Песочница в Build Settings | P0 | `RunPlaymat` и песочница в билде |
| A3 | Один владелец камеры/света | P0 | убрать рассинхрон `.unity` и рантайма |
| A4 | `SceneProfile` на сцену | P1 | настройка сцены без правки кода |
| A5 | AudioListener на персистентном объекте | P1 | звук во всех сценах |
| A6 | Пост-обработка в сценах-экранах | P1 | одинаковый look |
| A7 | Additive-слой общих сервисов | P2 | не пересоздавать Canvas |
| A8 | SceneSmoke для DeckBuilder/Battle | P2 | покрытие смоуком |
| B1 | Общие материалы вместо `new Material` | P0 | батчинг, редактируемость |
| B2 | Login: полноценная 3D-сцена | P0 | первый экран |
| B3 | Lobby и DeckBuilder: 3D-сцены | P1 | сейчас 6 и 3 куба |
| B4 | Battle: окружение вокруг поля | P1 | задник/свет без правки поля |
| B5 | Prefab-экраны UI | P1 | уйти от code-generated |
| B6 | Addressables для арта | P1 | разгрузить `Resources` |
| B7 | Light/reflection probes, baked-свет | P2 | качество света |
| B8 | Живое окружение | P2 | неон, частицы, скайбокс |
| C1 | Аллокации в стрелках | P0 | 0 B/кадр в бою |
| C2 | Один тик руки | P0 | убрать двойной `SetPose` |
| C3 | Строки не каждый кадр | P0 | GC и цена UI |
| C4 | Событийный `InputLock` | P1 | паузы по факту, не по таймеру |
| C5 | Raycast без аллокаций | P1 | hover/drag |
| C6 | Синхронизировать `AnimationConfig` | P1 | убрать мёртвые поля |
| C7 | Профилирование и бюджет кадра | P2 | 60 fps, метрики |
| C8 | Анимационный LOD | P2 | не тикать невидимое |
## 3. A — Разбиение на сцены
- [ ] **A1. P0 — Загрузочная сцена и плавный переход.**
Файлы: `Runtime/App/SceneFlow.cs`, новый `Runtime/UI/LoadingOverlay.cs`,
`Runtime/App/VanguardApp.cs`, `Scenes/Boot.unity`.
Сделать: `Boot` становится загрузочной сценой — прогревает `VanguardApp` и
сервисы, показывает прогресс `operation.progress` и fade 0.15–0.25 с, затем
грузит `Login`; для переходов между экранами используется тот же overlay.
Зачем: переход идёт без fade и прогресса — сцена меняется одним кадром,
экран загрузки не показывается.
Критерий: Login → Lobby → Battle без вспышки; прогресс виден на первом
входе в Lobby.
Зависит: нет.
- [ ] **A2. P0 — Песочница поля в сборке.**
Файлы: `ProjectSettings/EditorBuildSettings.asset` (через
`Editor/SceneBuilder.cs`), `Runtime/App/SceneFlow.cs`.
Сделать: добавить `Assets/Vanguard/Scenes/GameBoard3D.unity` после `Battle`,
метод `LoadPlaymatAsync`, доступ только в dev-сборке.
Зачем: константа `PlaymatSandbox` не имеет загрузчика, сцены нет в билде.
Критерий: песочница открывается в standalone, `SceneSmoke.RunPlaymat`
проходит.
Зависит: A1.
- [ ] **A3. P0 — Один владелец камеры и света.**
Файлы: `Editor/SceneBuilder.cs`, `Runtime/App/SceneEnvironment.cs`,
`*.unity`.
Сделать: выбрать одну модель — сцены хранят камеру/свет, а
`SceneEnvironment` их не перезаписывает, либо сцены пустые и всё создаёт
окружение. Зафиксировать в `client-visual-direction.md`.
Зачем: сейчас FOV 60 в сцене против 46/52 в рантайме, свет 0.9 против 1.15;
правки в сцене не действуют.
Критерий: значения в инспекторе совпадают с рантаймом;
`SceneBuilder.BuildAll` не создаёт расхождений.
Зависит: нет (A4 опирается на это решение).
- [ ] **A4. P1 — `SceneProfile` (ScriptableObject) на сцену.**
Файлы: новый `Runtime/App/SceneProfile.cs` + 4 ассета,
`Runtime/App/SceneEnvironment.cs`, контроллеры сцен.
Сделать: вынести камеру, свет, палитру, туман, декор и пост в профиль;
`Build(SceneProfile)` вместо `switch(SceneTheme)`.
Зачем: настройка сцены сейчас требует правки кода; поля
`ScenePalette.Zone/EnemyZone` не читаются.
Критерий: внешний вид сцены меняется правкой ассета, без компиляции.
Зависит: A3.
- [ ] **A5. P1 — AudioListener и аудио-корень.**
Файлы: `Runtime/App/VanguardApp.cs`, `*.unity`.
Сделать: один `AudioListener` на персистентном объекте, убрать дубль из
`Boot`-камеры.
Зачем: единственный listener в `Boot` уничтожается при Single-загрузке —
при добавлении звука его не будет слышно.
Критерий: ровно один `AudioListener` в любой сцене, звук слышен.
Зависит: нет.
- [ ] **A6. P1 — Пост-обработка в сценах-экранах.**
Файлы: `*.unity`, `Assets/Settings/SampleSceneProfile.asset`.
Сделать: `UniversalAdditionalCameraData` и Volume (или общий профиль через
Global Settings) на всех экранах; сверить bloom/vignette/tonemapping.
Зачем: Global Volume есть только в `Boot`, в остальных сценах
пост-обработка, вероятно, не применяется.
Критерий: одинаковый look `Boot`/`Login`/`Lobby`/`DeckBuilder`/`Battle`.
Зависит: A3.
- [ ] **A7. P2 — Additive-слой общих объектов.**
Сделать: Canvas оверлеев, EventSystem и аудио грузить один раз и не
пересоздавать при смене экрана.
Зачем: каждый экран заново строит Canvas через `UiKit.CreateCanvas`.
Критерий: Lobby ↔ DeckBuilder без пересоздания оверлеев и мигания.
Зависит: A1.
- [ ] **A8. P2 — Расширить `SceneSmoke`.**
Файлы: `Editor/SceneSmoke.cs`.
Сделать: проверять `DeckBuilder` и `Battle` (контроллер, окружение, камера,
`AudioListener`), сверять FOV/интенсивность света с профилем.
Зачем: сейчас проверяются только `Login`/`Lobby`.
Критерий: смоук падает при потере окружения или listener в любой сцене.
Зависит: A3–A6.
## 4. B — 3D в каждой сцене
- [ ] **B1. P0 — Общие материалы вместо `new Material`.**
Файлы: `Runtime/App/SceneEnvironment.cs`, новый `Assets/Vanguard/Art/…`.
Сделать: 3–5 общих URP-материалов ассетами; фон и декор используют их.
Зачем: сейчас каждый примитив получает инстанс материала в рантайме
(`:177-202`), это ломает батчинг и не редактируется в инспекторе.
Критерий: Frame Debugger без лишних `SetPass`, SRP Batcher активен.
Зависит: A4.
- [ ] **B2. P0 — Login: полноценная 3D-сцена.**
Сделать: authored-стол, 4–6 карт, неоновые направляющие, задник; свет и
камера из профиля; геометрия — префабы, а не код.
Зачем: первый экран сейчас собран из 14 кубов в рантайме.
Критерий: визуально читаемый стол; в рантайме не создаётся статическая
геометрия.
Зависит: B1, A4, референс экрана от пользователя.
- [ ] **B3. P1 — Lobby и DeckBuilder: 3D-сцены.**
Сделать: Lobby — зал с постаментом/витриной и световыми акцентами;
DeckBuilder — стол с матом, стопками и подсветкой зон.
Зачем: сейчас 6 и 3 куба, экраны визуально пустые.
Критерий: у каждой сцены своя композиция, а не общий `switch` с кубами.
Зависит: B1, A4, референс экрана от пользователя.
- [ ] **B4. P1 — Battle: окружение вокруг поля.**
Сделать: задник, постамент, световые акценты вокруг `GameBoard3D`, не
вмешиваясь в геометрию поля и `PlaymatLayout`.
Зачем: сейчас 4 фоновых куба; поле выглядит висящим в пустоте.
Критерий: поле читается как стол в помещении; `GameBoard3DTests` без
изменений.
Зависит: B1, A4, референс боевого экрана от пользователя.
- [ ] **B5. P1 — Prefab-экраны UI.**
Сделать: перенести code-generated панели в префабы (по одному экрану за
шаг), `UiKit` оставить фабрикой для мелких элементов.
Зачем: `client-visual-direction.md` фиксирует это как следующий шаг;
сейчас UI собирается кодом и не редактируется дизайнером.
Критерий: экран собирается из префаба, поведение и тесты не меняются.
Зависит: приёмка TASK-2026-003 (HUD), иначе конфликт правок.
- [ ] **B6. P1 — Addressables для арта.**
Сделать: инициализировать Addressables-группы, перенести арт карт из
`Resources/CardArt` в адресуемые группы, грузить по `cardId`
(`CardImageService`), оставить процедурный fallback по клану.
Зачем: 246 `.jpg` лежат в `Resources` и грузятся целиком; пакет установлен,
но не используется.
Критерий: арт грузится адресуемо и выгружается, `Resources` не растёт;
контракт `BoardArt`/`CardArt` не меняется.
Зависит: нет; модели карт и стола остаются процедурными (решение 8.1).
- [ ] **B7. P2 — Light/reflection probes и baked-свет.**
Сделать: запечь статику сцен, добавить probes для карт и стола.
Зачем: `m_LightingDataAsset` пустой, освещение только realtime.
Критерий: статичный свет не считается каждый кадр, карты получают
корректные отражения.
Зависит: B2–B4 (финальная геометрия).
- [ ] **B8. P2 — Живое окружение.**
Сделать: анимация неона, лёгкие частицы, скайбокс/градиент, дыхание камеры
в Login/Lobby.
Зачем: окружение статично; при этом правила запрещают DOTween — только
существующий ручной тик.
Критерий: эффекты не блокируют ввод и не добавляют аллокаций в кадре.
Зависит: B2–B4, C1–C3.
## 5. C — Анимации: плавность и стоимость кадра
- [ ] **C1. P0 — Убрать покадровые аллокации в стрелках.**
Файлы: `Board3D/View/GameBoard3D.cs` (`UpdateBoostArrows`,
`UpdateAttackArrowPoints`, `BattleCard`), `Animation/ArrowDirector.cs`,
`View/AttackArrowView.cs`.
Сделать: кешировать список пар, заменить LINQ на индексированные циклы,
кешировать якоря на `Render`, обновлять геометрию один раз за кадр, убрать
`new[] { _shaft, _head }` в `ApplyColor`.
Зачем: `new List` каждый кадр, 2–4 LINQ-делегата, 4 `UpdateGeometry` на
стрелку за кадр.
Критерий: 0 B/кадр в бою без движения (Profiler), визуал не меняется.
Зависит: нет.
- [ ] **C2. P0 — Один тик и один `SetPose` руки.**
Файлы: `Board3D/View/GameBoard3D.cs` (`UpdateHandView`), `Board3D/View/Card3D.cs`.
Сделать: dirty-флаг на изменение состава руки, камеры, hover и staged-списка;
убрать повторный вызов из `Render`/колбэков, если позы уже посчитаны.
Зачем: позы обеих рук пересчитываются каждый кадр, возможен двойной
`SetPose` в одном кадре.
Критерий: при неподвижной камере и руке расчёт не выполняется; hover,
добор, муллиган и reflow без регрессий.
Зависит: C1 (общий бюджет кадра).
- [ ] **C3. P0 — Строки не каждый кадр.**
Файлы: `UI/BattleView.cs` (`UpdateClock`), `UI/Localization.cs`
(`LocalizedText`), `Battle/InputLockService.cs` (`Describe`).
Сделать: обновлять текст только при изменении значения; кешировать перевод
до смены языка; убрать `Describe`/LINQ из кадрового пути.
Зачем: таймер и банк пишутся каждый кадр, `LocalizedText` проходит
`Loc.Translate` на каждый лейбл, при локе — `string.Join`/LINQ.
Критерий: в покое нет аллокаций от UI; переключение RU/EN по-прежнему
мгновенное.
Зависит: нет.
- [ ] **C4. P1 — Событийный `InputLock` вместо таймера.**
Файлы: `Board3D/Animation/CardMoveAnimator.cs`,
`Board3D/View/GameBoard3D.cs`, `Battle/InputLockService.cs`.
Сделать: снимать `board`-лок по завершении очереди и stagger, таймер
оставить только страховкой.
Зачем: лок держится `cardMoveDuration * 3 + tail` и не совпадает с фактом
окончания анимаций — лишние паузы или преждевременный ввод.
Критерий: ввод открывается сразу после последней карты; зависаний нет
(`CancelAll`, reconnect, отмена).
Зависит: C2.
- [ ] **C5. P1 — Raycast без аллокаций.**
Файлы: `Board3D/View/GameBoard3D.cs` (`UpdateHandHover`, drag, targeting).
Сделать: `RaycastNonAlloc` с переиспользуемым буфером, выбор ближайшего без
`Array.Sort` с лямбдой, кеш `GetComponentInParent`.
Зачем: `RaycastAll` + сортировка + `GetComponentInParent` на каждый хит
каждый кадр при активном указателе.
Критерий: наведение и drag без аллокаций; выбор карты не изменился.
Зависит: C1.
- [ ] **C6. P1 — Синхронизировать `AnimationConfig` с кодом.**
Файлы: `Battle/AnimationConfig.cs`, `Resources/AnimationConfig.asset`,
`docs/Development/ANIMATION-INVENTORY.md`.
Сделать: удалить или явно пометить поля, которые не читаются или читаются
не по назначению: `cardFlipDuration`, `callMoveDuration`,
`cameraRezoomDuration`, `arrowHideDuration`, `retireDuration`,
`attackDeclare` и паузы feedback — не читаются; `cardMoveDuration` —
только таймаут board-lock; `arrowFadeDuration` и `boostThickness` дублируют
`ArrowConfig`. Asset привести к фактическим значениям.
Зачем: ~15 мёртвых полей — источник неверной настройки.
Критерий: каждое поле читается кодом или удалено; тест на загрузку ассета.
Зависит: нет.
- [ ] **C7. P2 — Профилирование и бюджет кадра.**
Сделать: маркеры Profiler в `CardMoveAnimator`, `GameBoard3D.Update`,
`UpdateHandView`; зафиксировать бюджет (60 fps, доска ≤ 1 мс на референсном
ПК); выровнять `shadowDistance` (40 в QualitySettings против 50 в URP asset);
MSAA оставить выключенным (решение 2026-10-30) и проверить края карт с TAA.
Зачем: сейчас метрик нет, настройки противоречат друг другу.
Критерий: отчёт «до/после» с числами, зафиксированный бюджет в документе.
Зависит: C1–C5.
- [ ] **C8. P2 — Анимационный LOD.**
Сделать: не тикать карты вне экрана и стопки, обновлять их по dirty;
уменьшить частоту обновления рук противника и дальних стопок.
Зачем: `GameBoard3D.Update` тикает все карты в `_cards` каждый кадр.
Критерий: то же визуальное поведение, меньше работы в кадре при 40+ картах.
Зависит: C2, C7.
## 6. Порядок работ
1. **Старт (P0):** A1 → C1 → C2 → C3 → A2. Загрузочная `Boot` и чистый кадр
дают сразу видимый эффект и не конфликтуют с приёмкой HUD.
2. **3D-база (P0):** A3 → A4 → B1 → B6.
3. **Экраны (P1):** B2 → A5 → A6 → B3 → B4; B2–B4 стартуют после референсов.
4. **Анимации (P1):** C4 → C5 → C6.
5. **UI (P1):** B5 — после приёмки TASK-2026-003.
6. **Полировка (P2):** A7 → A8 → B7 → B8 → C7 → C8.
Правило: этапы 1–2 не меняют компоновку HUD из TASK-2026-003, пока идёт его
ручная приёмка; в `BattleView` допускаются только точечные правки вне
разметки (например, `UpdateClock` в C3).
## 7. Проверки
- Клиентские EditMode в копии проекта без `Library` (команда в
`docs/SESSION-STATE.md`).
- `SceneSmoke.Run` и `SceneSmoke.RunPlaymat` после каждого блока сцен.
- `SceneBuilder.BuildAll` — сцены пересобираются без ошибок, `.meta`
контроллеров на месте.
- Standalone-сборка на копии: переходы, пост-обработка, звук, песочница.
- Profiler: Allocation Call Stacks (0 B/кадр в бою без движения), Frame
Debugger для батчинга.
- Регрессии: drag&drop, ride/call/move, добор, муллиган, trigger, урон,
hover руки.
- Ручная приёмка визуала и геймплея — пользователем.
## 8. Решения и риски
**Зафиксировано пользователем 2026-10-30:**
1. **3D-арт.** Работаем на примитивах с нормальными URP-материалами; закупка
и генерация моделей в объём не входят. Модели карты/стола можно добавить
позже без смены контрактов `BoardArt`/`CardArt`.
2. **`Boot`.** Остаётся первой сценой и становится загрузочной: прогрев
сервисов, прогресс, fade, затем `Login` (входит в A1).
3. **Addressables.** Арт переводим сейчас, отдельным шагом (B6), не дожидаясь
prefab-экранов.
4. **MSAA.** Остаётся выключенным; края карт сглаживаются TAA. Пересматривать
только по результатам C7.
5. **Референсы.** Пользователь готовит мокапы для всех экранов: `Login`,
`Lobby`, `DeckBuilder` (включая экран колод), `Battle`. B2–B4 стартуют
после получения соответствующего референса.
6. **Первый блок.** A1 + C1–C3.
**Риски:**
- правки сцен ломают GUID-ссылки — сцены пересобираются `SceneBuilder`,
копировать `.meta` вместе со скриптами;
- перенос UI в префабы (B5) конфликтует с TASK-2026-003, пока HUD не принят;
- `new Material` в рантайме нельзя заменить на ассеты, пока не решён A3/A4
(кто владеет материалами сцены);
- референсы для `Battle` могут потребовать правок HUD из TASK-2026-003 —
тогда это отдельная задача, а не попутная правка.
## 9. Вне объёма
- боевые правила, сервер, протокол, баланс, YAML-контент;
- HUD по мокапу (TASK-2026-003) и боевые анимации из
`docs/3d-ux-backlog.md` (cost overlay, attack resolution queue,
world-space Power/Shield);
- звук и настройки как отдельная система (кроме A5 — listener);
- лаунчер WPF и сборка релиза.

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,181 @@
# Передача сессии # Передача сессии
Состояние проекта на **23 октября 2026**. Документ нужен, чтобы новая сессия Состояние проекта на **28 октября 2026**. Документ нужен, чтобы новая сессия
продолжила работу без повторного разбора истории. Краткая оперативная продолжила работу без повторного разбора истории. Краткая оперативная
сводка — `docs/SESSION-STATE.md`; этот файл хранит подробную историю. сводка — `docs/SESSION-STATE.md`; этот файл хранит подробную историю.
--- ---
## 2026-10-30 — реализацию выполняет DeepSeek
- Решение пользователя: этап реализации больше не закреплён за Kimi. Оба
этапа — анализ и реализация — выполняет DeepSeek, но раздельно: сначала
план, потом код по нему.
- Обновлены `AGENTS.md`, `docs/Development/AGENT-WORKFLOW.md`,
`docs/Development/IMPLEMENTATION_PLAN.md` (процесс и шаблон задачи),
`docs/SESSION-STATE.md`, `README.md` и Skill
(`.opencode/skills/vanguard-remaster/SKILL.md`,
`workflow-and-decisions.md`).
- Двухэтапность и все запреты сохранены: анализ не меняет код, реализация не
расширяет объём; `[x]` без проверки запрещён.
- Исторические записи о Kimi (TASK-2026-001, раздел 2026-10-24) не
переписывались.
- Код, сцены, префабы и конфиги не затрагивались.
---
## 2026-10-28 — TASK-2026-018: карта из руки не двигается при выборе зоны
- Для карты из руки зажатие ЛКМ больше не запускает обычный drag:
включается `_handTargeting`, карта остаётся в hand-раскладке и
hover-подъёме.
- Курсор двигается по зонам, `UpdateHandTargeting` только подсвечивает
валидную цель; цель берётся raycast-ом по карте/кругу, затем плоскостью.
- На release над VC/RC отправляется `CardDropped`; невалидный drop
оставляет карту в руке.
- Карты поля сохраняют обычный drag с движением.
- EditMode — **58/58**, standalone собран, клиент запущен.
---
## 2026-10-28 — TASK-2026-015: drag из руки, смена через VC
- `Card3D.EndDragAtCurrent` фиксирует карту в точке drop-а без возврата
в руку и сохраняет подъём/масштаб.
- Для карты из руки `FinishDrag` больше не вызывает `ReturnToOrigin` до
ответа сервера: карта ждёт снапшот и летит в VC/RC из точки drop-а.
- `ReturnDroppedCard` снимает hold и плавно возвращает карту, если drop не
стал интентом, сервер отказал или сработал guard-выбор.
- Карты поля сохраняют прежний путь (возврат к source до снапшота).
- Тест `Dropped_Hand_Card_Holds_At_Drop_Until_Returned`.
- Карта из руки при зажатии ЛКМ не двигается: включается `_handTargeting`,
карта остаётся в руке приподнятой, курсор выбирает зону; на release над
VC выполняется смена. Карты поля сохраняют обычный drag.
- EditMode — **58/58**, standalone собран, клиент запущен.
---
## 2026-10-28 — TASK-2026-014: подъём карты в руке при наведении
- Своя карта под курсором плавно поднимается камера-вверх на 10% своей
высоты (`handHoverLift = 0.1`); после ухода курсора плавно возвращается.
- Hover работает только для своей руки и отключается при UI, input-lock,
драге и анимациях раскладки/возврата/добора.
- Плавность — существующий `Card3D.SetPose`/`Tick`.
- Тест `Hand_Hover_Lifts_Card_By_Ten_Percent`; EditMode — **58/58**,
standalone собран, клиент запущен.
---
## 2026-10-28 — TASK-2026-013: единый механизм раскладки руки
- Раздельные staged-списки возврата/добора заменены единым
`_visualHandCards` + `_handLayoutOverride`.
- В визуальный список сразу входят оставшиеся карты руки и ожидающие
возврата; новые добранные добавляются после завершения дуги.
- После каждого возврата/добора список пересчитывается, поэтому остатки руки
всегда участвуют в сдвиге; повторный Render не сбрасывает прогресс.
- Новые карты при доборе скрыты и показываются по одной.
- Тест `Mulligan_Remaining_Cards_Reflow_After_Each_Return`.
- EditMode — **58/58**, standalone собран, клиент запущен.
---
## 2026-10-28 — TASK-2026-011: ступенчатый reflow руки при муллигане
- При возврате нескольких карт рука больше не перестраивается один раз под
финальное количество.
- `_stagedReturnCards` хранит старые карты руки в порядке слева-направо,
включая ожидающие возврата; новые карты в него не входят.
- После завершения каждой карты она убирается из staged-списка, оставшиеся
сдвигаются под новое количество; ожидающие возврата тоже перестраиваются.
- Ожидающая карта получает цель под колодой только в момент старта своей
анимации (`CardMoveAnimator.onStart` + `fromCurrent`).
- Тест проверяет: до первого ухода рука не сдвигается, после первого —
сдвигается; EditMode — **58/58**.
---
## 2026-10-28 — TASK-2026-010: рука перестраивается при выходе карты
- При любом выходе карты из руки оставшиеся карты получают новые позы в том
же `Render`, не дожидаясь окончания анимации уходящей карты.
- Перекомпоновка `Hand → Hand` не ставится в `CardMoveAnimator`
(`NeedsMove` → false), поэтому hand reflow идёт одновременно с movement.
- Добавлен тест `Hand_Reflows_While_A_Card_Leaves_From_Hand`; тест возврата
в колоду дополнительно проверяет сдвиг остальной руки.
- EditMode — **58/58**. Ручная проверка — за пользователем.
---
## 2026-10-27 — TASK-2026-007: общий добор карты
- Добор после муллигана приведён к виду добора в муллигане: карта стартует над
колодой **полного размера** (`cardScale`), рубашкой вверх в ориентации
будущей карты руки, летит в руку по дуге и переворачивается лицом вверх.
- `GameBoard3D.GetDrawSource` — общий старт для `AnimateHandDraw` и
`ResolveSourcePose`; новая карта руки больше не стартует в размере руки.
- Длительность обычного добора — `introDrawDuration = 0.6 с`, как в
муллигане.
- Тест проверяет позицию над колодой и `cardScale` у новой карты.
- EditMode — **58/58**, standalone собран, клиент запущен. Ручная проверка —
за пользователем.
---
## 2026-10-27 — TASK-2026-006: возврат карт из руки в колоду
- Добавлена анимация возврата: старая карта руки уходит по дуге **под низ
колоды**, во время движения переворачивается рубашкой вверх; следующая
карта стартует только после завершения предыдущей.
- Старт — **справа налево**; у противника зеркально с его стороны поля.
- `GameBoard3D.QueueHandReturnToDeck` получает `MulliganView` и
`CardReturnedToDeckView` из `BattleView`; старые карты определяются по
исчезновению из руки, вынимаются из `_cards`, анимируются и возвращаются
в пул после завершения.
- `CardMoveAnimator.Enqueue` получил необязательный `onCompleted`; возврат
использует `sequential: true`. Ожидающие карты удерживаются в руке через
`HoldAtSource`, поэтому не уезжают под колоду параллельно.
- Длительность возврата — общая с добором: `DrawDuration`
(`introDrawDuration = 0.6 с`).
- Тесты: `Hand_Return_To_Deck_Starts_Right_To_Left_And_Targets_Deck_Bottom`,
`CardMoveAnimator_Runs_Sequential_Requests_And_Completes_Each`.
- EditMode — **58/58**, standalone собран, клиент запущен. Ручная проверка —
за пользователем.
---
## 2026-10-27 — TASK-2026-005: направление набора руки
- Своя рука снова набирается **слева направо**: первая карта слева, новая
добавляется справа и остаётся поверх.
- Рука противника зеркальна с его стороны поля: на экране набирается
**справа налево**, новая карта появляется слева.
- `GetHandViewPose` / `GetOpponentHandViewPose` возвращены к зеркальным
формулам; тест `Hands_Fill_Mirrored_And_Newest_Is_On_Top` обновлён под
новое направление.
- EditMode — **51/51**, standalone собран, клиент запущен. Ручная проверка
порядка набора — за пользователем.
---
## 2026-10-27 — TASK-2026-004: удаление боевых баннеров
- Удалён центральный feedback-блок `BattleView`: текст событий
(`АТАКА`, `GUARD`, `DRIVE`, `TRIGGER`, `HIT/BLOCK`, `DAMAGE`, `HEAL`,
`RETIRE`, `INTERCEPT`, `PERFECT GUARD`), полноэкранная вспышка,
кнопка «Пропустить анимации», очередь `FeedbackStep` и `InputLock("feedback")`.
- Удалены мёртвые `_turnBanner`/`_turnBannerText`.
- Журнал событий, `Describe`, trigger-последовательность, верхний
`ActionPrompt` и `StatusToast` не тронуты.
- Паузы `eventPause`/`post*Pause` больше не применяются; темп держат
`CardMoveAnimator` и trigger-последовательность. Поля `AnimationConfig`
пока оставлены неиспользуемыми, чтобы не менять контракт ассета.
- EditMode — **51/51**. Ручная проверка боевого темпа — за пользователем.
- Инвентаризация анимаций: `docs/Development/ANIMATION-INVENTORY.md`.
---
## 2026-10-24 — TASK-2026-001: плавность карточных перемещений ## 2026-10-24 — TASK-2026-001: плавность карточных перемещений
- Реализована стабильная идентичность анонимных карт (`InstanceId == 0`) в - Реализована стабильная идентичность анонимных карт (`InstanceId == 0`) в
@ -36,6 +206,116 @@
--- ---
## 2026-10-25 — TASK-2026-003: боевой HUD по мокапу
- **HUD:** убрана постоянная правая панель действий и `PhasePanel`;
подтверждения перенесены в нижний правый `ConfirmationBar`. Верхний HUD
показывает `Turn`, текстовую фазовую шкалу `Standby › Main › Battle › End`,
таймер и банк отдельно. Debug-панель (журнал) скрыта по умолчанию,
открывается кнопкой «Показать/скрыть панель».
- **Панели игроков:** новый `PlayerStatsPanelView` — `Hand/Deck/Damage/
Shield/Trigger` и кнопки `Drop/Soul`. `Deck` неинтерактивен; `Guardian`
в бар не входит. `UpdateZoneStrips` переведён на новый view.
- **Поле:** `BoardSlotView` получил тёмную пластину под рамкой; над зоной
урона добавлен счётчик `x/6` (у противника зеркально к центру).
`Card3D` получил рамку лица; статы вынесены под карту, крит показывается
только при `>1`, вместо `P/C` — символы `▲/◆`.
- **Триггеры:** `BattleView` всегда запускает trigger-зум на `DriveCheckedView`
и damage-триггере; карты trigger-зоны скрыты до `ZoomCompleted`, затем
показываются. При отсутствии триггера в зоне пишется «БЕЗ ТРИГГЕРА».
Подписи распределения: `+5000`, `Крит +1`, `Поднять юнита`, `Взять карту`,
`Лечить`.
- **Дополнительно:** карты урона укладываются сверху вниз (у противника
зеркально) с шагом 30% от ширины карты, новая карта приподнята по Y и
рисуется поверх; trigger-карта повёрнута на 90° против часовой стрелки;
на карте показываются имя и бейдж триггера; добавлен центральный баннер
хода; no-trigger damage check определяется по `DamageTakenView` с
подавлением повторного зума после trigger-карты.
- **Правки по замечаниям:** имена карт на поле убраны; панель действий
сжата до автоподстраиваемого столбца кнопок справа снизу; рука
центрируется по экрану (`handViewportX = 0.5`); подсказка муллигана
перенесена в верхний `ActionPrompt`, при выборе карт появляется кнопка
«Заменить N карт»; центральный баннер хода убран; левый нижний бар —
`Колода, Душа, Рука, Сброс`, без `Щит/Триг`, `Душа/Сброс` кликабельны.
- **Триггеры:** фокус камеры всегда первый; после фокуса карта
раскрывается анимацией, затем пауза 0,75 с, затем резолв. Выбор цели
открывается после паузы. Сначала `+5000` на RC/VC, затем эффект:
`Critical` — RC/VC, `Stand` — только повёрнутый RC, `Draw` —
автоматически, `Heal` — выбор карты урона при условиях. Подсказки — в
верхнем информационном блоке. Нетриггерная карта пропускается.
- **Урон:** переворот face-up/face-down идёт вокруг длинной стороны.
- **Tejas:** атака заднего ряда по колонке `V↔1`, `3↔2`, `4↔0`.
Сервер пересобран и задеплоен на `ssh home`, контейнер `server`
перезапущен.
- **Основная фаза:** в панели подтверждений только кнопка «Боевая фаза»;
«Закончить ход» и список всех действий скрыты, способности — из меню
карты.
- **Рамки карт:** тонкая чёрная рамка по краю карты; подсветка выбора
ужата до размера карты и не перекрывает соседние карты. В руке рамки
скрыты, карты лежат широким веером: своя рука заполняется слева направо,
новая карта добавляется правее и перекрывает предыдущую; ширина веера
сохраняется до 10 карт и плавно уменьшается начиная с 11-й. Рука
противника — зеркальный камеро-зависимый веер ниже верхней панели:
новая карта слева, наклон и глубина зеркальны. Позы обеих рук
пересчитываются каждый кадр, поэтому добор не улетает к устаревшей цели;
порядок закреплён тестом `Hands_Fill_Mirrored_And_Newest_Is_On_Top`.
- **Стартовый Авангард:** до конца муллиганов закрыт (`MatchProjection`
передаёт реальный `FaceUp`, без принудительного открытия); после обоих
муллиганов открывается анимацией 0,8 с вокруг длинной оси слева направо
(у противника зеркально), затем начинается первая фаза. На время
переворота клиент блокирует ввод.
- **Старт боя:** камера над стороной игрока лицом к столу, 1,5 с →
дуга вниз на основную позицию → пауза 1,5 с → одновременный добор своей
и чужой руки → после всех доборов появляется муллиган. Ввод заблокирован
до конца интро.
- **Взятие карт:** карта создаётся ровно над колодой рубашкой вверх и
летит в руку по дуге с переворотом лицом вверх. Любой добор последовательный:
следующая карта стартует только после завершения предыдущей. В стартовой
раздаче своя и чужая руки идут парами, внутри каждой пары — по одной.
- **Проверки:** EditMode 50/50; серверные `Vanguard.Rules` 155/155 на
`ssh home`; standalone Windows собран, клиент запущен. Ручная приёмка —
за пользователем.
- **Частично:** bloom/свет, HUD-анимации; эвристика no-trigger damage check
требует ручной проверки.
---
## 2026-10-25 — TASK-2026-002: боевой UX и правила
- **D1–D2:** в `Card3D` добавлено состояние hold-at-source; пока запрос ждёт в
очереди `CardMoveAnimator`, карта не плавно двигается к target.
`GameBoard3D.Render` вызывает `HoldAtSource` после `PlaceImmediate` для
перемещаемых карт; отмена/очистка снимает hold.
- **D3:** `GameBoard3D.FinishDrag` больше не вызывает `DropAt` для
server-authoritative перемещений; карта возвращается в source и ждёт
снапшота, после которого идёт одна дуга.
- **D4:** добавлены EditMode-тесты: queued-карта остаётся в source до старта
arc, отмена queued-запроса возвращает нормальное движение, переключение
видимости руки.
- **D5:** в `Vanguard.Rules` добавлена `SameColumnAcrossPlayers` (3↔2, 4↔0,
V↔1) и используется в legal intents/validation для атаки заднего ряда.
Тесты Tejas обновлены: 3→2, 4→0, негативный тест на противоположную колонку.
- **D6:** Perfect Guard переделан в стадийную способность: `Guard` размещает
Sentinel без оплаты; в `TriggerFromCard` для Sentinel в `OnGuard` добавляется
синтетическая опциональная способность с оплатой сбросом карты того же
клана. Принятие способности открывает выбор из руки, отказ оставляет щит 0.
`GuardCommand` больше не генерирует варианты с `DiscardPrintingId`;
клиентская discard-подсказка перед размещением убрана.
- **D7:** в `AnimationConfig` добавлены `eventPause`, `postDrivePause`,
`postTriggerPause`, `postDamagePause` (по умолчанию 1.5 с); увеличены
`moveDuration` (0.8), `importantMoveDuration` (1.3), stagger, camera durations
и `triggerRevealHold` (1.5). `BattleView` ставит паузу после боевых событий.
- **D8:** `TriggerCheckSequence` получил событие `FocusChanged`; `BattleView`
подписывается и скрывает руку на trigger-зуме, восстанавливая при возврате
камеры. `GameBoard3D.SetHandVisible` управляет видимостью своей руки.
- **Проверки:** клиентские EditMode в копии без `Library` — **50/50 passed**;
компиляция чистая. Серверные тесты (`Vanguard.Rules`) не запускались на
рабочем ПК; проверка только на `ssh home`/Docker.
- **Ручная проверка** draw/call из руки и души/Tejas/Perfect Guard/триггер-камеры
остаётся за пользователем.
---
## 2026-10-23 — защита и оплата способностей ## 2026-10-23 — защита и оплата способностей
- Панель «Фазы» переименована в «Действия»; защита перенесена туда. - Панель «Фазы» переименована в «Действия»; защита перенесена туда.

View File

@ -1,6 +1,6 @@
# Vanguard ReMaster — состояние сессии # Vanguard ReMaster — состояние сессии
**Дата:** 2026-10-24 **Дата:** 2026-10-27
**HEAD:** см. `git log -1` **HEAD:** см. `git log -1`
**Коротко:** сервер готов, клиент — рабочий каркас с 3D-сценами. **Коротко:** сервер готов, клиент — рабочий каркас с 3D-сценами.
@ -12,8 +12,8 @@
пользователем (или его свободный ответ). Не угадывать. пользователем (или его свободный ответ). Не угадывать.
- Полный свод правил — `AGENTS.md`. - Полный свод правил — `AGENTS.md`.
- Новые изменения: анализ DeepSeek → план в - Новые изменения: анализ DeepSeek → план в
`docs/Development/IMPLEMENTATION_PLAN.md` → реализация Kimi. Код на этапе `docs/Development/IMPLEMENTATION_PLAN.md` → реализация. Обе стадии выполняет
анализа не меняется; процесс — `docs/Development/AGENT-WORKFLOW.md`. DeepSeek, раздельно по проходам. Код на этапе анализа не меняется; процесс — `docs/Development/AGENT-WORKFLOW.md`.
## Сервер ## Сервер
@ -359,17 +359,83 @@ client/Assets/Plugins/Vanguard.Shared + MessagePack
1. **TASK-2026-001 — ручная проверка пользователем** (C10): draw, call, ride, 1. **TASK-2026-001 — ручная проверка пользователем** (C10): draw, call, ride,
move/swap, guard, trigger, drive/damage, retire, search, открытие скрытого move/swap, guard, trigger, drive/damage, retire, search, открытие скрытого
урона, рука противника, повторные клики UI во время/после анимации. урона, рука противника, повторные клики UI во время/после анимации.
Автотесты 46/46; код изменён в `GameBoard3D`, `CardMoveAnimator`, Автотесты 47/47; код изменён в `GameBoard3D`, `CardMoveAnimator`,
`BattleView`, `AnimationConfig.asset` + тесты. `BattleView`, `AnimationConfig.asset` + тесты.
2. 3D UX: P0 (движение, ориентации, камера/триггер) и базовый P1 (стрелки, 2. **TASK-2026-002 — реализован D1–D8, остались серверные проверки и
ручная приёмка.** Реализованы: hold-at-source для queued-карт,
drag-and-drop без локального `DropAt`, Tejas-колонка через границу
игроков, стадийный Perfect Guard (отдельная способность Sentinel после
размещения), боевые паузы 1.5 с, скрытие руки на trigger-зуме.
Клиентские EditMode-тесты 50/50. Серверные тесты (`Vanguard.Rules`)
— только на `ssh home`; ручная проверка — пользователь.
3. **TASK-2026-003 — боевой HUD по мокапу: реализовано, требуется ручная
приёмка.** Готово: правая панель действий убрана, подтверждения в правом
нижнем углу, debug-панель по кнопке, верхний HUD с Turn/фазами/таймером/
банком, центральный баннер хода, компактные панели игроков, `Drop/Soul`
из бара, `Deck` неинтерактивен, счётчик `x/6` над уроном, пластины
слотов, рамка/имя/бейдж триггера карты, статы под картой (`▲`/`◆`, крит
только >1), конкретные подписи триггеров (`+5000`, `Крит +1`,
`Поднять юнита`, `Взять карту`, `Лечить`), trigger-зум с плашкой
«Без триггера». Карты урона идут сверху вниз с перекрытием 30%, новая
поверх старой; trigger-карта повёрнута на 90° против часовой. Имена карт
на поле убраны; панель действий — компактные кнопки справа снизу; рука
центрируется по экрану; подсказка муллигана — в верхней панели, при
выборе карт появляется «Заменить N карт»; центральный баннер убран;
левый бар — `Колода, Душа, Рука, Сброс`, `Душа/Сброс` кликабельны.
EditMode 50/50, серверные правила 155/155, standalone собран, клиент
запущен. Триггер: фокус камеры → анимация раскрытия карты → пауза
0,75 с → резолв; затем выбор `+5000` и эффекта (`Critical` — RC/VC,
`Stand` — только повёрнутый RC, `Draw` — авто, `Heal` — карта урона);
подсказки в верхнем блоке. Урон переворачивается вокруг длинной стороны.
Tejas: колонка `V↔1/3↔2/4↔0`, сервер пересобран и задеплоен на
`ssh home`. В Основной фазе — только кнопка «Боевая фаза». Рамка карты —
тонкая чёрная по краю, в руке рамка скрыта; карты лежат широким веером,
своя рука заполняется слева направо, новая карта добавляется правее и
остаётся поверх предыдущей, ширина сохраняется до 10 карт и плавно
уменьшается начиная с 11-й. Рука противника — зеркальный камеро-зависимый
веер ниже верхней панели: новая карта слева, наклон и глубина зеркальны;
позы обеих рук обновляются каждый кадр, добор не летит к устаревшей цели.
Это закреплено тестом `Hands_Fill_Mirrored_And_Newest_Is_On_Top` (58/58).
Возврат карт в колоду: старая карта уходит из руки по дуге под низ колоды
с переворотом рубашкой вверх; следующая стартует только после завершения
предыдущей. Рука идёт ступенчатым reflow: после каждого ухода оставшиеся
карты, включая ожидающие возврата, сдвигаются под новое количество.
Длительность как у добора (`DrawDuration` / `introDrawDuration = 0.6`),
у противника зеркально. Стартовый
Авангард закрыт до конца муллиганов (`MatchProjection` передаёт реальный
`FaceUp`), затем переворачивается вокруг длинной оси слева направо
(у противника зеркально).
Старт боя: камера над стороной игрока лицом к столу, 1,5 с → дуга вниз
на основную позицию → пауза 1,5 с → одновременный добор обеих рук →
только потом муллиган. Добор общий: карта стартует ровно над колодой
полного размера (`cardScale`), рубашкой вверх, летит в руку с переворотом
лицом вверх; длительность как в муллигане — `introDrawDuration = 0.6 с`.
Любой добор последовательный — следующая карта только после предыдущей.
Частично: bloom/свет
и HUD-анимации. 2026-10-27: боевые feedback-баннеры, вспышки и кнопка
«Пропустить анимации» удалены; журнал событий и trigger-подсказки
оставлены. Рука перестраивается под новое количество одновременно с
перемещением уходящей карты (любой выход из руки). При доборе новые
карты показываются по одной, а рука расширяется после каждой. Раскладка
руки идёт через единый `_visualHandCards`: остатки, возвращаемые и уже
добранные карты участвуют в сдвиге после каждого изменения. Hover карты
в руке плавно поднимает её на 10% высоты и возвращает после ухода курсора.
Карта из руки при зажатии ЛКМ не двигается и остаётся в руке
приподнятой; курсор наводится на зону, при отпускании над VC выполняется
смена. Цель определяется raycast-ом по VC-карте/кругу, затем плоскостью.
Невалидный drop оставляет карту в руке. Валидные для смены карты
подсвечиваются в руке; по логу это были TD02/007EN и BT01/015EN.
Карты поля сохраняют обычный drag. EditMode 58/58.
Детали — `docs/Development/IMPLEMENTATION_PLAN.md`.
4. 3D UX: P0 (движение, ориентации, камера/триггер) и базовый P1 (стрелки,
multi-target Counter Blast) готовы. Осталось: `AttackResolutionQueue`, multi-target Counter Blast) готовы. Осталось: `AttackResolutionQueue`,
multi-target Soul Blast/прочие стоимости, отдельный `CostPaymentOverlay`, multi-target Soul Blast/прочие стоимости, отдельный `CostPaymentOverlay`,
world-space Power/Shield (верхний strip принят как HUD), world-space world-space Power/Shield (верхний strip принят как HUD), world-space
dash shader; полировка позиций на конкретном мониторе. dash shader; полировка позиций на конкретном мониторе.
3. Декбилдер: drag&drop, счётчики лимитов, live-валидация, пресеты. 4. Декбилдер: drag&drop, счётчики лимитов, live-валидация, пресеты.
4. 3D-наполнение: модели карт/стола, prefab-экраны сцен, анимации retire/ghost. 5. 3D-наполнение: модели карт/стола, prefab-экраны сцен, анимации retire/ghost.
5. Звук и настройки, DOTween. 6. Звук и настройки, DOTween.
6. Лаунчер WPF. 7. Лаунчер WPF.
Тренировка с ботом и боевой UI с артами/анимациями/локализацией уже есть. Тренировка с ботом и боевой UI с артами/анимациями/локализацией уже есть.

25
opencode.jsonc Normal file
View File

@ -0,0 +1,25 @@
{
"$schema": "https://opencode.ai/config.json",
// MCP for Unity (CoplayDev): мост поднимается внутри редактора Unity
// (Window → MCP for Unity). Сервер доступен, только пока Unity открыт.
"mcp": {
"servers": {
"unity": {
"type": "remote",
"url": "http://localhost:8080/mcp",
},
},
},
// Каталоги, которые Unity генерирует сама: не индексировать.
"watcher": {
"ignore": [
"client/Library/**",
"client/Temp/**",
"client/Logs/**",
"client/obj/**",
"client/Builds/**",
],
},
}