# Conflicts: # Docs/Modules/Core.md # Docs/Modules/Population.md # Docs/Modules/README.md # Docs/Modules/Settlement.md
22 KiB
Население
Люди города: жители, рабочие, прирост, еда, голод, найм в войско и демографические ограничения. Самостоятельный модуль (регламент 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. Если часы ушли назад, ничего не считается, а метка последнего расчёта назад не уходит: когда часы снова дойдут до уже посчитанного, второй раз оно не начислится (решения 04.11). В начале каждого шага и в концеTickмодуль публикуетGameTimeSimulatedEvent— до какого момента дошёл расчёт; по нему стройка, закончившаяся офлайн, вступает в силу ровно в свой момент (App/ConstructionClock). - Еда списывается целыми единицами, доли копятся в модуле: склад хранит
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 еды. Решает Дима.
- п. 7, история голода -- сделано (
- Этап 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 (Валдемар) | Решения 04.11 (офлайн по временным меткам): событие GameTimeSimulatedEvent на каждом шаге расчёта и в конце Tick — стройка, закончившаяся офлайн, вступает в силу в свой момент, добыча с момента завершения. Часы назад: метка последнего расчёта не уходит назад — закрыто повторное начисление. Тесты в ProductionTests (офлайн-стройка, повторный Tick, загрузка), обновлён тест про часы назад. Побочный эффект: после «DEV +10 часов» и перезапуска расчёт стоит, пока реальное время не догонит перемотку. |
| 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, тесты. |