Статус: Ready


Архитектура игрового цикла: стейт-машина забега, события как флоу, точки автосейва, структура сцен и мультиплеер-сессия (фиксированное лобби + реконнект). Опирается на 5. Технологический стек и архитектура и 6. Боевая модель.


1. Две петли — не смешивать

УровеньПриродаТехнология
Макро-флоу (меню → лобби → забег → акт → ивент → награды)Пошаговый, ждёт ввода игроков и сетиasync UniTask-флоу
Микро-бой (внутри ивента «Бой»)Реалтайм, детерминированныйFixed-tick loop (доки 5/6)

Макро-флоу детерминизма не требует (это последовательность решений, не покадровая переигровка) — поэтому async/await тут уместен и не угрожает гарантиям детерминизма боя.


2. Иерархия флоу (стек push/pop)

GameFlow (root, composition root + VContainer)
├── Boot → MainMenu → Lobby (мультиплеер-сетап)
├── RunSetup: выбор ГМ [мини-игра], раздача Сосудов, перки [мини-игра], сложность
│                                                          └── 💾 АВТОСЕЙВ (старт забега)
└── Run
    └── Act loop (карта генерится из сида ЦЕЛИКОМ на старте акта)
        ├── MapState — выбор следующего узла
        └── ResolveNode → push IEventFlow по типу узла       └── 💾 АВТОСЕЙВ (перед ивентом)
            ├── BattleFlow:  Prep → Combat → Outcome
            │                          └── 💾 АВТОСЕЙВ (перед наградами)
            ├── ShopFlow / TrainingFlow / HealFlow / TournamentFlow / RiskFlow
            └── MiniGameFlow
        └── Босс (гильдия-финалист) в конце акта → следующий акт

Каждый флоу — async UniTask<TResult> Run(ctx), await-ит под-флоу. Узел карты пушит флоу; завершился → pop назад. Никаких ручных enum-стейтов на верхнем уровне.


3. Ивенты как полиморфные флоу (без центрального switch)

public interface IEventFlow
{
    UniTask<EventResult> Run(RunContext ctx);   // Бой/Магазин/Лечение/Риск/Турнир/Мини-игра
}

Узел карты = { тип, данные }; резолв = создать соответствующий IEventFlow и await Run. Новый тип ивента = новая реализация без правки центрального кода (≥6 реализаций — интерфейс «по делу»).


4. RunState (durable) vs RuntimeUnit (на бой)

  • RunState — переживает забег и сохраняется: сид, текущий акт, граф карты + позиция, ростер гильдии (Сосуды + инвентарь реликвий/предметов), золото/ресурсы, травмы, сложность, привязка игрок→Сосуды, ГМ, разблокированные AI-профили. Всё по строковым ID.
  • RuntimeUnit (6. Боевая модель) — собирается из RunState на каждый бой, после боя отбрасывается; травмы/изменения пишутся обратно в RunState.

RunState = сейв; RuntimeUnit = эфемерный боевой слой. Чёткое разделение убивает прошлую боль «рантайм протёк в данные».


5. Автосейв — на переходах флоу

Три точки из 2. Сохранения = вызовы на переходах:

await _save.Autosave(runState);   // 1) старт забега  2) перед стартом ивента  3) перед экраном наград

Сейв = снапшот RunState с ID → три строчки в нужных местах.


6. Бой и ретраи (до 2 раз)

BattleFlow: Prep → Combat → Outcome. Поражение (попытка 1–2) → возврат в Prep (переназначить реликвии/позиции/AI) → перезапуск. 3-е поражение — финальное.

  • Точка восстановления = автосейв «перед ивентом».
  • Детерминизм vs ретрай: саб-сид боя = runSeed + индекс_боя + номер_попытки. Каждая попытка воспроизводима и реплеится, но не идентична предыдущей; основная агентность игрока — изменить стратегию в Prep.

7. Мини-игры — модульные, изолированные

public interface IMiniGame { UniTask<MiniGameResult> Run(MiniGameContext ctx); }

Каждая — флоу за этим интерфейсом, плагается в трёх контекстах (создание Сосуда, выбор ГМ, ивенты карты). Отдельная сборка Guildmaster.MiniGames, чтобы не запутывать ядро. Добровольные (перк-мини-игра) — можно пропустить, вернув дефолт.


8. Карта акта

Генерится целиком из сида на старте акта (структура как Slay the Spire — игрок видит весь граф и планирует путь). Хранится в RunState (граф + позиция). Генерация карты, наград, пула магазина, ивентов — через IRngService (см. 3. Рандомизация).


9. Структура сцен

  • Persistent Core-сцена (всегда загружена): VContainer-корень, менеджеры, RunState, постоянный UI-слой (оверлеи).
  • Аддитивные геймплейные сцены: MapScene, BattleScene — грузятся/выгружаются по флоу через NGO scene management (синхронно у всех).
  • «Глянуть карту/меню в бою» (как в StS) = read-only UI-оверлей, читающий RunState (всегда в памяти), а не переключение сцен. Дёшево и доступно из любого места.

Так память чистая (боевая сцена выгружается), а карта/меню доступны в бою через UI поверх.


10. Мультиплеер: сессия и реконнект

  • Host-authoritative: макро-флоу крутится на хосте, клиентам реплицируется состояние, клиенты шлют интенты.
  • Ready-gate — переходы, ждущие всех игроков:
    await _net.WhenAllPlayersReady();   // все закончили Prep / все загрузили боевую сцену
    (Чистая версия старых AllPlayersLoadedScene / PlayerTurnEndConfirmed.)
  • Состав фиксирован в лобби (по SteamID), но реконнект поддержан:
    • Хост резервирует слот ушедшего.
    • Сосуды ушедшего продолжают драться по своему AI-профилю — автобатлер делает дисконнект graceful (нет паралича, как было бы при прямом управлении).
    • Управление ГМ при выходе владельца — временный fallback на хоста/другого игрока.
    • Реконнект: хост стримит текущий RunState → игрок забирает слот.
  • Автопауза при дисконнекте (в бою): хост ставит тиковый цикл на паузу. Резолюция на выбор: подождать / продолжить без игрока (его Сосуды на AI) / выйти в меню. Опциональный авто-таймаут против грифа бесконечной паузой.
  • Пауза = окно синка реконнекта: во время паузы вернувшегося клиента подтягивают (снапшот RunState + сим-снапшот или переигровка лога команд от старта боя). Это закрывает mid-battle реконнект.
  • Возобновление — синхронный отсчёт «3…2…1»: хост назначает общий тик старта, все клиенты возобновляются одновременно (иначе рассинхрон).
  • Пауза боя стопит тиковый цикл, не макро-флоу (в макро «пауза» встроена — флоу ждёт на ready-gate).

11. Сборки (обновить карту при реализации)

Флоу оркестрирует Combat + Guild + UI + сцены → нужны новые верхнеуровневые модули:

  • Guildmaster.Game — оркестрация + composition root (VContainer-bootstrap), зависит от Core/Units/Combat/Guild, поднимает UI.
  • Guildmaster.MiniGames — изолированные мини-игры.

Добавить в 1. Сборки (Assembly Definitions) при создании модулей.


Открытые вопросы

  • Поведение ГМ при выходе игрока-владельца: автоматический fallback или ручная передача.
  • Mid-battle реконнект: снапшот сим-состояния vs переигровка лога команд — выбрать при реализации сети.
  • Структура RunState в деталях (поля, версионирование сейва).
  • Что именно дают Тренировка / Турнирное событие / Риск-ивент — контент-дизайн (GDD 7. Ивенты).