# Поселение: население Люди города: сколько их, кто работает, сколько едят, как прирастают и кого можно нанять в войско. Это часть модуля [Поселения](Settlement.md): Поселение — это город, его здания и люди. Этап 2 плана разработки. - **Ответственный:** Вальдемар. - **Сборка:** `Bogatyri.Settlement.Population` (без Unity). Отдельная сборка внутри Поселения — как правила добычи внутри Экономики: правила людей без Unity переедут на сервер. - **Папка:** `Assets/Scripts/Modules/Settlement/Population`. - **Зависит от:** ядра. Еду берёт через `IEconomy`, воинов считает через `IArmy`, уровень Ратуши — через `ISettlement`. ## Как устроено - **Люди города = жители + новобранцы в Казармах + воины.** Жителей и новобранцев хранит этот модуль, воинов — Армия. Так один человек не записан в двух местах. - **Жители:** способные работать (`Workforce` — 40% всех людей города, в стартовом городе 120 из 300) и остальные. Способные работать стоят на профессиях (`WorkJob`: лесорубы, фермеры, каменотёсы, шахтёры металла и золота) или свободны (`FreeWorkers`). - **Найм воинов** (решение команды от 19.10.2026). Воины набираются из свободных жителей (не на работе), гражданский резерв не отдаётся: 150 свободных, резерв 30 — нанять можно не больше 120 (`Recruitable`). Воин стоит `PeoplePerSoldier` жителей (1). Человек уходит из жителей в начале обучения (`TryRecruit`), учится в Казармах (`InTraining`, ест как воин) и после обучения переходит в Армию (`RecruitsTrained`). Погибшие в бою воины уменьшают население — их больше нет в Армии. Резерв задаёт уровень поселения: `CivilianReserve` + прибавка за уровень Ратуши (прибавка не утверждена, сейчас 0). - **Рабочих ставят** окно здания (`SetWorkers`) и `App/CitySync` (новые места заполняются свободными сами). Больше, чем мест в зданиях (`IProduction.Slots`) и свободных, не поставить. - **Еда в час:** 0.1 на жителя, 0.2 на воина. Стартовый город (270 жителей + 30 воинов) ест 33 еды в час. - **Прирост:** +2.5 человека в час, только если еды хватило и население ниже предела жилья. - **Голод:** еды не хватило — остатки доедаются, прирост останавливается, `IsStarving = true`. Бой ослабляет голодное войско (модуль Боя, −50%). - **История голода:** модуль помнит отрезки голода за последние `MaxOfflineHours` (48 ч) с точностью до шага расчёта (10 минут). Нужна Бою: поход мог дойти до цели, пока игра была закрыта, и голод берётся на момент боя (`WasStarvingAt`), а не на момент входа в игру. - **Предел жилья:** `HousingBase + HousingPerTownHallLevel × (уровень Ратуши − 1) + HousingPerHouseLevel × уровни Домов` = 300 + 50 + 50. По решению команды от 19.10 стартовый город (300) упирается в предел: прирост пойдёт после постройки Дома. Прибавки не утверждены. - **Добыча** идёт внутри расчёта населения: в каждом куске сначала `IProduction.Produce`, потом еда, потом прирост. Иначе офлайн город «голодал» бы, хотя фермы работали. - **Время** — только из `GameClock`. Расчёт идёт кусками по 10 минут, и в игре, и офлайн, и при перемотке — правила одни. Офлайн досчитывается не больше чем за `MaxOfflineHours`. Если часы ушли назад, расчёт просто продолжается с «сейчас». - **Еда списывается целыми единицами,** доли копятся в модуле: склад хранит `float`, и частые мелкие списания давали бы ошибку округления. ## Контракт `IPopulation` | Член | Что это | | --- | --- | | `Total` | Все люди: жители + воины (в городе и в походах). | | `Civilians`, `Workers`, `Idle` | Жители, из них на работе и остальные. | | `Workforce`, `FreeWorkers` | Сколько могут работать; сколько из них без работы. | | `GetWorkers(job)`, `SetWorkers(job, n)` | Рабочие профессии; поставить n (в пределах мест и свободных). | | `Soldiers` | Воины — из модуля Армии. | | `HousingCap` | Предел населения. | | `FoodPerHour`, `GrowthPerHour` | Расход еды и прирост в час сейчас. | | `IsStarving` | Еды не хватило на последний расчёт. | | `CivilianReserve` | Гражданский резерв: столько свободных жителей в войско не взять. | | `Recruitable` | Сколько воинов можно нанять прямо сейчас: (свободные − резерв) / жителей на воина. | | `InTraining` | Людей, взятых из жителей и ещё обучающихся в Казармах. | | `TryRecruit(n)`, `RecruitsTrained(n)` | Забрать людей в Казармы в начале обучения; обучение закончено — их считает Армия. | | `WasStarvingAt(unixTime)` | Голодал ли город в этот момент (секунды Unix, время `GameClock`). Позже последнего расчёта -- как сейчас; старше 48 часов -- нет. | | `Tick(unixNow)` | Досчитать население до момента `unixNow`. | Получить: `GameServices.Get()`. ## События - **Публикует:** - `PopulationChangedEvent` — изменилось число людей; - `WorkersChangedEvent` — переставили рабочих; - `StarvationChangedEvent` — голод начался или закончился. - **Слушает** (через `App/GameModules`): - `GameLoadedEvent` — офлайн-прогресс; - `GameTimeAdvancedEvent` — перемотка в отладочной панели; - `TroopsChangedEvent`, `TownHallLevelChangedEvent` — пересчёт общего числа и предела. ## Сохранение Секция `"population"`, простые строки: `civilians=`, `jobs=` (рабочие по профессиям), `jobsInit=`, `houses=`, `growth=`, `food=`, `lastTick=`, `starving=`, `training=` (новобранцы в Казармах), `famine=` (история голода: `начало-конец;начало-конец`, конец 0 -- голод идёт), `kit=`. Сброс (`ResetState`): стартовый город, 270 жителей. Стартовое войско (30 пехоты) и стартовые ресурсы выдаёт `App/GameModules` после сброса и при первой игре (`NewGameStartedEvent`) — один раз (`kit=1`). Старым сохранениям без секции населения стартовый набор не выдаётся. ## Связь с игрой (слой приложения) | Файл | Что делает | | --- | --- | | `App/GameModules.cs` | Создаёт модуль с числами из `EconomyConfig`, подписывает на события, выдаёт стартовый набор, держит ресурс «Люди» на верхней панели равным `Total`. | | `Meta/Core/GameProgress.cs` | Казармы: перед обучением проверяет `Recruitable`, забирает людей (`TryRecruit`), после обучения — `RecruitsTrained`, потом воины в Армию. | | `Meta/Panels/TroopsPanel.cs` | Панель «Войска»: «Обучаются в Казармах», «Можно нанять из жителей (резерв N)»; кнопки найма серые, если жителей не хватает. | | `App/PopulationTicker.cs` | Раз в 2 секунды вызывает `Tick` (модуль без Unity и не знает про `Update`). | | `App/DebugPanel.cs` | Кнопка «DEV»: «+1 час», «+10 часов», ресурсы, «Еда = 0». Только редактор и Development Build. | ## Файлы | Файл | Что это | | --- | --- | | `PopulationRules.cs` | Числа правил (заполняются из `EconomyConfig`). | | `PopulationService.cs` | Модуль: расчёт, контракт `IPopulation`, сохранение. | ## Тесты `Modules/Tests/PopulationTests.cs`: - стартовый город — 300 человек, ест 33 еды в час; - прирост 2.5 в час; - голод останавливает прирост, еда вернулась — прирост идёт; - упёрлись в предел — прирост стоит, Ратуша выше — идёт; предел не превышается; - офлайн ограничен 48 часами; - частые расчёты дают то же, что один большой; - часы назад — ничего не ломается; - перемотка идёт через те же правила; - сохранение; - стартовый набор — один раз; - сброс; - найм: 150 свободных, резерв 30 → не больше 120; людей столько же, новобранцы не считаются дважды; рабочие остаются на работе; новобранцы едят как воины и сохраняются; старый заказ обучения не уводит в минус; - история голода: бой офлайн видит голод на свой момент, история сохраняется, сбрасывается и забывается через 48 часов. ## Решения и план - **Голод:** дебаффы (в том числе у защитников) и правило снятия — решение команды от 19.10.2026, числа не утверждены. Модуль уже сообщает `StarvationChangedEvent`. - **Предел жилья:** по решению команды растёт с постройкой Дома — сделано (здание «Население»). - **Этап 3:** сделано 27.10.2026 — рабочие по профессиям, Дома, добыча в общем расчёте. - **Вопросы Саши от 28.10.2026 (отчёт по военной части):** - п. 7, история голода -- сделано (`WasStarvingAt`). Бою нужно перейти с `IsStarving` на `WasStarvingAt(время боя)`. Походы идут по реальному времени, Население -- по `GameClock`: после перемотки в DEV-панели они расходятся. - п. 8, норма еды на солдата -- сейчас 0.2 еды в час реального времени, игрового календаря нет. Предложение: не вводить; если нужен «игровой месяц» -- 1 месяц = 1 сутки, норма ≈ 5 еды. Решает Дима. - **Этап 5:** найм воина забирает человека из жителей (гражданский резерв — решение от 19.10.2026) — сделано 28.10.2026. Цену воина в ресурсах (`EconomyConfig`, раздел «Солдат»: 15 еды, 5 металла, 5 золота) Казармы пока не используют — у них своя цена в `GameData.Troops`. - **Схема модулей (28.10.2026):** население — часть Поселения, как на схеме Димы из 8 модулей. ## Журнал изменений | Дата | Кто | Что | | --- | --- | --- | | 28.10.2026 | Claude | Население стало частью Поселения: папка `Modules/Settlement/Population`, сборка `Bogatyri.Settlement.Population`. Найм воинов из свободных жителей с гражданским резервом (`CivilianReserve`, `Recruitable`, `InTraining`, `TryRecruit`, `RecruitsTrained`), строка `training=`, `Workforce` — 40% всех людей. Тесты. | | 28.10.2026 | Claude | История голода: `IPopulation.WasStarvingAt`, строка `famine=` в сохранении, тесты (вопрос Саши п. 7). | | 27.10.2026 | Claude | Рабочие по профессиям (`Workforce`, `FreeWorkers`, `GetWorkers`, `SetWorkers`, автозаполнение), Дома в пределе жилья, базовый предел 300 (решение 19.10), добыча в общем расчёте. | | 26.10.2026 | Claude | Создан модуль: жители, еда, прирост, голод, предел жилья, офлайн-прогресс, секция `population`, тесты. |