Здания и развитие города: Ратуша, участки, свободная стройка на сетке, постройка зданий, восстановление города из сохранения. Люди города — отдельный модуль [Население](Population.md), связь только через контракты ядра.
Игрок сам выбирает, где строить. Участки (`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. После постройки режим остаётся; «Здания» сверху — строить дальше, «Выйти» — сетка прячется.
- **Время реальное** (решение 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 уровня, улучшение — до уровня выше текущего); завершение только повышает уровень, повторное ничего не даёт; перенос не тратит и не возвращает ресурсы.
Секция `"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`, сохранение города, восстановление на сцене. |
## 3D-поселение (ТЗ «Система строительства поселения» от 04.11.2026)
Переход на 3D (2.5D: наклонная камера без вращения) идёт **отдельной сценой**`Assets/Scenes/City3D.unity`. Старый 2D-город (MVP1) работает как раньше, пока 3D его не догонит. Создать сцену: **Tools → Bogatyri → 3D-город: создать сцену City3D**, затем Play.
**Этап I — прототип на примитивах (сделан 04.11.2026):**
- Правила места — без Unity, сборка `Bogatyri.Settlement.Placement` (`SettlementMap`), тесты `PlacementTests` (сценарии ТЗ 01–05, 09–12).
- Невидимая сетка; клетка 1 единица мира.
- Здание — прямоугольник клеток с максимальным габаритом с первого уровня, поворот 0/90/180/270.
- Проверки: край карты, зона по уровню Ратуши, река и скалы (постоянные), деревья и валуны (расчищаются), другие здания, технический отступ 1 клетка, перепад высоты под зданием до 0,7 м (больше — «слишком круто», меньше — фундамент).
- Перенос — то же здание с тем же id, целиком или никак.
- Сетка, открытая зона и следующая зона видны только в режиме стройки и редактирования.
- Новое здание: кнопка типа → куб-призрак тянется пальцем, зелёный или красный с причиной, «Повернуть 90°», «Поставить» / «Отмена».
- Перенос: «Переместить» в меню здания или режим «Редактировать»; «Подтвердить» / «Отмена» возвращает на последнее подтверждённое место.
- Трава прячется под зданием и возвращается, когда его перенесли.
- Камера: перетаскивание, два пальца / колесо, границы карты; при открытии зоны плавно отходит.
- Ратуша −/+ — **только для теста** зон.
- Рендер: в URP добавлен второй рендерер — обычный 3D (`Assets/Settings/Renderer3D.asset`), его использует только камера City3D. 2D-сцены не тронуты.
**Предложения на согласование (ТЗ, раздел 12), ТЕСТ:** клетка 1 м; отступ 1 клетка; перепад до 0,7 м; зоны — квадраты от центра 16 / 24 / 32 / 40 / 48 клеток для Ратуши 1–5; габариты: Ратуша 4×4, Дом 2×2, Ферма 3×3, Лесопилка и Каменоломня 3×2, Шахта 2×2, Склад и Амбар 2×3, Казармы и Госпиталь 3×3.
**Дальше:** этап II — стройка через модуль Поселения (цена, таймер по реальному времени, события, сохранение позиции, поворота и id), перенос живых зданий без сброса производства / обучения / лечения; этап III — награда за расчистку, сохранение расчищенного, автодороги, зоны от настоящей Ратуши; этап IV — телефон. Кнопки — временные (IMGUI), окна — с Володей.
- **Стройка на сетке (27.10.2026, по просьбе Володи):** вернулись к свободной стройке — игрок сам выбирает место, сетка показывает границы и красные занятые клетки, у зданий разный размер.
| 05.11.2026 | Claude (Саша) | Перенос 2D-города надёжнее: если места нет, деревья и валуны под зданием расчищаются (бесплатно, без награды — `SettlementMap.ValidateIfCleared`, `ObstaclesUnder`, тест `Placement_IfCleared_IgnoresOnlyTreesAndListsThem`); если места нет и так — здание не теряется, а ждёт в `settlement3d.pendingFrom2D` и ставится при входе в город или когда Ратуша открывает зону. DEV → «Камера»: ползунки наклона, перспективы (поле зрения), ближе / дальше всего и текущего отдаления; подобранное запоминается на устройстве (только редактор и отладочная сборка), «Сбросить» — числа из сцены. |
| 05.11.2026 | Claude (Саша) | **Решение команды: только 3D-поселение, камера перспективная.** (1) В 3D перенесены Золотой рудник (золото, Ратуша 2, 50 камня), Кристалл (Ратуша 6, цена из `Data_Kristall`) и Терем (`HeroCastle`, окно героя; фигурка героя гуляет только по 2D-сетке, в 3D — нет). (2) Перенос 2D-города: если в сохранении нет `settlement3d`, а есть 2D-город, при первом входе в 3D Ратуша получает уровень и улучшение из 2D, остальные здания встают на ближайшие свободные места с теми же уровнями и стройками (`City3DController.MoveFrom2D`, итог — в Console «[3D] Перенос 2D-города»). Секция `settlement` в файле не меняется. (3) `SceneNavigator.CitySceneName` = City3D; MVP1 выключена в Build Settings (сцена и код 2D остаются в репозитории до конца проверки). (4) `City3DCamera.perspective` (по умолчанию да, поле зрения 40°) — угол, высота и поле зрения ТЕСТ. Меню стройки — в два столбца. |
| 05.11.2026 | Claude (Саша) | 3D: камера сцены City3D ортографическая — у неё расстояние не меняет размер города, поэтому «+ / −» и щипок не приближали. `City3DCamera.Apply` для ортографической камеры ставит `orthographicSize` по расстоянию (как видно перспективной камерой с этого расстояния). |
| 05.11.2026 | Claude (Саша) | 3D: дымка идёт за камерой (`City3DCamera.Apply`: начало и конец — от расстояния камеры), поэтому при отдалении город больше не уходит в серую дымку. Окно здания: кнопка «Улучшить» (Ратуша и уровни зданий) сама становится зелёной, когда ресурсов стало хватать (`BuildingWindow.LiveAfford`) — не нужно закрывать и открывать окно. |
| 05.11.2026 | Claude (Саша) | 3D: здание, которое ещё строится, теперь видно — оно серее, над ним «Стройка 0:45» (`BuildTimer` затемнял только 2D-спрайты, поэтому Казармы выглядели готовыми, а «Войска» писали «казарм: 0»). Окно «Войска»: пока Казармы строятся — «Казармы строятся, осталось …», после стройки окно перерисовывается само; «Построить Казармы» в 3D ставит призрак Казарм (`City3DController.PlaceBuilding`). Кнопки «+ / −» справа — приближение камеры (в Device Simulator колесо мыши не доходит до игры). |
| 05.11.2026 | Claude (Саша) | Интеграция 3D: (1) снимок 3D-города для сохранения до расстановки сохранённых зданий отдаёт загруженное состояние — сохранение в первом кадре (поход вернулся офлайн) больше не затирает 3D-город пустым; (2) `ISettlement.TownHallLevel` вне города — уровень Ратуши последнего открытого города (2D или 3D), а не всегда 2D-сохранения: на карте мира предел жилья, резерв и открытие войск считаются по 3D-Ратуше. |
| 04.11.2026 | Claude | Решения 04.11: снос убран, остался бесплатный перенос; офлайн по временным меткам — стройка, закончившаяся офлайн, вступает в силу в момент завершения внутри расчёта добычи (`ConstructionClock`, `GameTimeSimulatedEvent`, `BuildTimer.MayComplete`); проверки таймеров из сохранения; уровень при завершении только растёт. |
| 01.11.2026 | Claude | Стройка и улучшение по времени (`BuildTimer`), 2 строителя, ускорение за кристаллы, офлайн; лимит зданий по уровню Ратуши; перенос и снос зданий из окна. Сохранение: поля таймера у зданий и Ратуши (старые сохранения читаются). Терем богатыря (`HeroCastle`) и гуляющий герой. Числа — ТЕСТ, `EconomyConfig`. |
| 04.11.2026 | Claude (Саша) | 3D-поселение, этап I (ТЗ от 04.11): правила места `Bogatyri.Settlement.Placement` + тесты, сцена City3D на примитивах (местность, зоны, призрак, поворот, перенос, расчистка, камера), 3D-рендерер для её камеры. Ветка `feature/3d-settlement`. |
| 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`) — **ТЕСТ, согласовать с Димой**; цена улучшения общая, тоже не утверждена. |
| 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"`). Восстановление защищено от двойного запуска и от освобождения занятого участка. Файлы переименованы под имена классов. |