Модуль Армии (Bogatyri.Army, без Unity): - ArmyService — единственное хранилище войск (4 типа), секция сохранения "army" - IArmy расширен: войска по типам, обучение (AddTroops), потери (LoseFraction), войска в походах - событие TroopsChangedEvent — по нему перерисовываются панели HUD - GameProgress и панели работают с войсками только через IArmy - перенос из progress.json (troops, sashaMarchUnits) при первой загрузке; старые данные удаляются только после записи save.json (флаг armyInModule) - ArmyBridge удалён: контракты и перенос сохранений проверены тестами - тесты: поход не создаёт и не теряет солдат, потери по типам, сохранение, перенос, сброс Окно «ВОЙСКО ВЕРНУЛОСЬ» в городе (UI/MarchReturnPopup): - куда ходили, победа/поражение, сколько вернулось из скольких, добыча - показывается и за походы, закончившиеся пока игра была закрыта; несколько — по очереди - в MarchReturnedEvent добавлены Sent, TargetName, LootText GameLoadedEvent публикуется и без файла сохранения. Документы Army, Core, Save, World, README обновлены. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
108 lines
6.8 KiB
Markdown
108 lines
6.8 KiB
Markdown
# Сохранения
|
||
|
||
Игровое состояние: запись и загрузка `save.json`. Модуль не знает, что хранят другие модули: каждый модуль сам отдаёт свою секцию.
|
||
|
||
- **Сборка:** `Bogatyri.Save`.
|
||
- **Папка:** `Assets/Scripts/Modules/Save`.
|
||
- **Зависит от:** ядра.
|
||
|
||
## Контракт `ISaveSystem`
|
||
|
||
| Член | Что делает |
|
||
| --- | --- |
|
||
| `HasSave` | Есть ли файл сохранения. |
|
||
| `SaveNow()` | Сохранить сейчас. |
|
||
| `DeleteSave()` | Только стереть файл (состояние модулей в памяти не меняется). |
|
||
| `ResetAll()` | **Сброс сохранений**: все модули возвращаются к новой игре, файл сразу перезаписывается. |
|
||
|
||
Для слоя приложения остаются `SaveManager.Instance.SaveGame()`, `LoadGame()`, `HasSaveFile()`, `DeleteSave()`.
|
||
|
||
## Как модуль подключает свои данные
|
||
|
||
```csharp
|
||
public class MyModule : ISaveParticipant
|
||
{
|
||
public string SaveKey => "mymodule";
|
||
public string Capture() => JsonUtility.ToJson(data); // null -- оставить то, что уже в файле
|
||
public void Restore(string json) { data = JsonUtility.FromJson<MyData>(json); }
|
||
public void ResetState() { data = new MyData(); } // новая игра
|
||
}
|
||
SaveRegistry.Register(myModule); // при запуске модуля
|
||
```
|
||
|
||
| Секция | Модуль |
|
||
| --- | --- |
|
||
| `economy` | Экономика |
|
||
| `settlement` | Поселение |
|
||
| `world` | Глобальный мир |
|
||
| `circle` | Круг (простые строки, не JSON — модуль без Unity) |
|
||
| `army` | Армия (простые строки: `troops=…`, `march=…`) |
|
||
|
||
## Формат файла
|
||
|
||
Версия 2: `{ "version": 2, "sections": [ { "key": "economy", "json": "..." } ] }`.
|
||
|
||
Версия 1 (старый плоский `save.json`) читается сама: файл целиком отдаётся Экономике и Поселению, их секции берут поля по старым именам.
|
||
|
||
## Когда сохраняется
|
||
|
||
- **Автоматически:**
|
||
- автосохранение каждые 30 секунд;
|
||
- при сворачивании и закрытии игры;
|
||
- перед переходом между сценами (`SceneNavigator`);
|
||
- после событий походов.
|
||
- **Защиты:**
|
||
- **пока сохранение не загружено, запись пропускается** — иначе стартовые нули модулей затёрли бы файл;
|
||
- секция модуля, которого сейчас нет (например, город на карте мира), не пропадает.
|
||
|
||
## Сброс сохранений
|
||
|
||
| Откуда | Что происходит |
|
||
| --- | --- |
|
||
| Настройки → «Сбросить весь прогресс» | `GameProgress.ResetAll()`: удаляет `progress.json`, вызывает `ISaveSystem.ResetAll()`, создаёт новый прогресс (герой, задания, почта, войска). |
|
||
| `ISaveSystem.ResetAll()` | Стирает `save.json`, вызывает `ResetState()` у каждого модуля, сразу записывает новую игру, публикует `GameResetEvent`. |
|
||
| Tools → Сбросить сохранения (редактор) | В Play Mode — то же, что кнопка в Настройках. Вне Play Mode — удаляет `save.json` и `progress.json`. |
|
||
| Tools → Открыть папку сохранений | Открывает `Application.persistentDataPath`. |
|
||
|
||
Что сбрасывает каждый модуль:
|
||
|
||
- **Экономика** — стартовые ресурсы из Inspector `ResourceManager`;
|
||
- **Поселение** — Ратуша 1 уровня, постройки убраны, участки свободны;
|
||
- **Мир** — все походы отменены;
|
||
- **Круг** — игрок ни в каком Круге, его Круги распущены;
|
||
- **Армия** — войск и походов нет.
|
||
|
||
Новый модуль с данными обязан реализовать `ResetState()`, иначе он не скомпилируется: метод входит в контракт.
|
||
|
||
## События
|
||
|
||
**Публикует:**
|
||
|
||
- `GameLoadedEvent` — после загрузки (и когда файла сохранения нет — загрузка завершена, новая игра);
|
||
- `GameSavedEvent` — после записи;
|
||
- `GameResetEvent` — после сброса сохранений.
|
||
|
||
**Слушает:** ничего.
|
||
|
||
## Файлы
|
||
|
||
| Файл | Что это |
|
||
| --- | --- |
|
||
| `SaveManager.cs` | Запись и чтение секций, автосохранение, перенос версии 1. |
|
||
| `GameBootstrap.cs` | Загружает сохранение при старте сцены города. |
|
||
| `Assets/Editor/SaveResetMenu.cs` (редактор) | Меню Tools → Сбросить сохранения / Открыть папку сохранений. |
|
||
|
||
## Решения и план
|
||
|
||
- **Этап 0:** одно сохранение. `GameProgress` (`progress.json`: герой, задания, почта, магазин, арена) пока пишет свой файл. Его данные переводятся в секции по мере выноса в модули. Войска уже перенесены — секция `army`.
|
||
- Номер версии файла поднимать при каждом несовместимом изменении и дописывать перенос старой версии здесь.
|
||
|
||
## Журнал изменений
|
||
|
||
| Дата | Кто | Что |
|
||
| --- | --- | --- |
|
||
| 23.10.2026 | Claude | Новая секция `army` (модуль Армии). `GameLoadedEvent` публикуется и без файла сохранения. |
|
||
| 19.10.2026 | Claude | Новая секция `circle` (модуль Круга). |
|
||
| 19.10.2026 | Claude | Сброс сохранений: `ISaveSystem.ResetAll()`, `ResetState()` у модулей, `GameResetEvent`, меню редактора. Кнопка в Настройках теперь сбрасывает и походы, и память Поселения. |
|
||
| 19.10.2026 | Claude | `SaveManager` переписан на секции модулей (`ISaveParticipant`), формат v2 с переносом v1. Запись до загрузки запрещена. Сохранение города вынесено в модуль Поселения, походов — в модуль Мира. |
|