ТЗ на реализацию экранов подготовки и осмотра. Дизайн решён и лежит в Modes - Preparation Screens; чертежи — раздел Лаборатории
ui-prep-final(./scripts/lab-serve.ps1 ui-prep-final). Здесь — что для этого нужно в коде, чего сейчас нет и в каком порядке делать.Написано до первой строки кода по требованию Макса (22.08.2026): «Сначала ЗАПИШЕМ ВСЕ ТЗ по нашим вайрфреймам, изучив что и как у нас уже UI, убедимся что все решили, ничего не забыли обговорить и лишь потом по тз все выполняем».
1. Что строим
Семь экранов, принятых 22.08.2026:
| № | Экран | Жест открытия |
|---|---|---|
| 1 | Страница «Отряд» — четвёрка телами, лента из 8 мест | вкладка экрана подготовки |
| 2 | Страница «Предметы» — «Сосуд» строкой, 3+1 слот, склад | вкладка того же экрана |
| 3 | Панель осмотра — колонка у правой кромки | ЛКМ по юниту, везде где есть юнит |
| 4 | Карточка «Сосуда», таб «Основное» — разворот, 4 клетки | ПКМ по «Сосуду» |
| 5 | Карточка «Сосуда», таб «Дополнительно» — лор и статистика | таб внутри карточки |
| 6 | Карточка Реликвии, таб «Основное» — кит и 6 улучшений | ПКМ по Реликвии, кнопка «о Реликвии» |
| 7 | Карточка Реликвии, таб «Дополнительно» — лор и статистика | таб внутри карточки |
Сквозное правило, определяющее плотность всех семи: описания живут в тултипах, на поверхности имена (§0 дизайн-дока). Исключения — числа статов и цена способности.
2. Что уже есть (и переиспользуется как есть)
Инвентарь снят с HEAD 22.08.2026. Это не справочник — список того, что не надо писать заново.
| Слой | Что есть | Где |
|---|---|---|
| Навигация | UiNavigator, UiScreen, ScreenKind, MenuRouter.OpenLoadout(OpenLoadoutRequest) | Scripts/UI/Navigation, MenuRouter.cs |
| Тултипы | TooltipSystem, TooltipChain (лимит 3, FIFO), TooltipManipulator, ITooltipContentFactory, KeywordLinks | Scripts/UI/Tooltips |
| Компоненты | Slot (три размера, SetSelected, SetIcon), Chip, SlantedChip, PlateButton, PanelFrame, SlantedPanel, BackButton, RelicCard, TooltipCard, AspectBox | Scripts/UI/Components |
| Реестр состояний | UiComponentRegistry + гейт UiStateGateTests + контактный лист | там же |
| Экран лоадаута | LoadoutViewModel, LoadoutInventoryView, LoadoutScreen.uxml, LoadoutInventoryScreen.uxml | Scripts/UI, UI/Screens |
| Состояние забега | RunState.Guild: RosterSlot[], RosterSlot { VesselId, RelicId, AiPresetId, VesselItemIds, SavedPosition, Injuries } | Scripts/Guild/RunState.cs |
| Человек | VesselState { Id, BirthSeed, Name, PregenId, PositiveTraitId, NegativeTraitId, RunsSurvived } | Scripts/Guild/VesselState.cs |
| Травмы | InjuryLedger (SlotsOf, Inflict, AdvanceNode, Remove), InjuryCascade, ConsequenceData | Scripts/Guild |
| Память дома | GuildBook — Летопись, Хроника, Мемориал; ChronicleEntry | Scripts/Guild/GuildBook.cs |
| Команды забега | IRunCommands: SetSlotPosition, SetSlotRelic, InflictInjury, HealInjury, AddGold, RequestSave | Scripts/Guild/Commands |
| Контент | ItemData (скоуп Vessel/Party, моды, активка, иконка), TraitData (полярность, моды), RelicData, VesselData | Scripts/Data/Definitions |
| Конфиг | GameConfig.GuildSize (сейчас 4), VesselItemSlots (3), RelicCapacityBase/Max | GameConfig.cs |
| Сборка боя | GuildRoster.Build(run) — берёт весь run.Guild слот-в-слот | Scripts/Game/Flow/GuildRoster.cs |
| Кооп | IRunCommands у владельца, жест гостя — каналом (образец DeploymentIntentRelay) | Scripts/Game/Session/Net |
3. Чего нет — разрывы между дизайном и кодом
Это главный результат разведки: половина принятого дизайна не поддержана данными.
3.1. Отряд из восьми и боевая четвёрка
GameConfig.GuildSize = 4, а GuildRoster.Build отправляет в бой весь ростер. То есть понятия
«кто выходит в бой» в коде нет вовсе: сейчас в бой идут все, кто есть.
Нужно:
GuildSize→ 8 (данные, не хардкод), плюс сколько мест открыто на старте (6);- признак боевой четвёрки в
RunState— развилка, см. §9.1; GuildRoster.Buildберёт только боевых и в их порядке;- места 7–8 закрыты до улучшения — механики открытия тоже нет (§9.3).
Сейв. RunState помечен [SaveSchema(1)]; расширение массива и новое поле — изменение схемы.
Старый сейв обязан читаться: восемь мест при четырёх записанных, боевые — первые четыре.
3.2. Команд состава и предметов не существует
В IRunCommands есть SetSlotRelic и SetSlotPosition, но нет ничего про людей и вещи. Нужны:
| Команда | Что делает |
|---|---|
SetSlotInBattle(int slotIndex, bool inBattle) | ставит «Сосуда» в бой или уводит в запас |
SwapSlots(int a, int b) | меняет местами два места ростера |
SetSlotItem(int slotIndex, int itemSlot, string itemId) | надевает предмет; пустой id — снимает |
MoveItem(int fromSlot, int fromItemSlot, int toSlot, int toItemSlot) | перекладывает вещь между людьми одним действием |
Все — у владельца забега; гость публикует жест каналом и исполняет владелец (правило равенства,
CLAUDE.md §Кооп).
3.3. Улучшений Реликвии нет в данных
RelicData не знает про улучшения вовсе, а карточка показывает шесть (3×T1 + 3×T2) с состояниями
«взято / доступно / закрыто». Нужны: тип улучшения (или поле в RelicData со списком), место
взятых в RunState (на Реликвию, не на «Сосуда»), правило доступности по уровню Реликвии.
Дизайн улучшений — методичка; самих шестёрок для конкретных Реликвий тоже ещё нет.
3.4. Статистики «Сосуда» нет
Таб «Дополнительно» показывает шесть строк: боёв, побед, смертей, урона нанесено, урона принято,
походов. В коде есть только VesselState.RunsSurvived и Летопись (ChronicleEntry — подвиги, а не
счётчики). Нужен либо счётчик на VesselState, либо агрегат по Летописи — §9.4.
3.4a. Людей в забеге нет вовсе — и дом их не заводит
Найдено при проводке экрана 22.08.2026. RosterSlot.VesselId пуст у всех: наём не написан, а
ProfileService.CreateGuild заводит дом с пустым Roster. То есть страница «Отряд», подключённая
к живому забегу, покажет восемь свободных мест — показывать некого.
Сделано: VesselFactory рождает человека из сида (имя и перки), пул имён вынесен в ассет
VesselNamePool (журнал — про
соседнее решение; сама фабрика описана в своём докстринге).
Осталось и требует нового шва: люди принадлежат ДОМУ (GuildState.Roster), а забег ссылается на
них строковым id. Дом грузит и пишет ProfileService (сборка Game), забег держит RunStateService
(сборка Guild), и IProfileService живёт в Core — то есть отдать ростер через него нельзя, Core
не видит GuildState. Нужен отдельный интерфейс уровня Guild (например IGuildRosterView) с
реализацией в Game; без него связка «дом → забег» не пишется, а не пишется она — экран остаётся
пустым.
Ассета пула имён ещё нет: тип создан, но Unity его не видел (Auto Refresh выключен). Ассет заводится после одного рефреша редактора.
3.5. Лор и внешность — процедурные, генератора в UI нет
VesselState.BirthSeed объявлен как источник досье и внешности («из него детерминированно
разворачивается досье (мечта / характер / байка) и внешность»). Генератор досье для UI не найден —
на карточке нужен текст «кто, откуда, как выглядит».
3.6. Облик в облачении Реликвии
Карточка показывает человека в текущем облачении. Это не UI-задача: нужен способ получить
изображение связки «Сосуд + Реликвия» — рендер живого UnitView в текстуру либо собранный спрайт.
Решения нет.
3.7. Склада предметов не было вовсе
Найдено при реализации фазы 0 и сразу закрыто: предметы «Сосуда» существовали только надетыми
(RosterSlot.VesselItemIds), а снятая вещь исчезала из мира — хранить её было негде. Заведён
RunState.ItemInventory, все правки идут одним глаголом SetSlotItem
(журнал).
3.8. Перетаскивания в UI Toolkit у нас ещё не было
Обе страницы стоят на перетаскивании (в бой, в запас, местами, надеть, снять). Готового
манипулятора в Scripts/UI/Components нет — это новый общий контрол, а не код экрана.
4. Экраны: контракты
Единый вход — экран подготовки с лентой страниц; страницы переключают режим работы с составом, четвёрка остаётся на месте.
4.1. Страница «Отряд»
- Данные:
RunState.Guild(8 мест),VesselStateпоVesselId,RelicDataпоRelicId. - Показывает: четыре тела на живой расстановке (арена под интерфейсом, вуаль лёгкая), подпись — имя и Реликвия; ленту из 8 мест внизу: занятые с портретом и меткой «в бою» / «в запасе», пустые пунктиром, закрытые под замком.
- Жесты: ЛКМ — панель осмотра; ПКМ — карточка; тянуть — в бой, в запас, местами.
- Состояния: пусто · занято · закрыто · выбрано · перетаскивается · недопустимая цель.
- В тултипе: ничего сверх имени и Реликвии на самой странице нет — всё в панели и карточке.
4.2. Страница «Предметы»
- Данные: те же плюс
RosterSlot.VesselItemIds,ItemData,GameConfig.VesselItemSlots. - Показывает: четыре строки (портрет, имя, Реликвия, 4 квадратных слота: 3 открытых + закрытый), склад всех доступных предметов сеткой, панель осмотра справа.
- Жесты: тянуть из склада в слот и обратно, между людьми; ЛКМ — панель; ПКМ — карточка.
- В тултипе: что делает предмет (статы, триггер, заряды).
4.3. Панель осмотра
- Живёт везде, где есть юнит — подготовка, бой, пауза, Двор, расстановка. Один контрол.
- Данные: боевой стат-блок ИТОГОМ (
IUnitStatPreviewуже есть вLoadoutViewModel), перки, снаряжение, профиль AI. - Показывает: портрет, имя, Реликвия и класс; статы числами (не в тултипе!); перки чипами; снаряжение иконками; поведение чипом; две кнопки — «О Сосуде» и «о Реликвии».
- Кнопка без Реликвии гаснет текстом, но остаётся живой (
ui-feedback§1 — HARD). - Скролл обязателен: содержимое длиннее колонки.
4.4. Карточка «Сосуда» — разворот, два таба
- Окно 86% кадра, кромка подложки видна, закрывается Esc / повторным ПКМ / кнопкой.
- Таб «Основное»: облик в облачении колонкой слева; четыре клетки — статы, перки (чипы), снаряжение (иконки + переход к Реликвии), травмы (6 мест 3/2/1 + чипы названий + Закалка).
- Таб «Дополнительно»: слева кто и откуда, как выглядит; справа шесть строк статистики и строка летописи.
- В тултипе: перк, травма, предмет, Закалка.
4.5. Карточка Реликвии — тот же каркас
- Таб «Основное»: знак и облачение, автоатака, уровень и копии, пассивки; справа три способности (знак, имя, цена) и под ними шесть улучшений рядами T1 / T2 с состояниями.
- Таб «Дополнительно»: чей был, как меняет носителя, статистика Реликвии, список носителей.
- В тултипе: эффект и условие способности, что даёт улучшение.
5. Что добавить в данные
| Куда | Что | Зачем |
|---|---|---|
GameConfig | GuildSize = 8, GuildSlotsOpenAtStart = 6, BattleSlots = 4 | числа состава — данные, не хардкод |
RunState | признак боевой четвёрки (§9.1), места взятых улучшений Реликвий | без них экран нечего показывать |
IRunCommands | четыре команды из §3.2 | правки состава и вещей идут одной дорогой |
RelicData | список улучшений (6: 3×T1 + 3×T2) | §3.3 |
VesselState | счётчики статистики либо агрегат по Летописи (§9.4) | таб «Дополнительно» |
| UI-компоненты | DragManipulator, InspectPanel, SpreadCard (окно с табами), UpgradeGrid | новые общие контролы |
Все новые компоненты обязаны попасть в UiComponentRegistry со своими состояниями — иначе гейт
UiStateGateTests красный, и это правильно.
6. Кооп
Гильдия единая, «Сосуды» между игроками не распределяются. Отсюда:
- страницы, панель и карточки — буквально один код у хозяина и гостя; разница считается дефектом;
- осмотр — чтение: открывается любому, чужой осмотр не блокирует;
- правки состава и вещей — жест публикуется одинаково у обеих ролей, исполняет владелец.
7. Локализация
Все надписи — ключами сразу, EN + RU (ui.* — интерфейс, ContentLocalization — контент). Тексты
тултипов идут от контента ({id}.desc), надписи экранов — свои ключи. Правило проекта: дописывать
потом дороже, чем закладывать сразу.
8. Порядок работ
| Фаза | Что | Проверяется |
|---|---|---|
| 0 | ✅ сделано 22.08.2026 — данные: восемь мест, InBattle, склад предметов, три команды | 14 тестов зелёные |
| 1 | ✅ сделано 22.08.2026 — DragManipulator и DropZoneManipulator | компиляция; кадр — за фазой 2 |
| 2 | ✅ сделано 22.08.2026 — страница «Отряд»: разметка, вью, стили, проводка к забегу | 17 тестов; превью Alebardium → UI Preview → Party |
| 3 | ✅ сделано 22.08.2026 — страница «Предметы»: строки, слоты 3+1, склад, проводка | 18 тестов; превью Alebardium → UI Preview → Items |
| 4 | ✅ сделано 22.08.2026 — панель осмотра компонентом, тултипы на чипах, дом на обеих страницах | 12 тестов; кадр из игры — за подключением к навигации |
| 5 | ✅ сделано 22.08.2026 — карточка «Сосуда»: разворот SpreadCard, оба таба, травмы 3/2/1 | 9 тестов; превью Vessel Card |
| 6 | Улучшения Реликвии в данных + карточка Реликвии | fast-tests + кадр |
| 7 | Статистика и лор на табе «Дополнительно» | кадр |
Фазы 0 и 1 не пропускаются: без них экраны негде проверить, а перетаскивание, написанное внутри экрана, придётся переписывать на второй же странице.
8.1. Где мы сейчас (точка продолжения, 22.08.2026)
Сделаны фазы 0–5. 39 тестов зелёные, 34 коммита в dev.
| Готово и живёт в коде | Где |
|---|---|
Отряд из 8 мест, InBattle, склад предметов, 4-й слот по модели наград | RunState, RunStateService, GameConfig |
| Команды состава и вещей во всех четырёх реализациях | IRunCommands и родня |
| Перетаскивание общим контролом | DragManipulator, DropZoneManipulator |
| Страница «Отряд» и страница «Предметы» с проводкой | PartyScreen*, ItemsScreen* |
| Панель осмотра компонентом, стоит на обеих страницах | InspectPanel, InspectPanelPresenter |
| Карточка «Сосуда»: разворот с табами, травмы 3/2/1 | SpreadCard, VesselCard* |
| Дом рождается с людьми, забег их сажает | VesselFactory, IGuildRosterView |
Следующий шаг — подключение к навигации игры. Сейчас экраны живут только в превью-стенде
(Alebardium → UI Preview: Party, Items, Vessel Card). Нужно: открыть страницы из петли забега
(вход через MenuRouter.OpenLoadout / UiNavigator), завести слой поверх экрана под карточку по
ПКМ, связать ЛКМ по «Сосуду» с панелью осмотра. После этого появится первый кадр из игры — до
сих пор ни один экран его не видел.
Ждёт одного рефреша редактора: типы VesselNamePool и поля _vesselItemSlotsMax,
_vesselNamePool в GameConfig написаны, но Unity их не видел (Auto Refresh выключен). После
рефреша заводятся ассет пула имён и заготовка перков — пока люди рождаются под ярлыком
«Безымянный», а фабрика об этом кричит.
Блокирует фазу 6: улучшений Реликвии нет ни в RelicData, ни в контенте (§3.3).
9. Открытые вопросы — решить ДО кода
✅ Решено 2026-08-22 — четыре вопроса закрыты
| Было вопросом | Решение | Запись |
|---|---|---|
| Чем помечена боевая четвёрка | поле RosterSlot.InBattle, не порядок в массиве | журнал |
| Старые сейвы | читаем и расширяем молча, версию схемы не двигаем | там же |
| Статистика «Сосуда» | счётчики на VesselState | там же |
| Чем открываются места 7-8 и 4-й слот | награда внутри забега — сгорает вместе с ним | ГД-журнал 2026-08-22/9 |
Следствие последнего, важное для кода: число открытых мест — состояние ЗАБЕГА, а не конфига.
GameConfig держит базу (6) и потолок (8); сколько открыто сейчас — в RunState. Экран, читающий
вместимость из конфига, покажет чужое число.
9.5. Панель осмотра в бою
Ставит ли паузу? Что происходит, когда осматриваемый погибает — окно закрывается, замирает или показывает «пал»?
9.6. Геймпад
ПКМ и перетаскивание — мышиные жесты. Чем заменяются: кнопка «осмотреть» + подтверждение, или режим перестановки?
9.7. Облик в облачении
Рендер живого UnitView в текстуру или собранный спрайт? Первое честнее и дешевле в поддержке,
второе быстрее на экране.
9.8. Тултип и панель на одном экране
Остаётся ли наведение с тултипом там, где уже открыта панель осмотра, или тултип живёт только на складе, в наградах и внутри карточек?
9.9. Панель осмотра для врага
Тот же состав или урезанный (без снаряжения и перков)?
9.10. Где живёт вход на экран подготовки
Сейчас MenuRouter.OpenLoadout пушит полноэкранный оверлей. Открывается он с карты, из передышки,
из обоих мест? Есть ли он в бою на паузе?
10. Что этот план НЕ трогает
- Вкладка предметов отряда (Party-скоуп) — отложена Максом 22.08.2026.
- Панель эффектов (баффы/дебаффы), второй дом травм — позже, учитывается только как направление.
- Страницы «Реликвии» и «Поведение» — существуют в текущем экране, переезжают в ленту после того, как встанут первые две.