Войска и их характеристики: сколько воинов каждого типа в городе, сколько из них в гарнизоне, сколько в походах, обучение, потери, уход в поход и возвращение. **Единственное место, где хранятся войска.**
| `MarchSlots`, `ActiveMarches` | Слоты похода (регламент 31.10): сколько походов может идти одновременно (база 2 из `MarchConfig`, «Военная логистика» — до 7, позже) и сколько идёт сейчас. `TakeForMarch` отказывает, если все слоты заняты. Походы из старых сохранений сверх лимита дойдут, новых не будет. |
| `AvailableForMarch` | Свободные воины: в городе минус гарнизон. Только их можно отправить в поход. |
| `SetGarrison(count)` | Назначить гарнизон: набирается по составу армии в городе (30 пехоты и 10 лучников, гарнизон 8 → 6 пехоты и 2 лучника). Не больше, чем воинов в городе; возвращает, сколько назначено. |
| `TakeForMarch(marchId, count)` | Забрать свободных воинов (без гарнизона) в поход пропорционально составу и запомнить состав. `false` — ничего не взято: не хватает воинов или поход с таким номером уже есть. |
| `ReturnFromMarch(marchId, survivors)` | Вернуть выживших по тем же типам (пропорционально). `false` — поход неизвестен, никто не добавлен. |
| `ReturnUnits(marchId, survivorUnits)` | Вернуть выживших точно по типам, как посчитал бой. Больше, чем ушло, не вернётся. `false` — поход неизвестен. |
| `WoundedTotal`, `GetWoundedWaiting(type)`, `Healing`, `HospitalBeds` | Раненые всего; ждут места по типам; группы на лечении (`HealingInfo`); мест в госпитале сейчас. |
| `ReturnUnits(marchId, survivorUnits, woundedUnits, nowMs)` | Выжившие — в город, раненые — в госпиталь в момент `nowMs`. Только известный поход, один раз. |
| `ProcessHealing(nowMs)` | Закончить лечение по сроку (и офлайн), положить ждущих на освободившиеся места; `SoldiersHealedEvent` — один раз на группу. |
| `Training` | Очередь обучения: заказы (`TrainingOrderInfo`: номер, тип, сколько, начало и конец в мс часов игры). |
| `CanQueueTraining(type, count, queueSize, out error)` | Можно ли поставить заказ: очередь не заполнена, жителей хватает (Население). Ничего не меняет. |
| `TryQueueTraining(type, count, durationMs, queueSize, nowMs, out error)` | Поставить заказ в конец очереди; люди уходят из жителей в Казармы (`IPopulation.TryRecruit`). `false` — ничего не изменилось. |
| `ProcessTraining(nowMs)` | Закончить заказы, срок которых наступил (и офлайн): воины переходят в Армию через `IPopulation.CompleteTraining`, каждый заказ — один раз. |
| `FinishAllTraining(nowMs)` | Ускорение: все заказы готовы сейчас. |
Получить: `GameServices.Get<IArmy>()`. Регистрирует `App/GameModules`. `GameProgress` (обучение, кампания, арена, сила армии) и панели работают с войсками только через этот контракт.
Решения по 8 вопросам Саши от 31.10, п. 7, регламент 31.10. **Раненых хранит и лечит Армия.**
| Кто | Что делает |
| --- | --- |
| Бой | Делит выбывших на погибших и раненых (Battle.md). |
| Мир | Раненые идут домой вместе с войском (`MarchData.woundedUnits`). |
| Армия | Принимает раненых при возвращении, кладёт в госпиталь, лечит, возвращает в город. Сохраняет. |
| Население | Считает раненых людьми: `Soldiers` = в городе + в походах + раненые. Погибшие уходят из людей, вылеченные — не новые люди. |
| Поселение | Здание госпиталя: уровень и места (`IHospital.Beds`, регламент — 20…60 мест на уровнях I–V). **Здания пока нет** — Армия берёт временные места из таблицы. |
| Экономика | Цена лечения — `IEconomy.TrySpend` целиком; **цена не утверждена**, в таблице 0. |
- Таблица `Assets/Resources/HospitalConfig.asset` (ТЕСТ): `temporaryBeds` 20 (пока нет здания), `healGameHours` 4 / 4 / 6 / 8 игровых часа (пехота, лучники, кавалерия, осадные; 1 игровой час = 5 реальных минут), цена за воина `healFoodPerSoldier`, `healGoldPerSoldier` — 0.
- Раненые ложатся на места по типам по порядку; каждая группа лечится своё время и возвращается в город целиком.
- **Мест не хватает — лишние ждут** (ТЕСТ, решение Саши 01.11; регламент не задаёт). Место освободилось — ждущие ложатся в тот же момент, в том числе пока игра закрыта.
- Не хватает ресурсов на лечение — группа ждёт, ничего не списано.
- Повторное возвращение похода или повторный расчёт никого не создают и не лечат дважды.
- Окно «Войска»: раненые (лечатся / ждут места), мест в госпитале, когда вылечатся ближайшие. Окно возвращения и итог боя на карте показывают, сколько ранено.
- **Дальше:** здание госпиталя в городе (уровни I–V, окно с очередью) — регистрирует `IHospital`; герои — отдельная очередь (регламент), когда появятся герои.
Решения по 8 вопросам Саши от 31.10, п. 8: **единственный владелец очереди — Армия.** Раньше её вёл `GameProgress` (`progress.json`, время по настоящим часам) — перенесено.
| Кто | Что делает |
| --- | --- |
| Армия | Заказы по порядку (новый начинается, когда закончится последний), сроки, завершение — один раз на заказ (`TroopsTrainedEvent`), сохранение. |
| Население | Свободные жители и резерв (`Recruitable`, `CivilianReserve`), уход людей в Казармы (`TryRecruit`) и переход в воины (`CompleteTraining`). |
| Слой приложения (`GameProgress`) | Есть ли Казармы и открыт ли тип войск, длина очереди (`Barracks.QueueSize`), цена — через Экономику (если заказ не встал — цена возвращается), окна, статистика заданий по `TroopsTrainedEvent`. |
- Время — часы игры (`GameClock.UnixNowMs`): перемотка «DEV +1 час» и офлайн досчитывают обучение так же, как марши. Раньше обучение шло по настоящим часам и перемотки не замечало.
- Повторный расчёт или загрузка не обучают заказ второй раз: он удаляется из очереди в момент завершения.
- Население не нашло новобранцев заказа (испорченное сохранение) — заказ снимается без воинов: люди не появляются из ниоткуда, очередь не встаёт.
- **Перенос старой очереди:** после загрузки `App/GameModules` один раз переносит заказы из `progress.json` (`Data.training`) в Армию с тем же оставшимся временем и признаком «люди взяты». Из `progress.json` они удаляются только после записи `save.json`.
Воин либо в городе, либо в походе. Поход забирает воинов из города и запоминает состав, возвращение раздаёт выживших по тем же типам. Тест `ArmyService_MarchNeverCreatesOrLosesSoldiers` проверяет, что «дома + в походах» всегда равно общему числу.
- **Публикует:** `TroopsChangedEvent` (воинов в городе, из них в гарнизоне, и в походах) — при обучении, потерях, походе, возвращении, загрузке, сбросе. По нему `App/GameModules` перерисовывает панели HUD.
- **Публикует:** `TroopsTrainedEvent` (номер заказа, тип, сколько, когда были готовы) — один раз на заказ. По нему `GameProgress` ведёт статистику заданий и показывает «Обучено».
- **Этап 5 (план разработки):** солдат стоит 1 человека, 15 еды, 5 металла, 5 золота; содержание 0,2 еды в час; погибшие уменьшают население. Делается вместе с модулем «Население».
- **Сделано 27.10.2026:** гарнизон города (`SetGarrison`, `AvailableForMarch`).
- **Дальше по гарнизону:** участие в защите города (когда на него смогут напасть — PvP), еда гарнизона (сейчас все воины едят одинаково, считает модуль Населения), дебаффы при голоде. Выбор состава по типам — если понадобится.
| 01.11.2026 | Claude | Решения 31.10, п. 7: раненые и лечение у Армии — `WoundedTotal`, `Healing`, `HospitalBeds`, `ReturnUnits(…, woundedUnits, nowMs)`, `ProcessHealing`, `SoldiersHealedEvent`; места от `IHospital` (пока нет здания — `HospitalConfig.temporaryBeds`), лишние ждут, цена через Экономику (не утверждена). Сохранение `wounded=`, `heal=`. Тесты: места и сроки, однократность, люди в Населении, сохранение и цена. |
| 01.11.2026 | Claude | Решения 31.10, п. 8: очередь обучения перенесена из `GameProgress` в Армию (`Training`, `CanQueueTraining`, `TryQueueTraining`, `ProcessTraining`, `FinishAllTraining`, `TroopsTrainedEvent`, строки `train=` в сохранении), время — часы игры, перенос старой очереди из `progress.json`. Изменены `GameProgress`, `TroopsPanel`, `Barracks`, `BuildingWindow` (код Вальдемара, с разрешения команды). Тесты: порядок и однократность, лимит и ускорение, люди из Населения и обратно, сохранение и перенос. |
| 31.10.2026 | Claude | Слоты похода: `MarchSlots`, `ActiveMarches`; `ArmyService(baseMarchSlots)`, база 2 из таблицы `MarchConfig`; `TakeForMarch` отказывает при занятых слотах. Тест `ArmyService_MarchSlots`. |
| 28.10.2026 | Claude | По ревью 27.10.2026 (п. 2.1–2.2): `ReturnFromMarch` и `ReturnUnits` возвращают `bool` и зачисляют только по известному походу и только один раз; `TakeForMarch` отклоняет повторный номер похода; дубликаты походов в сохранении пропускаются. Тесты: неизвестный поход, повторное возвращение, повторный номер. |
| 27.10.2026 | Claude | Гарнизон города: `GarrisonTotal`, `GetGarrison`, `AvailableForMarch`, `SetGarrison`; в поход уходят только свободные; строка `garrison=` в сохранении; поле `Garrison` в `TroopsChangedEvent`; карточка «Гарнизон города» в окне «Войска». Тест «один солдат в одном месте» с гарнизоном. |
| 23.10.2026 | Claude | Войска переехали в модуль: `ArmyService` — единственное хранилище войск (секция `"army"`), `IArmy` расширен (типы, обучение, потери, войска в походах), событие `TroopsChangedEvent`. Перенос из `progress.json` с удалением старых данных только после записи `save.json`. `ArmyBridge` удалён — контракты и перенос сохранений проверены тестами. |
| 19.10.2026 | Claude | `ArmyBridge.TroopsInMarches` для панели «Войска». После удаления маленькой карты войска в походах учитываются только здесь (`sashaMarchUnits`). |
| 19.10.2026 | Claude | Модуль создан: `ArmyMath` (правила деления войск, без Unity, тесты на отсутствие двойного учёта). `ArmyBridge` сохранён и теперь реализует `IArmy` через `ArmyMath`; формат `sashaMarchUnits` не изменён. |