Files
MVP1/Docs/Modules/Settlement.md
T

24 KiB
Raw Blame History

Поселение

Здания и развитие города: Ратуша, участки, свободная стройка на сетке, постройка зданий, восстановление города из сохранения. Люди города — отдельный модуль Население, связь только через контракты ядра.

  • Ответственность: совместная.
  • Сборка: 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).

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, целиком или никак.
    • Расчистка — один раз на объект.
  • Сцена (Assets/Scripts/City3D): местность 48 × 48 с холмами, река, скалы, 90 деревьев, 22 валуна, трава.
    • Сетка, открытая зона и следующая зона видны только в режиме стройки и редактирования.
    • Новое здание: кнопка типа → куб-призрак тянется пальцем, зелёный или красный с причиной, «Повернуть 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), окна — с Володей.

Решения и план

  • Решение команды от 19.10.2026 (этап 2): Ратуша даёт базовую вместимость жителей, новое здание «Дом» — дополнительные места.
    • Лимит населения = база Ратуши + сумма Домов + бонусы, значения из EconomyConfig.
    • Уровень поселения задаёт доступные уровни и количество жилья, а также минимальный гражданский резерв.
  • Этап 3: рабочие места в зданиях (BuildingData), экран распределения рабочих.
  • Стройка на сетке (27.10.2026, по просьбе Володи): вернулись к свободной стройке — игрок сам выбирает место, сетка показывает границы и красные занятые клетки, у зданий разный размер.
  • Этап 4 (утверждён без изменений): доступность зданий зависит от месторождений позиции.

Журнал изменений

Дата Кто Что
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) — ТЕСТ, согласовать с Димой; цена улучшения общая, тоже не утверждена.
31.10.2026 Claude Откат: Население снова самостоятельный модуль (Modules/Population), из Поселения убрано.
28.10.2026 Claude Население стало частью Поселения (Settlement/Population, сборка Bogatyri.Settlement.Population) — как на схеме Димы. Подробности — 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"). Восстановление защищено от двойного запуска и от освобождения занятого участка. Файлы переименованы под имена классов.