-`Bogatyri.World.Simulation` — карта и походы, чистый C# без Unity (переедет на сервер);
-`Bogatyri.World` — `MarchManager`, Unity-часть.
- **Папка:** `Assets/Scripts/Modules/World`.
- **Зависит от:** ядра. Войска — через `IArmy`, добыча — через `IEconomy`, бой — через `IBattleSystem`.
## Контракт `IWorld`
| Член | Что делает |
| --- | --- |
| `ActiveMarches` | Сколько походов в пути. |
| `TryStartMarch(targetId, soldiers, out error)` | Отправить войско на цель. |
Получить: `GameServices.Get<IWorld>()`. Интерфейсу карты (`WorldMapController`) можно обращаться к `MarchManager.Instance` и его C#-событиям: там полные данные похода.
У всех событий похода — номер похода и `TimeMs`: когда это случилось по часам игры, а не когда игра это заметила (после офлайна это разные моменты; документ 02).
-`MarchStartedEvent` — войско выступило (`TimeMs` = выступление);
-`MarchBattleResolvedEvent` — бой у цели прошёл (`TimeMs` = приход, `BattleId` = номер боя);
-`MarchReturnedEvent` — войско вернулось, солдаты и добыча зачислены. Поля: `MarchId`, `Victory`, `Survivors`, `Sent`, `TargetEmpty` (лагерь уже был разбит, боя не было), `TargetName`, `LootText`.
Секция `"world"`: `{ "marches": [ MarchData ], "camps": [ { "id", "respawnMs" } ] }` — походы и разбитые лагеря с моментом их появления. Старые сохранения без `camps` читаются: все лагеря на месте.
Раньше походы лежали в `progress.json` (`sashaMarches`). При первом запуске `App/GameModules` переносит их в модуль Мира и удаляет из `progress.json` только после записи `save.json`.
## Файлы
| Файл | Что это |
| --- | --- |
| `MarchManager.cs` | Ведёт походы, реализует `IWorld` и `ISaveParticipant`. Создаётся сам при запуске игры. |
- у похода есть состав по типам (`MarchData.units`, берётся у Армии при выступлении) и выжившие по типам (`survivorUnits`);
- у цели — состав гарнизона `WorldMapLayout.GarrisonUnits(node)`. Он вычисляется из id цели, а не из генератора карты, поэтому карта осталась прежней. У Змея Горыныча 40% пехоты, 30% лучников, 20% кавалерии, 10% осадных; у соседей пропорции свои у каждого;
- замок и города сражаются за стенами;
- выжившие возвращаются в Армию точно по типам (`IArmy.ReturnUnits`). Старые походы без состава — как раньше, по пропорции;
- окно атаки показывает состав гарнизона и ожидаемый исход: силу сторон, «скорее победа / поражение» и примерные потери. Расчёт тот же, что сделает модуль Боя.
- 12 лагерей, по 4 каждого уровня. Ставятся отдельным генератором после городов (`seed ^ CampSeedSalt`), поэтому замок, твой город и соседи остались на прежних местах. Лагерь может стоять ближе к городу (2,6 клетки), чем город к городу (3,6).
| Уровень | Название | Гарнизон | Добыча | Нужно пехоты (примерно) |
| --- | --- | --- | --- | --- |
| 1 | Стан разбойников | 6–9 | 15 еды, 15 дерева | 8–12 |
- бой в поле: стен нет (`WorldMapLayout.IsFortified`), у разбойников нет осадных — их доля уходит пехоте;
- после победы лагерь **разбит** на 30 минут (`CampRules.RespawnMs`, отсчёт от момента боя): на карте серый, с таймером, напасть нельзя;
- если к лагерю шли два войска, бьётся первое пришедшее (бои считаются по времени прихода). Второе находит лагерь пустым: боя нет, домой идут все, добычи нет (`MarchData.targetEmpty`, окно «ЛАГЕРЬ ПУСТ»);
- на карте лагерь — фиолетовый круг, подпись «название, ур. N, гарнизон».
**Отправка целиком или никак** (ревью 27.10.2026, п. 2.2): `MarchManager.TryStartMarch` создаёт поход, затем просит Армию забрать воинов. Если Армия отказала, поход откатывается (`WorldSimulation.CancelMarch`) и игрок видит ошибку. Если при возвращении Армия не знает поход, ни воины, ни добыча не зачисляются (предупреждение в консоли).
**Номер боя:** при выступлении поход получает `battleSeed` (из номера похода, времени выступления и цели) и хранит его в сохранении. Тот же поход всегда даёт тот же бой. После боя в походе запоминаются силы сторон и выпавшая случайность (`attackerPower`, `defenderPower`, `attackerLuck`, `defenderLuck`). Окно итога боя на карте показывает «Сила 356 : 160, удача −3,1% / +3,2%» (твоя / врага); текст окна ужимается, чтобы не уходить под кнопку OK. Окно атаки показывает прогноз риска и диапазоны силы и потерь.
**Голод в бою:** перед боем мир спрашивает состояние войска нападающего (`WorldSimulation.AttackerConditions`). `MarchManager` отвечает по модулю Населения: город голодает — войско бьётся на 50% силы. Мир сам голод не считает. Голод берётся на момент прихода войска: `IPopulation.WasStarvingAt(время боя)`. Перед боем, прошедшим пока игра была закрыта, `MarchManager` просит Население досчитать время (`IPopulation.Tick`) — офлайн обрабатывается в причинно-следственном порядке (документ 02). Окно атаки показывает шанс победы (решение №11) и предупреждает: «Голод: войско слабее, сила 50%. Накормите город до боя», прогноз учитывает голод. Если голод начался или кончился, пока окно атаки открыто, предупреждение и прогноз обновляются сами. Окно итога боя пишет «голод: сила 50%». В походе запоминается `attackerConditions` и `attackerConditionPercent`.
**Ресурсные точки (регламент 31.10, раздел 7; решение №46)**
Состояние точки ведёт Мир: запас, появление, истощение, конец экспедиции. Зачисление добычи и складские лимиты — Экономика: груз приходит как добыча похода (`MarchData.loot`) и зачисляется через `IEconomy` по возвращении, как трофеи боя. Числа — таблица `Assets/Resources/WorldResourceConfig.asset` (ТЕСТ).
| Ресурс | Запас: малая / средняя / крупная | Добыча воина за игровой час | Груз на воина |
- **Срок жизни** — 12 / 24 / 48 реальных часов с момента появления. 1 игровой час = 5 реальных минут.
- **Сбор:** дошли до точки → войско стоит и собирает (`MarchState.Gathering`). Сбор кончается, когда груз полон, запас истощён или точка исчезла — что раньше; затем физический возврат с грузом. Всё известно в момент прихода, поэтому работает и при закрытой игре. Пример: 10 пехотинцев на Лес — 120 дерева за 30 реальных минут.
- **Осадные машины груза не несут** (регламент); добывают наравне с остальными.
- **Одна точка — одно войско.** Пока к точке идёт или на ней стоит войско, отправить другое нельзя.
- **Точка исчезла до прихода** — возврат без добычи (окно «ТОЧКИ НЕТ»). Исчезла во время сбора — уносят, что успели.
- **Истощённая или исчезнувшая точка** заменяется новой того же ресурса в другом месте (место и размер — из seed и номера появления, одинаково на сервере). Убирается с карты, когда к ней уже никто не идёт.
- **На карте** — ромб цвета ресурса с подписью «запас, сколько осталось жить» или «идёт сбор». Окно отправки показывает запас, добычу и груз на воина, путь и прогноз «унесут N за T, весь поход ~X».
- **Открыто (не задано регламентом, ТЕСТ):** сколько точек на карте (сейчас по 2 каждого ресурса), как выбирается размер (сейчас равновероятно), предел добычи на точку («скорость с пределом на точку»). Нет точек еды — в регламенте их нет.
- **Позже:** нападение на занятую точку (бой в поле, победитель забирает до 50% накопленного) — когда появятся другие игроки; досрочный отзыв — технология «Связь Волхвов»; переполнение склада при возвращении (5 минут на разгрузку) — вместе с Экономикой.
**Порядок событий** (документ 02: офлайн — в причинно-следственном порядке): `WorldSimulation.Advance` каждый раз берёт самое раннее событие — приход к цели (бой или начало сбора), конец сбора, исчезновение ресурсной точки или возвращение. Бой назначает время возвращения, поэтому за один вызов поход может и сразиться, и вернуться. При равном времени — бой раньше возвращения, затем по номеру похода. Раньше за один вызов сначала шли все бои, потом все возвращения — войско, вернувшееся раньше чужого боя, попадало в события после него. Повторный вызов с тем же временем ничего не создаёт заново.
Все таймеры мира — в миллисекундах часов `GameClock` (реальное время + перемотка DEV). Регламент 31.10: 1 игровой час = 5 реальных минут, но **физический марш идёт в реальном времени** — поэтому таймеры мира остаются реальными.
**Скорость марша** (регламент 31.10, раздел 7): у каждого типа своя скорость, таблица `Assets/Resources/MarchConfig.asset` (клеток карты за реальный час; 1 клетка = 1000 единиц координат). Смешанное войско идёт **со скоростью самого медленного типа** — одна осадная машина тормозит всех (`mixedUsesSlowest`; выключить — средняя скорость по числу воинов). Правило для одной армии регламент не задаёт — **открытый вопрос команде**. Время прихода пересчитывается, когда Армия забрала воинов (`WorldSimulation.AssignUnits`); обратно идут выжившие — со скоростью своего состава (погибли осадные — войско возвращается быстрее). Окно атаки показывает путь по составу при выбранном числе воинов. Бонусы героя (+15%) и технологии (+20%) — когда появятся герои и технологии.
**Слоты похода**: база 2 (`MarchConfig.baseMarchSlots`, ТЕСТ), «Военная логистика» поднимет до 7 — уровни и цены не утверждены. Слоты ведёт модуль Армии (`IArmy.MarchSlots`, `ActiveMarches`). При занятых слотах окно атаки пишет «Все слоты похода заняты», кнопка «В поход» неактивна; верхняя строка карты — «отрядов: 1 из 2».
- **Этап 0, решено 19.10.2026:** осталась одна карта мира — эта. Маленькая карта (панель «МИР», `Meta/World`) удалена. Перед удалением проверены ссылки в коде, сценах и префабах. Перенос сохранений: солдаты из её походов (`marches` в `progress.json`) возвращаются в город при загрузке — отправленные, а если отряд уже шёл назад, то выжившие. Добыча таких походов не переносится.
- **Лагеря:** числа (гарнизон, добыча, 30 минут до появления) — предложение, требуют балансировки. Позже их можно перенести в `EconomyConfig`. В мультиплеере разбитый лагерь — общий для всех игроков, его состояние будет вести сервер.
| 31.10.2026 | Claude | Ресурсные точки (регламент 31.10, раздел 7): `ResourcePoint`, `ResourcePointRules`, `WorldSimulation.Points.cs` — появление, сбор (`MarchState.Gathering`), истощение, исчезновение, замена новой точкой; события `GatherStarted`, `GatherFinished`; сохранение в секции `world`; таблица `WorldResourceConfig`; на карте — ромбы точек, окно отправки с прогнозом сбора, подпись отряда «сбор». Тесты: появление, сбор и груз, истощение и новая точка, осадные без груза, точка исчезла до прихода и во время сбора, одна точка — одно войско, сохранение посреди сбора. |
| 31.10.2026 | Claude | Скорости маршей по типам (регламент 31.10: 8 / 9 / 12 / 4 клетки за реальный час, таблица `MarchConfig`), войско — по самому медленному типу, обратно — по составу выживших; `WorldSimulation.TravelTimeMs`, `AssignUnits`, `MarchSpeedRules`; слоты похода (база 2) в окне атаки и верхней строке. Тесты: скорости по типам, смешанное войско, обратный путь; старые тесты переведены на часовые марши. |
| 30.10.2026 | Claude | События строго по времени после офлайна (раньше — сначала все бои, потом все возвращения); `TimeMs` у событий похода и `WorldEvent`, `BattleId` у события боя; таблица таймеров с единицами. Тесты: порядок после офлайна, повторный вызов без дублей, границы таймеров, 0 и 1 солдат. |
| 30.10.2026 | Claude | Голод на момент боя — по истории голода Населения (`WasStarvingAt`), перед офлайн-боем Население досчитывает время. Окно атаки: шанс победы в процентах. |
| 28.10.2026 | Claude | Голод в момент боя ослабляет войско игрока (−50%): `WorldSimulation.AttackerConditions`, `MarchManager.PlayerConditions`; предупреждение в окне атаки, строка в итоге боя. Тест: голод в момент боя — поражение. |
| 28.10.2026 | Claude | Номер боя у похода (`battleSeed`), силы и удача сторон в итоге; прогноз риска в окне атаки, удача в окне итога боя. Тест: тот же поход — тот же бой. |
| 28.10.2026 | Claude | По ревью 27.10.2026: отправка с проверкой и откатом (`CancelMarch`), возвращение по неизвестному походу ничего не зачисляет. Тест отмены похода. |
| 27.10.2026 | Claude | Лагеря разбойников (PvE): 12 лагерей трёх уровней, бой в поле, после победы лагерь разбит на 30 минут, второе войско находит его пустым. Секция `world` хранит разбитые лагеря, в `MarchReturnedEvent` добавлен `TargetEmpty`. Тесты: расстановка лагерей, разбит и появляется снова, сохранение. |
| 27.10.2026 | Claude | Бой с типами войск: состав похода и гарнизона, стены у замка и городов, выжившие по типам, ожидаемый исход в окне атаки. Время походов — по `GameClock` (перемотка в DEV проматывает походы). |
| 19.10.2026 | Claude | Маленькая карта мира (`Meta/World`: WorldSystem, WorldMapPanel, WorldData, WorldLine) удалена, окно `Panel_World` убрано из сцены города. Кнопка «МИР» открывает эту карту. Солдаты из походов старой карты возвращаются в город. |
| 19.10.2026 | Claude | `ResetState()`: при сбросе сохранений походы отменяются. |
| 19.10.2026 | Claude | Модуль выделен. `MarchManager` больше не обращается к `GameProgress` и `ResourceManager`: войска через `IArmy`, добыча через `IEconomy`, сохранение — своя секция `"world"`, статистика — через события. Формула боя вынесена в модуль Боя. |