Files
MVP1/Docs/Modules/Population.md
T
AlexanderandClaude Opus 5.5 6c39c8b102 Пропавшие без вести (решения 01.11, п. 5)
Бой: отдельный статус среди выбывших (доля не утверждена -- 0, выключено), версия правил 8.
Армия: пропавшие из похода -- в группу; окно 6 игровых часов после обратного пути, возвращение 2-4 группами,
70% здоровыми / 30% ранеными, остальные списываются; сохранение без повторного возвращения.
Население: занимают места, но не едят. Окна: сколько пропало и сколько ещё может вернуться.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 15:16:43 +01:00

126 lines
21 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.
# Население
Люди города: жители, рабочие, прирост, еда, голод, найм в войско и демографические ограничения. Самостоятельный модуль (регламент 28.10 и 31.10.2026). С Поселением (здания, Ратуша) связан только через контракт ядра `ISettlement`. Этап 2 плана разработки.
- **Ответственный:** Валдемар.
- **Сборка:** `Bogatyri.Population` (без Unity).
- **Папка:** `Assets/Scripts/Modules/Population`.
- **Зависит от:** ядра. Еду берёт через `IEconomy`, воинов считает через `IArmy`, уровень Ратуши — через `ISettlement`.
## Как устроено
- **Люди города = жители + новобранцы в Казармах + воины.** Жителей и новобранцев хранит этот модуль, воинов — Армия. Так один человек не записан в двух местах.
- **Жители:** работать может любой житель — общего ограничения нет (регламент 31.10). Рабочих ограничивают свободные жители и рабочие места в зданиях. Рабочие стоят на профессиях (`WorkJob`: лесорубы, фермеры, каменотёсы, шахтёры металла и золота), остальные свободны. Стартовый город: 120 рабочих из 270 жителей — при первой расстановке (`AutoFillAll`) больше `StartWorkers` (120) не ставится.
- **Найм воинов** (решение команды от 19.10.2026). Воины набираются из свободных жителей (не на работе), гражданский резерв не отдаётся (`Recruitable` = свободные − резерв). Пример команды: 150 свободных, резерв 30 — нанять можно не больше 120. Воин стоит `PeoplePerSoldier` жителей (1). Человек уходит из жителей в начале обучения (`TryRecruit`), учится в Казармах (`InTraining`, ест как воин) и после обучения переходит в Армию (`RecruitsTrained`). Погибшие в бою воины уменьшают население — их больше нет в Армии. Резерв зависит от уровня Ратуши (регламент 31.10, ТЕСТ): уровни 1–2 — 10, 3–5 — 20, 6–10 — 30 (`EconomyConfig`, `civilianReserveByTownHallLevel`).
- **Группы людей не пересекаются** (регламент 31.10). Каждый человек ровно в одной группе: свободный житель (`Idle`), рабочий (`Workers`), новобранец (`InTraining`), воин (`Soldiers`, хранит Армия). `Total` — их сумма. На работу ставятся только свободные жители, в войско берутся только свободные жители сверх резерва.
- **Рабочих ставит игрок** в окне здания (`SetWorkers`): не больше, чем мест в зданиях (`IProduction.Slots`) и свободных жителей. Автоматически места заполняются только один раз — стартовая расстановка (`AutoFillAll`, до 120 рабочих) в новой игре или при первой загрузке старого сохранения. Новые места после постройки или улучшения остаются пустыми, пока игрок не поставит рабочих. Мест стало меньше — лишних рабочих снимают (`ClampJobs`).
- **Еда в час:** 0.1 на жителя, 0.2 на воина. Стартовый город (270 жителей + 30 воинов) ест 33 еды в час.
- **Прирост:** +2.5 человека в час, только если еды хватило и население ниже предела жилья.
- **Голод:** еды не хватило — остатки доедаются, прирост останавливается, `IsStarving = true`. Добыча всего, кроме еды, ×0.5 (модуль Экономики, регламент 31.10, ТЕСТ), еда добывается без штрафа. Бой ослабляет голодное войско (модуль Боя, −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` | Воины — из модуля Армии: в городе, в походах и раненые (решения 31.10, п. 7: раненые — тоже люди). |
| `HousingCap` | Предел населения. |
| `FoodPerHour`, `GrowthPerHour` | Расход еды и прирост в час сейчас. |
| `IsStarving` | Еды не хватило на последний расчёт. |
| `CivilianReserve` | Гражданский резерв: столько свободных жителей в войско не взять. |
| `Recruitable` | Сколько воинов можно нанять прямо сейчас: (свободные − резерв) / жителей на воина. |
| `InTraining` | Людей, взятых из жителей и ещё обучающихся в Казармах. |
| `TryRecruit(n)`, `RecruitsTrained(n)` | Забрать людей в Казармы в начале обучения; обучение закончено — их считает Армия. |
| `CompleteTraining(type, n)` | Передать n новобранцев в Армию одним действием: люди уходят из Населения, только если Армия их приняла. Иначе ничего не меняется — никто не теряется и не дублируется. Так заканчивают обучение Казармы (`GameProgress`). |
| `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 (Ратуша 6) → не больше 120; стартовый город — резерв 10; резерв по уровням Ратуши 10/20/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 и 31.10.2026). Перенос внутрь Поселения от 28.10 был ошибкой и откатан 31.10.
- **Открыто (регламент 31.10, ждёт решения):** нормы еды для четырёх групп (гражданские и рабочие, новобранцы, солдаты, раненые); переход экономики на игровое время (1 игровой час = 5 реальных минут) и единицы офлайна (48 ч) и истории голода; кто считает предел жилья и рабочие места (по регламенту — Поселение); недоступен ли резерв и для работы.
## Журнал изменений
| Дата | Кто | Что |
| --- | --- | --- |
| 04.11.2026 | Claude (Саша) | Пропавшие без вести (решения 01.11, п. 5) занимают места в населении (`Soldiers` включает `IArmy.MissingTotal`), но не едят. |
| 04.11.2026 | Claude (Саша) | Решения по отчёту Саши от 01.11, п. 4: воины едят по норме типа — пехота 0,15, лучники 0,20, кавалерия 0,15, осадные 0,15 в час (`PopulationRules.FoodPerTroopTypePerHour`, `EconomyConfig.population.foodPerTroopTypePerHour`); раненые — полную норму своего типа, даже ожидая койку (`IArmy.GetAllTroops`). Новобранцы — по `foodPerSoldierPerHour`. Без таблицы — как раньше (тесты Населения не менялись). Час — тот же, что у остальной еды (переход еды на игровые часы — открытый вопрос). |
| 01.11.2026 | Claude (Саша) | Раненые считаются людьми: `Soldiers` = в городе + в походах + `IArmy.WoundedTotal` (решения 31.10, п. 7). Погибшие уходят из людей, вылеченные не появляются заново. Еда раненых — как у воинов (нормы четырёх групп — открытый вопрос). |
| 01.11.2026 | Claude (Саша) | Очередь обучения перенесена в Армию (решения 31.10, п. 8). Население по-прежнему отдаёт жителей (`TryRecruit`) и принимает новобранцев в воины (`CompleteTraining`) — его код не менялся, зовёт их теперь Армия. `RecruitsTrained` больше никто не вызывает. |
| 31.10.2026 | Claude | `CompleteTraining`: новобранцы переходят в Армию одним действием с откатом, если Армия не приняла. Заказ обучения помечает, брали ли под него людей (`TrainingOrder.recruited`); старые заказы — по-старому. Интеграционные тесты с настоящей Армией: переход без потерь, отказ Армии, повтор, нет Армии, часть заказа. |
| 31.10.2026 | Claude | Автозаполнение новых мест убрано (`AutoFill` удалён, `App/CitySync` больше не ставит рабочих после стройки и улучшения), осталась только стартовая расстановка один раз. При загрузке рабочих не больше, чем жителей. Тесты: группы людей не пересекаются и в сумме дают всех, рабочих не больше свободных, новые места пустые, стартовая расстановка один раз. |
| 31.10.2026 | Claude | По регламенту 31.10: Население снова самостоятельный модуль (`Modules/Population`, сборка `Bogatyri.Population`); убрано ограничение «40% всех людей» — работать может любой житель, первая расстановка — 120 рабочих; резерв по уровню Ратуши 10/20/30 (ТЕСТ, `civilianReserveByTownHallLevel` вместо `civilianReserve`); при голоде добыча кроме еды ×0.5 (ТЕСТ). Тесты. |
| 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`, тесты. |