447 lines
28 KiB
Markdown
447 lines
28 KiB
Markdown
# Implementation Plan
|
||
|
||
Активный журнал задач и планов изменений. Постоянные правила проекта хранятся
|
||
в Skill `vanguard-remaster`; история старых блоков — в `IMPLEMENTATION_CHECKLIST.md`,
|
||
`DEVELOPMENT_CHECKLIST.md` и `docs/HANDOFF.md`. Этот файл не заменяет их и не
|
||
является источником игровых правил.
|
||
|
||
## Процесс
|
||
|
||
1. **DeepSeek** анализирует пожелание, код и зависимости, задаёт вопросы и
|
||
создаёт/обновляет раздел задачи. Игровой код на этом этапе не меняется.
|
||
2. **Kimi** читает `AGENTS.md`, Skill и раздел задачи, сверяет план с текущим
|
||
кодом, реализует, проверяет и обновляет статусы.
|
||
3. Передача — только через файлы репозитория и Git. Память модели и чат не
|
||
считаются общим контекстом.
|
||
4. Если план устарел или требования изменились, Kimi останавливает зависимую
|
||
часть, фиксирует причину и при необходимости возвращает задачу на анализ.
|
||
5. Нельзя удалять чужие задачи, невыполненные пункты и историю статусов.
|
||
|
||
Подробный процесс — `docs/Development/AGENT-WORKFLOW.md`.
|
||
|
||
## Статусы
|
||
|
||
- `[ ]` — не выполнено;
|
||
- `[x]` — выполнено и подтверждено доступными проверками;
|
||
- `[!]` — заблокировано или ждёт решения;
|
||
- `[-]` — больше не требуется после согласованного изменения плана.
|
||
|
||
Для каждого пункта храни отдельно:
|
||
|
||
- **Реализация:** выполнена / не выполнена / не требуется.
|
||
- **Проверка:** выполнена (что именно) / не выполнена (почему).
|
||
|
||
`[x]` нельзя ставить, если реализация есть, а проверка не запускалась.
|
||
|
||
## Журнал задач
|
||
|
||
| ID | Статус | Название | Фаза | Проверка |
|
||
|---|---|---|---|---|
|
||
| TASK-2026-001 | `[ ]` | Плавность карточных перемещений и корректные источники | DeepSeek: анализ готов; Kimi: ожидает | не выполнена |
|
||
|
||
Новые задачи добавляются в конец файла отдельными разделами. Не используй один
|
||
раздел для нескольких независимых задач.
|
||
|
||
---
|
||
|
||
## Шаблон задачи (не является активной задачей)
|
||
|
||
### TASK-YYYY-NNN — краткое название
|
||
|
||
- **Статус:** `[ ]`
|
||
- **Создано:** YYYY-MM-DD
|
||
- **Анализ:** DeepSeek
|
||
- **Реализация:** Kimi
|
||
- **Затронутые системы:** ...
|
||
|
||
#### A. Цель и требования
|
||
|
||
- Что просит пользователь.
|
||
- Ожидаемое поведение.
|
||
- Обязательные правила Skill.
|
||
- Ограничения, которые нельзя нарушать.
|
||
- Критерии готовности.
|
||
|
||
#### B. Текущее состояние
|
||
|
||
- Как работает сейчас.
|
||
- Какие системы и файлы проверены.
|
||
- Существующие механики, которые нужно сохранить.
|
||
- Подтверждённые факты и открытые вопросы.
|
||
|
||
#### C. Чек-лист изменений
|
||
|
||
Каждый пункт:
|
||
|
||
- `[ ]` `MODIFY` / `CREATE` / `DELETE` / `TEST` / `VERIFY`
|
||
- путь к файлу (если известен);
|
||
- класс/метод (если существует);
|
||
- конкретная логика;
|
||
- причина изменения;
|
||
- ожидаемый результат;
|
||
- зависимости от других пунктов;
|
||
- **Реализация:** не выполнена;
|
||
- **Проверка:** не выполнена.
|
||
|
||
Пример формата:
|
||
|
||
```text
|
||
- [ ] MODIFY `client/Assets/.../BattleView.cs` — `BattleView.SomeMethod`
|
||
Логика: ...
|
||
Причина: ...
|
||
Результат: ...
|
||
Зависит от: ...
|
||
Реализация: не выполнена
|
||
Проверка: не выполнена
|
||
```
|
||
|
||
#### D. Последовательность выполнения
|
||
|
||
Нумерованный порядок с обязательными зависимостями. Разделяй работу по системам
|
||
на небольшие шаги.
|
||
|
||
#### E. Проверки
|
||
|
||
- Какие тесты/команды доступны и подтверждены проектом.
|
||
- Какие механики проверить на регрессию.
|
||
- Критерии готовности.
|
||
- Что остаётся на ручную проверку пользователя.
|
||
|
||
#### F. Риски и неизвестные вопросы
|
||
|
||
- Противоречия и побочные эффекты.
|
||
- Вопросы, требующие ответа пользователя.
|
||
- Что нельзя реализовывать до ответа.
|
||
|
||
#### G. Результат
|
||
|
||
- **Реализовано:** ...
|
||
- **Проверки:** ...
|
||
- **Не завершено/заблокировано:** ...
|
||
- **Skill обновлён:** да/нет, почему.
|
||
- **Git:** commit hash, изменённые файлы.
|
||
|
||
---
|
||
|
||
### TASK-2026-001 — Плавность карточных перемещений и корректные источники
|
||
|
||
- **Статус:** `[!]`
|
||
- **Создано:** 2026-10-24
|
||
- **Анализ:** DeepSeek выполнен
|
||
- **Реализация:** Kimi выполнена
|
||
- **Проверка:** автотесты 46/46; ручная C10 — пользователь
|
||
- **Затронутые системы:** `GameBoard3D`, `Card3D`, `CardMoveAnimator`,
|
||
`BattleView`, `AnimationConfig`, EditMode-тесты.
|
||
|
||
#### A. Цель и требования
|
||
|
||
Пользователь сообщил: анимации движения карт «отрабатывают рвано»,
|
||
запускаются ненужные анимации, карты двигаются не из нужных позиций. Нужно
|
||
исправить именно презентационный слой, не меняя правила игры.
|
||
|
||
Ожидаемое поведение:
|
||
|
||
- анимация запускается только при реальном переходе карты между зонами/слотами
|
||
или при изменении позы, а не при каждом UI-рендере;
|
||
- источник анимации — фактическая старая зона/поза карты (рука, поле, Trigger,
|
||
Damage, Drop, Soul, Guardian, Deck), а не всегда колода;
|
||
- рубашка/лицо источника учитываются: карта из колоды начинает движение
|
||
рубашкой вверх, а не лицом;
|
||
- повторный снапшот или локальный `Render` не создаёт второй запрос на ту же
|
||
карту и не откатывает её в середину пути;
|
||
- скрытые карты (рука противника, закрытый урон) не пересоздаются и не летят
|
||
из колоды на каждом `Render`;
|
||
- существующие правила, серверные контракты, `InputLockService`, пул карт и
|
||
публичные визуальные эффекты не ломаются.
|
||
|
||
Ограничения:
|
||
|
||
- не менять `GameEngine`, правила, сервер, протокол и YAML-контент;
|
||
- не добавлять DOTween/UniTask/новые пакеты;
|
||
- не менять сцены/префабы без отдельного решения;
|
||
- не переписывать `CardMoveAnimator` на другой animation stack;
|
||
- геймплей вручную проверяет пользователь; автотесты показывают только
|
||
техническую корректность.
|
||
|
||
#### B. Текущее состояние и найденные причины
|
||
|
||
Проверены:
|
||
|
||
- `client/Assets/Vanguard/Runtime/Board3D/View/GameBoard3D.cs`;
|
||
- `client/Assets/Vanguard/Runtime/Board3D/Animation/CardMoveAnimator.cs`;
|
||
- `client/Assets/Vanguard/Runtime/Board3D/View/Card3D.cs`;
|
||
- `client/Assets/Vanguard/Runtime/Board3D/Data/BoardTypes.cs`;
|
||
- `client/Assets/Vanguard/Runtime/Board3D/Logic/PlaymatLayout.cs`;
|
||
- `client/Assets/Vanguard/Runtime/UI/BattleView.cs`;
|
||
- `client/Assets/Vanguard/Runtime/Battle/AnimationConfig.cs`;
|
||
- `client/Assets/Vanguard/Resources/AnimationConfig.asset`;
|
||
- `client/Assets/Vanguard/Tests/Editor/GameBoard3DTests.cs`.
|
||
|
||
Факты:
|
||
|
||
1. **Скрытые карты с `InstanceId = 0` пересоздаются.** В
|
||
`GameBoard3D.AddOpponentHandSpecs` (`OH:player:i`) и
|
||
`GameBoard3D.AddDamageSpecs` (`D:player:i`) для скрытых карт
|
||
`BoardCardRef.InstanceId` равен `0`. В `GameBoard3D.Render` распределение
|
||
старых карт идёт только через `previousByInstance` и условие
|
||
`spec.Ref.InstanceId != 0`. Поэтому при каждом `Render` такие карты
|
||
арендуются заново, старые освобождаются, а новые получают `moved = true` и
|
||
анимацию из `SourcePose`.
|
||
2. **`BattleView.Render()` вызывается не только на снапшот.** Локальные
|
||
действия (выбор guard, меню карты, подтверждение, статус) тоже вызывают
|
||
`BattleView.Render` → `GameBoard3D.Render`. В сочетании с п.1 это даёт
|
||
«ненужные анимации» на каждый клик.
|
||
3. **`SourcePose` всегда использует колоду.** Метод
|
||
`GameBoard3D.SourcePose` возвращает `_layout.DeckPosition(localSeat)` и
|
||
базовый yaw без состояния лица. Новая карта, пришедшая из Soul, Drop,
|
||
Guardian, Trigger или Damage, начинает полёт из колоды; карта из колоды
|
||
начинает путь лицом вверх, хотя должна быть рубашкой вверх.
|
||
4. **У Damage ключ по индексу, а не по личности.** `D:player:i` остаётся тем
|
||
же при смене открытой карты или при открытии закрытой. Текущий branch
|
||
`sameInstance` освобождает старую карту и создаёт новую с `SourcePose`
|
||
(колода), хотя это может быть переворот/смена карты на месте.
|
||
5. **Нет coalescing в `CardMoveAnimator`.** `Enqueue` не отменяет старый
|
||
queued/active request той же `Card3D`. Если карта успевает сменить зону до
|
||
старта предыдущего запроса, второй запрос может стартовать из устаревшей
|
||
позиции; активная запись при этом может остаться в `_active`.
|
||
6. **`Card3D.SetPose` не является точкой начала движения.** Для существующей
|
||
карты `Render` вызывает `SetPose` (меняет target), затем `Enqueue`; для
|
||
новой карты `PlaceImmediate` + `SetPose`. При повторном `Render` target
|
||
может быть обновлён до/после старта, а старый arc не всегда
|
||
согласован с новым request.
|
||
7. **Конфиг анимаций не синхронизирован с кодом.** В
|
||
`AnimationConfig.asset` нет новых полей `moveDuration`,
|
||
`importantMoveDuration`, `moveDelayBetweenCards`, `arcHeight`, camera/arrow
|
||
полей, хотя они объявлены в `AnimationConfig.cs`. Нужно проверить
|
||
фактические значения после импорта Unity; нельзя считать asset
|
||
настроенным, пока поля не сериализованы.
|
||
8. **`UpdateHandView` каждый кадр делает `SetPose` для руки.** Это может
|
||
перетирать target во время движения карты в руку и мешать стабильности
|
||
arc; нужно проверить и, если подтверждено, не менять target у карты,
|
||
которая уже летит, либо синхронизировать target с move request.
|
||
9. **Пул карт и `InputLock` уже имеют защиту** (`CancelCard`, таймаут,
|
||
`CancelAll`); её нельзя удалять. Исправления должны сохранить отсутствие
|
||
вечного `card-move`-лока.
|
||
|
||
Что сохранить:
|
||
|
||
- `GameBoard3D` как единственный рендер-дифф и владелец пула;
|
||
- `CardMoveAnimator` как единый планировщик;
|
||
- `Card3D` как визуальную карту и arc-интерполяцию;
|
||
- `InputLockService` и существующие long-running последовательности;
|
||
- серверный snapshot как источник состояния.
|
||
|
||
#### C. Чек-лист изменений
|
||
|
||
- [x] **C1. `MODIFY` `GameBoard3D.Render`** — стабильная идентичность
|
||
анонимных карт.
|
||
- Файл: `client/Assets/Vanguard/Runtime/Board3D/View/GameBoard3D.cs`.
|
||
- Добавлена ветка reuse-by-key до поиска по `InstanceId`.
|
||
- Переиспользуется `Card3D` по `spec.Key`, если старая/новая карта анонимна
|
||
или совпадают реальные `InstanceId`; иначе используется `previousByInstance`
|
||
или `RentCard`.
|
||
- При смене `0→real` в том же слоте карта сохраняется и обновляется
|
||
`Bind`/`SetArt`/`SetPose` без анимации.
|
||
- Карты с исчезнувшими ключами освобождаются через `ReleaseCard`.
|
||
- Реализация: выполнена.
|
||
- Проверка: `Repeated_Render_Reuses_Hidden_Cards_And_Does_Not_Enqueue_Moves`,
|
||
`Revealing_Face_Down_Damage_Reuses_Card_And_Stays_In_Damage_Zone` — проходят.
|
||
- Зависит от: C2, C4.
|
||
|
||
- [x] **C2. `MODIFY` `GameBoard3D.AddOpponentHandSpecs` /
|
||
`AddDamageSpecs`** — стабильные ключи и явная политика анимации.
|
||
- Файл: `client/Assets/Vanguard/Runtime/Board3D/View/GameBoard3D.cs`.
|
||
- Ключи `OH:player:i` и `D:player:i` оставлены; `InstanceId=0` не считается
|
||
личностью.
|
||
- Скрытые карты при стабильном ключе не анимируются; новая скрытая карта в
|
||
конце анимируется из Deck.
|
||
- Для урона: смена `0↔real` или `real→real` в том же слоте обрабатывается
|
||
reuse-by-key, а не полётом из Deck.
|
||
- Реализация: выполнена.
|
||
- Проверка: см. C1 + `Board_Renders_Field_Hand_Guardian_Damage_And_Soul`.
|
||
- Зависит от: C1.
|
||
|
||
- [x] **C3. `MODIFY` `GameBoard3D` — карта источника из предыдущего
|
||
`MatchView`.**
|
||
- Файл: `client/Assets/Vanguard/Runtime/Board3D/View/GameBoard3D.cs`.
|
||
- Сохраняются `_previousView` и `_sourceByInstance` до замены `_view`.
|
||
- Построен `Dictionary<int, CardSourcePose>` для `Soul`, `Drop`,
|
||
`FaceUpDamage`; источники `Hand`/`Guardian`/`Trigger`/`Field` покрываются
|
||
существующими `Card3D` через `previousByInstance`.
|
||
- `ResolveSourcePose(spec)`: приоритет — `Card3D` (transfer), затем зона из
|
||
`_sourceByInstance`, затем `DeckPosition` с рубашкой вверх.
|
||
- Источники:
|
||
- Deck: `DeckPosition`, рубашка вверх (`baseYaw * X-flip 180°`);
|
||
- Soul: `SoulPosition`, лицом вверх;
|
||
- Drop: `DropPosition`, лицом вверх;
|
||
- FaceUpDamage: `DamagePosition`, лицом вверх и на боку;
|
||
- Hand/Guardian/Trigger/Field — через `previousByInstance`/transfer.
|
||
- Fallback Deck используется только с комментарием и покрывается тестами.
|
||
- `PlaceImmediate` получает rotation с учётом flip.
|
||
- Реализация: выполнена.
|
||
- Проверка: `New_Field_Unit_Starts_From_Hand_Not_Deck` — проходит.
|
||
- Зависит от: C1, C2.
|
||
|
||
- [x] **C4. `MODIFY` `CardMoveAnimator.Enqueue`** — один активный/queued
|
||
request на карту.
|
||
- Файл: `client/Assets/Vanguard/Runtime/Board3D/Animation/CardMoveAnimator.cs`.
|
||
- В начале `Enqueue` вызывается `CancelCard(card)`, что удаляет queued/
|
||
active request этой же карты, не трогая другие.
|
||
- Источник (`from*`) передаётся из `GameBoard3D` как текущая визуальная
|
||
позиция карты; target проверяется в `Card3D.StartArcMove`.
|
||
- `CancelCard`, `CancelAll`, timeout и снятие `card-move`-лока сохранены.
|
||
- Реализация: выполнена.
|
||
- Проверка: `CardMoveAnimator_Coalesces_Requests_For_Same_Card`,
|
||
`CardMoveAnimator_Releases_Input_Lock_When_Card_Is_Cancelled` — проходят.
|
||
- Зависит от: C1.
|
||
|
||
- [x] **C5. `MODIFY` `GameBoard3D.Render` + `BattleView.Render`** — явно
|
||
различать snapshot-рендер и локальный UI-рендер.
|
||
- Файлы: `GameBoard3D.cs`, `client/Assets/Vanguard/Runtime/UI/BattleView.cs`.
|
||
- `GameBoard3D.Render` получил параметр `bool animateMoves = true` без
|
||
изменения DTO.
|
||
- `BattleView.Render(bool animateMoves = true)`; `OnSnapshot` вызывает
|
||
`Render()` (`true`); все локальные вызовы заменены на
|
||
`Render(animateMoves: false)`.
|
||
- При `animateMoves: false` move-анимации не запускаются; идентичность карт
|
||
не меняется. Появление реально новой карты логирует warning.
|
||
- Реализация: выполнена.
|
||
- Проверка: `Repeated_Render_Reuses_Hidden_Cards_And_Does_Not_Enqueue_Moves`,
|
||
`ClientSmokeTests.Battle_Renders_3D_Damage_And_Rested_Cards` — проходят.
|
||
- Зависит от: C1, C2.
|
||
|
||
- [x] **C6. `MODIFY` `GameBoard3D.UpdateHandView`** — не перетирать движение
|
||
руки.
|
||
- Файл: `client/Assets/Vanguard/Runtime/Board3D/View/GameBoard3D.cs`.
|
||
- В `UpdateHandView` добавлено условие: пропускать карту, если
|
||
`card.IsMoving`, чтобы `SetPose` не менял target во время arc-перехода.
|
||
- `GetHandViewPose` и привязка руки к камере не изменены.
|
||
- Реализация: выполнена.
|
||
- Проверка: `Hand_Is_Anchored_To_Camera_Bottom_Center_And_Faces_Player`,
|
||
`New_Field_Unit_Starts_From_Hand_Not_Deck` — проходят.
|
||
- Зависит от: C3, C4.
|
||
|
||
- [x] **C7. `MODIFY` `AnimationConfig.asset` + проверка значений.**
|
||
- Файл: `client/Assets/Vanguard/Resources/AnimationConfig.asset`.
|
||
- Добавлены/сериализованы все поля из `AnimationConfig.cs`:
|
||
`moveDuration: 0.5`, `importantMoveDuration: 0.9`,
|
||
`moveDelayBetweenCards: 0.18`, `arcHeight: 0.45`,
|
||
`cameraZoomDuration: 0.35`, `triggerRevealHold: 1.2`,
|
||
`cameraReturnDuration: 0.4`, `cameraRezoomDuration: 0.3`,
|
||
`growDuration: 0.35`, `dashScrollSpeed: 1.5`,
|
||
`boostGrowDuration: 0.3`, `boostThickness: 0.8`,
|
||
`arrowFadeDuration: 0.2`.
|
||
- Длительности feedback не менялись.
|
||
- Реализация: выполнена.
|
||
- Проверка: `CardOverlayTests.Animation_Config_Asset_Is_Loaded_From_Resources`,
|
||
поле `arcHeight` читается в `GameBoard3D.Render` — проходят.
|
||
- Зависит от: C5 (можно параллельно).
|
||
|
||
- [x] **C8. `TEST` `GameBoard3DTests`** — регрессии анимаций.
|
||
- Файл: `client/Assets/Vanguard/Tests/Editor/GameBoard3DTests.cs`.
|
||
- Добавлены тесты:
|
||
1. `Repeated_Render_Reuses_Hidden_Cards_And_Does_Not_Enqueue_Moves`;
|
||
2. `Revealing_Face_Down_Damage_Reuses_Card_And_Stays_In_Damage_Zone`;
|
||
3. `New_Field_Unit_Starts_From_Hand_Not_Deck`;
|
||
4. `CardMoveAnimator_Coalesces_Requests_For_Same_Card`.
|
||
- В `CardMoveAnimator` добавлены read-only seam-свойства:
|
||
`PendingCount`, `ActiveCount`, `IsCardQueued`, `IsCardActive`.
|
||
- Исправлен `ClientSmokeTests.Battle_Renders_3D_Damage_And_Rested_Cards`
|
||
для вызова `Render` с новой сигнатурой через reflection.
|
||
- Реализация: выполнена.
|
||
- Проверка: все новые тесты + старые — 46/46.
|
||
- Зависит от: C1–C6.
|
||
|
||
- [x] **C9. `VERIFY` smoke / compile / EditMode.**
|
||
- Скопирован проект без `Library`;
|
||
`Unity 6000.6.6f1 -batchmode -nographics -runTests -testPlatform EditMode`.
|
||
- EditMode: **46/46 passed** (включая 15 `GameBoard3DTests`, 12
|
||
`ClientSmokeTests`, 6 `CardOverlayTests`, 12 `PlaymatLayoutTests`, 1
|
||
Addressables stub).
|
||
- `PLAYMAT_SMOKE success=True board=True cards=25 hand=5/5 fieldVisible=True`.
|
||
- `SCENE_SMOKE success=True active=Login env=True login=True`.
|
||
- Ошибок компиляции нет; warning CS8632 в `ClientSmokeTests` — pre-existing.
|
||
- Реализация: выполнена.
|
||
- Проверка: выполнена.
|
||
- Зависит от: C1–C8.
|
||
|
||
- [ ] **C10. `VERIFY` ручная проверка пользователем.**
|
||
- Пользователь проверяет: draw, call, ride, move/swap, guard, trigger,
|
||
drive/damage, retire, search, открытие скрытого урона, руку противника,
|
||
повторные клики по UI во время/после анимации.
|
||
- Ожидаемо: нет полёта скрытых карт из колоды при каждом клике; источник
|
||
совпадает с зоной; нет рывков/двойных стартов; ввод не зависает.
|
||
- Реализация: не применима.
|
||
- Проверка: остаётся на пользователе.
|
||
- Зависит от: C9.
|
||
|
||
#### D. Последовательность выполнения
|
||
|
||
1. C1 + C2: стабилизировать идентичность/ключи, убрать пересоздание скрытых
|
||
карт. Без этого остальные исправления будут маскировать симптом.
|
||
2. C3: добавить source map и корректные source pose/face. Проверить Deck,
|
||
Hand, Trigger, Damage, Soul/Drop/Guardian.
|
||
3. C4: coalescing в аниматоре и отмена старого request.
|
||
4. C5: разделить snapshot/UI-рендер, убрать лишние запуски.
|
||
5. C6: проверить hand target; не менять без подтверждённой необходимости.
|
||
6. C7: синхронизировать asset.
|
||
7. C8: тесты; C9: проверки; C10: ручная проверка пользователя.
|
||
|
||
#### E. Проверки
|
||
|
||
- Client EditMode: `GameBoard3DTests` + новые тесты из C8.
|
||
- Compile/Assembly: Unity batch mode в копии без `Library`.
|
||
- Smoke: `Vanguard.Editor.SceneSmoke.RunPlaymat`; `Run` только при изменении
|
||
сцен/сборки.
|
||
- `git diff --check`, просмотр `git diff`, отсутствие изменений в
|
||
Rules/Server/Shared/Content.
|
||
- Повторный `Render` с неизменным view: число карт и move-request не растёт.
|
||
- Повторный snapshot во время движения: нет двойного старта/сброса в
|
||
источник.
|
||
- Ручная проверка — пользователем; не заменять её автотестом.
|
||
|
||
Команды (подтверждены проектом):
|
||
|
||
```powershell
|
||
# клиентская копия без Library
|
||
& 'C:\Program Files\Unity\Hub\Editor\6000.6.6f1\Editor\Unity.exe' `
|
||
-batchmode -nographics -projectPath <copy> `
|
||
-runTests -testPlatform EditMode -testResults <copy>\TestResults.xml
|
||
```
|
||
|
||
Серверные тесты не требуются, если не меняются Rules/Server/Shared; при
|
||
изменении контрактов — только `ssh home`/Docker.
|
||
|
||
#### F. Риски и неизвестные вопросы
|
||
|
||
- **Анонимные карты:** `InstanceId=0` не является уникальной личностью;
|
||
нельзя использовать его для глобального map. Нужны стабильные ключи по
|
||
зоне/индексу и отдельная политика для reveal.
|
||
- **Источник вне видимых зон:** Deck не содержит `InstanceId`; для карты из
|
||
колоды источник всегда Deck. Для карты из Soul/Drop/Guardian/Trigger/Damage
|
||
источник можно восстановить из previous view; иначе fallback Deck.
|
||
- **Смена открытого урона:** при перестановке face-up damage возможна
|
||
неоднозначность. Не анимировать перемещение между слотами урона, если это
|
||
не подтверждено; приоритет — не «телепорт из колоды».
|
||
- **`AnimationConfig.asset`:** после добавления полей Unity может сохранить
|
||
старые значения; проверить Inspector, не считать defaults автоматически
|
||
настроенными.
|
||
- **`UpdateHandView`:** если менять target каждый кадр, можно ухудшить
|
||
плавность; C6 требует подтверждения тестом/наблюдением, а не слепой правки.
|
||
- **Не менять** `CardMoveAnimator` timeout/`CancelCard`/`CancelAll` без
|
||
сохранения защиты от зависшего `InputLock`.
|
||
- **Не расширять** задачу на рефакторинг `BattleView` или замену animation
|
||
stack.
|
||
|
||
#### G. Результат
|
||
|
||
- **Реализовано:** C1–C8 (см. чек-лист).
|
||
- **Проверки:** EditMode 46/46 в копии без `Library`; компиляция без ошибок.
|
||
- **Не завершено/заблокировано:** C10 — ручная проверка пользователем.
|
||
- **Skill обновлён:** не требуется; изменения технические, не затрагивают
|
||
подтверждённые правила/архитектуру.
|
||
- **Git:** будут изменены `GameBoard3D.cs`, `CardMoveAnimator.cs`,
|
||
`BattleView.cs`, `AnimationConfig.asset`, `GameBoard3DTests.cs`,
|
||
`ClientSmokeTests.cs`, `IMPLEMENTATION_PLAN.md`, `docs/SESSION-STATE.md`,
|
||
`docs/HANDOFF.md`.
|