Files
MVP1/Docs/Modules/Settlement.md
T

109 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Поселение
Здания и развитие города: Ратуша, участки, свободная стройка на сетке, постройка зданий, восстановление города из сохранения. Люди города — отдельный модуль [Население](Population.md), связь только через контракты ядра.
- **Ответственность:** совместная.
- **Сборка:** `Bogatyri.Settlement`.
- **Папка:** `Assets/Scripts/Modules/Settlement`.
- **Зависит от:** ядра. Ресурсы на стройку берёт через `IEconomy`.
## Стройка на сетке
Игрок сам выбирает, где строить. Участки (`BuildSlot`) больше не нужны: они и площадки «Застройка» прячутся при запуске, сцену менять не надо.
- **Сетка (`BuildGrid`).** Клетка — 1 единица мира, сетка прямоугольная. Область стройки — прямоугольник вокруг старых участков и Ратуши с запасом `padding` (3 клетки). Можно задать руками: поставить `BuildGrid` на сцену, `autoArea = false`, `areaMin` / `areaMax`. Объект создаётся сам, если его нет.
- **Занятые клетки:** под Ратушей и под каждым построенным зданием. Пересчёт раз в 0.3 с, пока сетка видна, и перед каждой постройкой.
- **Размер здания:** `BuildingData.footprint` (ширина × высота в клетках). `0 × 0` — посчитать по `BoxCollider2D` префаба (иначе по картинке), округлив до клеток. Размер здания задаётся там же, в карточке здания.
- **Призрак (`BuildGhost`):** полупрозрачная копия картинки здания и пятно клеток под ней — зелёное (свободно) или красное (занято / за границей). Только картинка: скриптов и коллайдеров нет, экономика его не видит.
- **Постройка:** `BuildManager.BuildAt(data, cell)` — проверяет клетки и ресурсы, списывает цену, ставит префаб так, чтобы его коллайдер лёг на клетки призрака, публикует `BuildingPlacedEvent`. `CanBuildAt(data, cell)` — та же проверка без постройки.
- **Старые сохранения:** здания встают на свои позиции, занятость считается по тому, какие клетки здание накрывает. Ничего переносить не нужно.
Управление — `Meta/Build/BuildModeController` (слой приложения):
1. «СТРОЙКА» — сетка на земле, подсказка сверху, список зданий (размер в клетках, цена; закрытые по уровню Ратуши — серые).
2. «Выбрать» — призрак встаёт в центр экрана или на ближайшее свободное место.
3. Тащить призрак пальцем (за пятно) или тапнуть по земле — призрак прыгает туда. Камера в это время стоит (`UIBlocker.WorldInputCaptured`).
4. Нижняя панель: «Поставить» (место свободно и хватает ресурсов), «Другое», «Отмена».
5. После постройки режим остаётся; «Здания» сверху — строить дальше, «Выйти» — сетка прячется.
## Стройка по времени, лимиты, перенос (01.11.2026, решения 04.11; числа — ТЕСТ)
- **Время реальное** (решение 04.11): 60 с стройки — 60 реальных секунд, «игровой час» к стройке не относится. Развитие долгосрочное: позже — тиры T1/T2/T3 и внутри них уровни с растущим временем (шкалу согласуют отдельно).
- **Стройка и улучшение идут по времени** (`BuildTimer`). Постройка — `buildSeconds` (60 с), улучшение до уровня N — `upgradeSecondsToLevel` (60 с … 2 ч), Ратуша ×2. Время — часы игры (`GameClock.UnixNowMs`): идёт и в офлайне, и при перемотке DEV.
- **Пока здание строится,** оно полупрозрачное и не работает: его скрипты выключены, `App/CitySync` его не считает (нет мест, жилья, лимитов; Казармы не учат, Терем без героя). При улучшении здание работает на старом уровне, новый — когда время выйдет.
- **Строители:** одновременно не больше `builders` (2) строек и улучшений, Ратуша тоже занимает строителя.
- **Ускорение:** 1 кристалл за `secondsPerGem` (30) секунд оставшегося времени — кнопка в окне здания.
- **Лимит зданий по уровню Ратуши** (`EconomyConfig.buildingLimits`): в списке стройки «построено 2 / 3», сверх лимита — «Лимит: больше на Ратуше N ур.». Здания не из таблицы — без лимита. В окне Ратуши — каких зданий станет больше на следующем уровне.
- **Перенос:** кнопка «Перенести» в окне здания — режим стройки с призраком на месте здания, «Сюда» — здание переезжает бесплатно (`BuildModeController.StartMove`, `BuildGrid.IgnoredBuilding`). Стройка или улучшение продолжаются.
- **Сноса нет** (решение 04.11): удалить здание и вернуть ресурсы нельзя. Перенос — бесплатно, в пределах поселения, уровень и текущие стройка или улучшение сохраняются. Ратушу не переносят.
- **Офлайн по временным меткам** (решение 04.11): в сохранении хранится момент конца стройки. После входа стройка вступает в силу в тот момент офлайн-расчёта добычи, когда время дошло до её конца (`App/ConstructionClock`, событие `GameTimeSimulatedEvent` от Населения, `BuildTimer.MayComplete`). Добыча нового здания или уровня идёт с момента завершения (точность — шаг расчёта 10 минут, никогда не раньше конца стройки).
- **Проверки:** таймер из файла принимается, только если он осмыслен (постройка — у здания 1 уровня, улучшение — до уровня выше текущего); завершение только повышает уровень, повторное ничего не даёт; перенос не тратит и не возвращает ресурсы.
## Контракт `ISettlement`
| Свойство | Что значит |
| --- | --- |
| `TownHallLevel` | Уровень Ратуши (если город не открыт — из сохранения). |
| `BuildingCount` | Сколько построек. |
| `IsCityLoaded` | Открыта ли сейчас сцена города. |
Получить: `GameServices.Get<ISettlement>()`.
## События
**Публикует:**
- `BuildingPlacedEvent` — здание построено (имя здания);
- `TownHallLevelChangedEvent` — Ратуша улучшена (новый уровень);
- `SettlementRestoringEvent` — город начинает восстанавливаться из сохранения. На него `App/GameModules` добавляет Казармы в список стройки.
**Слушает:** ничего.
## Сохранение
Секция `"settlement"`: `{ "townHallLevel", "townHallTimerStartMs", "townHallTimerEndMs", "townHallTimerTargetLevel", "placedBuildings": [ { "buildingName", "posX", "posY", "level", "timerKind", "timerStartMs", "timerEndMs", "timerTargetLevel" } ] }`. Поля таймера (01.11) — стройка или улучшение, которое ещё идёт (`timerKind`: 0 — нет, 1 — постройка, 2 — улучшение). В старых сохранениях их нет — значит, ничего не строится.
Сброс (`ResetState`): Ратуша 1 уровня, постройки убраны, участки освобождены, память модуля очищена. Иначе при следующем входе в город вернулись бы старые здания.
Модуль помнит последнее состояние города, поэтому сохранение на карте мира не стирает постройки. При каждом входе в город (`BuildManager.Start`) здания восстанавливаются один раз. Уровень здания берётся у любого компонента с `ILeveledBuilding`.
## Файлы
| Файл | Что это |
| --- | --- |
| `SettlementModule.cs` | Точка входа: `ISettlement`, сохранение города, восстановление на сцене. |
| `BuildManager.cs` | Список зданий, постройка на сетке (`BuildAt`), поиск данных здания по имени. |
| `BuildGrid.cs` | Сетка стройки: область, занятые клетки, рисунок сетки. Там же `BuildFootprint` — размер здания в клетках. |
| `BuildGhost.cs` | Призрак здания в режиме стройки. |
| `BuildingData.cs` | Описание здания (ScriptableObject): префаб, размер на сетке, цена, нужный уровень Ратуши. |
| `TownHall.cs` | Ратуша и её улучшение. Раньше файл назывался `NewEmptyCSharpScript.cs`. |
| `BuildSlot.cs` | Участок под здание (старая схема, участки прячутся) и связь здания с участком. |
| `PlacedBuildingId.cs` | Метка построенного здания. Раньше файл назывался `PlaceBuilding.cs`. |
В слое приложения остаются `BuildMenuUi.cs`, `Meta/Build/*` (Казармы, `BuildModeController`).
## Решения и план
- **Решение команды от 19.10.2026 (этап 2):** Ратуша даёт базовую вместимость жителей, новое здание «Дом» — дополнительные места.
- Лимит населения = база Ратуши + сумма Домов + бонусы, значения из `EconomyConfig`.
- Уровень поселения задаёт доступные уровни и количество жилья, а также минимальный гражданский резерв.
- **Этап 3:** рабочие места в зданиях (`BuildingData`), экран распределения рабочих.
- **Стройка на сетке (27.10.2026, по просьбе Володи):** вернулись к свободной стройке — игрок сам выбирает место, сетка показывает границы и красные занятые клетки, у зданий разный размер.
- **Этап 4 (утверждён без изменений):** доступность зданий зависит от месторождений позиции.
## Журнал изменений
| Дата | Кто | Что |
| --- | --- | --- |
| 04.11.2026 | Claude | Решения 04.11: снос убран, остался бесплатный перенос; офлайн по временным меткам — стройка, закончившаяся офлайн, вступает в силу в момент завершения внутри расчёта добычи (`ConstructionClock`, `GameTimeSimulatedEvent`, `BuildTimer.MayComplete`); проверки таймеров из сохранения; уровень при завершении только растёт. |
| 01.11.2026 | Claude | Стройка и улучшение по времени (`BuildTimer`), 2 строителя, ускорение за кристаллы, офлайн; лимит зданий по уровню Ратуши; перенос и снос зданий из окна. Сохранение: поля таймера у зданий и Ратуши (старые сохранения читаются). Терем богатыря (`HeroCastle`) и гуляющий герой. Числа — ТЕСТ, `EconomyConfig`. |
| 01.11.2026 | Claude (Саша) | Новое здание «Госпиталь» в списке стройки (`Meta/Build/HospitalBootstrap.cs`, как Казармы): уровни I–V сохраняются как у остальных (`ILeveledBuilding`), тап — окно госпиталя. Места — в `HospitalConfig`. Арта нет: копия обычного здания светло-голубого цвета. |
| 01.11.2026 | Claude (Саша) | Для госпиталя (решения 31.10, п. 7) в ядре есть контракт `IHospital.Beds` — его будет регистрировать здание госпиталя (уровни I–V, 20–60 мест). Здания пока нет. |
| 31.10.2026 | Claude | Ратуша: тап больше не улучшает сразу, а открывает окно Ратуши (событие `TownHall.Tapped`, окно — `App/BuildingWindow`). Улучшение — кнопкой в окне, с ценой и тем, что откроется. |
| 31.10.2026 | Claude | Казармы стали уровневым зданием (`ILeveledBuilding`, уровень сохраняется как у остальных). Тап открывает окно Казарм: очередь обучения, какие войска открыты и что нужно для остальных, улучшение, кнопка «Нанять войска». Войско открывается, когда хватает и уровня Ратуши, и уровня Казарм; считаются самые прокачанные Казармы, каждые Казармы по-прежнему дают +1 место в очереди. Уровни Казарм: пехота 1, лучники 2, конница 3, осадные 5 (`EconomyConfig`, `soldier.requiredBarracksLevel`) — **ТЕСТ, согласовать с Димой**; цена улучшения общая, тоже не утверждена. |
| 31.10.2026 | Claude | Откат: Население снова самостоятельный модуль (`Modules/Population`), из Поселения убрано. |
| 28.10.2026 | Claude | Население стало частью Поселения (`Settlement/Population`, сборка `Bogatyri.Settlement.Population`) — как на схеме Димы. Подробности — [Population.md](Population.md). |
| 27.10.2026 | Claude | Свободная стройка на сетке: `BuildGrid`, `BuildGhost`, `BuildingData.footprint`, `BuildManager.BuildAt` / `CanBuildAt` / `FindData`. Участки и площадки «Застройка» прячутся. Новый `BuildModeController` (призрак, «Поставить / Другое / Отмена»), камера не двигается, пока тащат призрак. |
| 19.10.2026 | Claude | `ResetState()` для сброса сохранений. Убранные здания помечаются, чтобы запись в том же кадре их не сохранила. |
| 19.10.2026 | Claude | Модуль выделен. Стройка и Ратуша платят через `IEconomy`. Сохранение города перенесено из `SaveManager` в `SettlementModule` (секция `"settlement"`). Восстановление защищено от двойного запуска и от освобождения занятого участка. Файлы переименованы под имена классов. |