Статус: План согласован (2026-07-14). Развилки §6 закрыты решениями Макса (порядок data-first, имя BattlePresetData, релик = весь кит, всё на UI Toolkit — не Odin). UI релика уточнён — полноэкранный loadout-экран (§5). Реализация — по шагам, каждый шаг отдельной сессией, с ресёрчем перед большими шагами. Остаются мелкие уточнения — §6.
План последовательности работ для игрового среза: интерактивная фаза расстановки, надевание реликов, система энкаунтеров, готовые бои и dev-переключатель между ними. Опирается на уже согласованный дизайн 10. Арена и расстановка (дизайн) и достраивает его до играбельного dev-инструмента.
Связано: 10. Арена и расстановка (дизайн), 7. Флоу забега и сессия, 07. ТЗ — Фаза 4 (Контент-каркас), 01. Roadmap реализации.
1. Что уже есть (карта после разведки 2026-07-14)
Дата-слой — готов. EnemyData и RelicData существуют, оба — тонкие наследники общего UnitData (кит вынесен в базу). Сборка рантайма едина и типо-агностична: RuntimeUnitFactory.Create(UnitData, VesselData, …). Домены id (enemy.*, relic.*) настроены, есть ContentDatabase + Content Hub (редактор). Единственный враг в контенте — enemy.training_dummy; гоблинов нет — контент создаётся с нуля.
Расстановка — данные и правила готовы, интерактива нет. DeploymentService.CanPlace(pos, side, canUseExtended) реализован и покрыт тестами; ArenaLayoutData/DeploymentZone/Rect2D/ArenaBounds готовы; Odin-авторинг арены (ArenaLayoutAuthoring + Scene-хэндлы) готов. Нет: мышиного ввода, picking юнита, drag, визуального фидбэка (outline/зоны/валидность), оркестратора фазы. InputContext.Deployment существует, но включает только камеру. У UnitView есть сокеты и feedback-хуки, но нет outline/selection.
Запуск боёв — конвейер готов, data-driven слоя нет. DI-цепочка GameBootstrap → GameFlow → SceneLoader → CombatLifetimeScope → CombatLoopService работает. ResetBattle()/OnBattleReset — рабочий «загрузчик боя на месте» (чистит сим без перезагрузки сцены, презентация подписана). Нет: ни одного data-driven описания боя (Encounter/Wave/BattleDefinition отсутствуют как класс и как домен). Состав тестовых боёв захардкожен императивно в GuildmasterCommands (gm_spawn_*) + заготовка BattleSetupBuilder.SetupTestBattle (в DI, но никто не вызывает). Dev-UI = Quantum Console команды + хоткеи R/F5, графической панели выбора боя нет.
Вывод: фундамент под всё это есть, но состав боя живёт хардкодом. Это определяет порядок ниже.
2. Рекомендованный порядок и его логика
Ведущий принцип: сначала развязать состав боя от хардкода (данные), потом наполнить (враги), потом сделать интерактив (расстановка, релики).
Почему НЕ начинать с расстановки, хотя запрос начинался с неё:
- Расстановка живёт внутри боя, который надо откуда-то загрузить. Сейчас бой = хардкод в
GuildmasterCommands. Если делать расстановку первой, она встанет на этот хардкод, и её придётся перепроводить, когда появится data-driven загрузка. Фундамент дешевле сделать первым. - Расставляться надо против кого-то. Сейчас враг — дамми. Настоящий бой требует врагов (гоблинов) раньше расстановки.
- «Готовый бой без расстановки» и «с расстановкой» — две ветки одного загрузчика. Значит загрузчик боя первичен, а фаза расстановки — надстройка над ним.
- Надевание реликов — надстройка над выбором юнита в расстановке. Значит оно идёт последним из интерактива.
Порядок: данные (энкаунтер) → контент (враги) → пресеты (готовые бои) → интерактив (расстановка) → релики. Каждый шаг оставляет играбельный результат и не переделывается следующим.
3. Шаги реализации
Шаг 1 — Энкаунтер-данные + загрузчик + dev-переключатель [фундамент]
Содержание:
EncounterData : ContentDefinition(новый доменencounterвContentDomains.cs). Поля по эскизу 10. Арена и расстановка (дизайн) §3, §5: список врагов (EnemyData+ позиция + опц. каунт), ключ/ссылка арены, опц. фиксированный сид, метка сложности.EncounterLoader— data-driven строитель боя поверх паттернаResetForNewBattle() → EnqueueUnitSpawn(оживить/заменитьBattleSetupBuilder). ПереиспользуетResetBattleкак движок переключения без перезагрузки сцены.- Реестр энкаунтеров в
ContentDatabase(механика доменов +TryGetуже есть). - Минимальный dev-UI: список доступных энкаунтеров с кнопками запуска (UI Toolkit — единая дизайн-система, решение Макса; не Odin). Сохранить R = рестарт выбранного.
- Перенести 2–3 существующих хардкод-боя в encounter-ассеты (враги пока = дамми).
Разблокирует: всё дальнейшее встаёт на data-driven загрузку. Хардкод боёв начинает отмирать.
Ресёрч перед шагом: 7. Флоу забега и сессия — владелец EncounterData и связь «энкаунтер → ключ арены» (открытый вопрос дизайна §9); финальная структура полей.
За Максом: визуальная проверка в сцене.
Шаг 2 — Враги: common-гоблины (контент + AI) [контент]
Содержание:
- Дизайн 3–4 базовых common-гоблинов (архетипы, статы, поведение) — по GDD.
EnemyData+ парныеAIPresetDataассеты (новым врагам сразу назначать пресет, не легаси inline-AI).- Наполнить 4–6 энкаунтеров разной сложности настоящими врагами (микс гоблинов + каунты).
Разблокирует: осмысленные бои для расстановки и тестов.
Ресёрч перед шагом: docs/wiki/gdd/enemies, damage-model-schools-affinities (сродства по типу существа), какие архетипы гоблинов дают спектр сложности (танк / рой / стрелок / бьющий).
За Максом: дизайн-ревью гоблинов, визуал.
Шаг 3 — Готовые бои (пресеты) + перенос тестовых боёв + второй dev-список [пресеты]
Содержание:
BattlePresetData:EncounterData(враги) + player-ростер (слоты:VesselData+RelicData+ опц. предметы + сохранённая позиция) +DeploymentMode { Fixed, Free }. Слот релика всегда заполнен: у «пустого» сосуда стоит базовый релик без особенностей (дамми-кит), см. §5.- Загрузчик расширяется: Fixed = спавн сразу по сохранённым позициям (чистый запуск, «реплей-подобный»); Free = отдать player-сторону в фазу расстановки (Шаг 4).
- Перенести все тестовые бои (
gm_spawn_spearman/_shepherd/…) вBattlePreset-ассеты. - Dev-UI: два списка — «Энкаунтеры» и «Готовые бои».
Разблокирует: полноценные пресеты боёв, чистый запуск, основа для фазы расстановки. Ресёрч перед шагом: структура player-ростера; как «сохранённая расстановка» сериализуется в SO (позиции как данные). За Максом: ревью набора пресетов, визуал.
Шаг 4 — Интерактивная расстановка (Free-режим) [интерактив]
Содержание:
- Оркестратор фазы: state-машина
Deployment → Combat, старт боя по «Готово». 4 юнита игрока создаются сразу в фазе расстановки. - Мышиный ввод: карта/действия мыши в
InputServiceдляInputContext.Deployment(позиция + Press). - Picking юнита: математический — ближайший
UnitViewв радиусе тела (Size × BodyRadiusPerSize), без коллайдеров (ближе к «геометрия = данные»). Различать одиночный клик/drag (двигать) и дабл-клик (открыть loadout, Шаг 5). - Drag + валидация через готовый
DeploymentService.CanPlace; анти-оверлап при drop (открытый вопрос дизайна §9). - Визуальный фидбэк: outline выбранного/наведённого юнита, призрак-превью, зелёная/красная индикация валидности, отрисовка
DeploymentZoneв рантайме (сейчас только редакторные гизмо).
Разблокирует: игровую фазу расстановки; на неё встаёт надевание реликов. Ресёрч перед шагом: picking (математика vs collider), архитектура фазы под host-authoritative команды (§6 дизайна — на будущее, но шов заложить), overlay зон и подсветка Extended. За Максом: весь визуальный QA (feel перетаскивания, читаемость).
Шаг 5 — Loadout-экран: надевание релика на сосуд [интерактив]
Утверждённый дизайн (Макс, 2026-07-14). Полноэкранное меню «герой + инвентарь», UI Toolkit.
Содержание:
- Триггер: дабл-клик по сосуду на арене (персонаж без релика или с реликом) → открывается полноэкранный loadout-экран.
- Структура — разделы (табы): сейчас активен только «Релик». Будущие разделы (Предметы, Улучшения, смена AI) — видимые заглушки/disabled-табы, чтобы заложить структуру (реализация — потом).
- Раздел «Релик»: грид карточек реликов. Карточка = анимированный спрайт релика (не иконка) + подпись-название. Наведение → карточка играет idle-анимацию + тултип (описание, теги релика из
InfoTags/TagData, статы). Выбор (клик) → карточка играет анимацию атаки + звук (см. ниже). - Звук при выборе: при выборе релика играет звук — озвучка/voice-стингер релика. Заложить хук в FMOD-каталог (sfx-plan-2026-07); ассет — позже.
- Иконки: поле
_iconоставить в данных (может понадобиться в других местах), но в карточках loadout primary = спрайт, не иконка. - Кнопки: «Принять» — применяет выбор и закрывает экран; «Сохранить» — применяет выбор, но не закрывает (остаёшься в меню). Оба коммитят текущий выбор; разница только в закрытии.
- Механика: релик = весь боевой кит сосуда (один релик на юнита). Назначение слоту → пересборка юнита через
RuntimeUnitFactory.Create(relic, vessel). «Пустой» сосуд = базовый релик без особенностей (слот всегда заполнен). Релики могут повторяться между слотами. - MVVM сразу (как settings-меню), дизайн-система 640×360.
Разблокирует: сборку состава игроком; задел под предметы/улучшения/AI (табы); запоминание между боями.
Ресёрч перед шагом: источник анимированного спрайта релика для карточки (переиспользовать боевой UnitVisual/спрайт-лист idle+attack?); FMOD-хук под звук выбора; MVVM-разводка loadout-экрана по образцу settings-меню (ui-toolkit-settings-menu).
За Максом: визуальный QA, наполнение спрайтов/анимаций реликов, озвучка выбора.
Будущее (вне этого захода)
- Запоминание последней расстановки + реликов между боями (озвучено как «в будущем»).
- Загрузка арена-префаба через Addressables по ключу энкаунтера (сейчас
FindFirstObjectByType). - Host-authoritative сетевые команды расстановки (кооп) — шов заложить в Шаге 4, реализация в сетевой фазе.
4. Граф зависимостей (кратко)
Шаг 1 (энкаунтер-данные + загрузчик)
├── Шаг 2 (враги-гоблины) ──┐
└── Шаг 3 (готовые бои) ◄─────┘
└── Шаг 4 (расстановка, Free)
└── Шаг 5 (релики)
Шаги 1 и 2 частично параллелятся (данные vs контент), но 3 требует обоих. 4 требует 3 (загрузчик Free-ветки). 5 требует 4 (выбор юнита).
5. UI надевания реликов — утверждённый дизайн (Макс, 2026-07-14)
Полноэкранное меню «герой + инвентарь» на UI Toolkit, дизайн-система 640×360. Открывается дабл-кликом по сосуду на арене (с реликом или без).
- Разделы (табы). Меню задумано как хаб экипировки: сейчас — «Релик»; на будущее заложить видимые заглушки под Предметы, Улучшения, смену AI (структура сейчас, реализация потом).
- Раздел «Релик». Грид карточек: внешний вид релика + название. Наведение → тултип (описание, теги релика, статы). Клик → выбор.
- Действия. «Принять» = применить + закрыть; «Сохранить» = применить, не закрывая. Оба коммитят текущий выбор.
- Модель. Один релик = весь боевой кит сосуда. Релики могут повторяться между слотами.
Детали реализации — в Шаге 5. Ранее рассмотренные альтернативы (drag с полки, поповер, dock карточек) отклонены в пользу этого полноэкранного хаба — он масштабируется под будущие разделы (предметы/улучшения/AI), чего точечные варианты не давали.
6. Развилки
Закрыты (решения Макса, 2026-07-14)
- Порядок — data-first (энкаунтер-first), как в §2. ✅
- Имя «готового боя» —
BattlePresetData(энкаунтер + player-ростер +DeploymentMode). ✅ - Релик на юните — один релик = весь боевой кит сосуда. «Пустой» сосуд = базовый релик без особенностей (слот всегда заполнен). ✅
- UI-технология — UI Toolkit везде, включая dev-панели. Odin отклонён. ✅
- UI релика — полноэкранный loadout-хаб, дабл-клик по сосуду; карточки = анимированные спрайты (idle при наведении, атака + звук при выборе), не иконки (§5). ✅
Остаются (мелкие, с дефолтами — уточнить при реализации соответствующего шага)
- Доступность loadout. Дефолт: только в фазе расстановки (в бою не переодеваешься).
- Базовый релик — нужен ассет
relic.*«пустого» дамми-кита (создать в Шаге 3, если нет).
7. Принципы этого захода
- Data-first: состав боя становится данными до того, как строится интерактив поверх.
- Переиспользуем готовое:
DeploymentService.CanPlace(валидатор + тесты),ResetBattle/OnBattleReset(движок переключения),RuntimeUnitFactory(типо-агностичная сборка) — не переписываем. - Шов под кооп/сеть закладываем, реализацию откладываем (host-authoritative команды расстановки — §6 дизайна).
- Каждый шаг = отдельная сессия + коммит после завершённой фазы. Ресёрч перед большими шагами (2, 4, 5).