Files
MVP1/Docs/Modules/Population.md
T

116 lines
14 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.
# Поселение: население
Люди города: сколько их, кто работает, сколько едят, как прирастают и кого можно нанять в войско. Это часть модуля [Поселения](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<IPopulation>()`.
## События
- **Публикует:**
- `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`, тесты. |