# Save/Load System

This document describes the game-mode save/load system in the editScene editor.

---

## Overview

The save/load system persists the entire game state to JSON files. It is
designed for **game mode** (`--game` flag) and is triggered from the pause
menu or startup menu.

### Architecture

```
┌─────────────────────────────────────────────────────────┐
│                    EditorApp                            │
│  saveGame(slotPath, slotName)                           │
│  loadGame(slotPath)                                     │
├─────────────────────────────────────────────────────────┤
│                    SaveLoadSystem                       │
│  (static utility: directory, file I/O, listing)         │
├─────────────────────────────────────────────────────────┤
│                    SaveLoadDialog                       │
│  (ImGui modal: slot selection UI)                      │
├─────────────────────────────────────────────────────────┤
│                    SceneSerializer                      │
│  (serializes/deserializes ECS entities & components)    │
├─────────────────────────────────────────────────────────┤
│  CharacterRegistry  │  ContainerStateRegistry          │
│  ItemStateRegistry  │  LuaSaveLoadApi                  │
└─────────────────────────────────────────────────────────┘
```

### Save File Location

Saves are stored in an OS-dependent user data directory:

| Platform | Path |
|----------|------|
| Linux | `~/.local/share/World2/saves/` (or `$XDG_DATA_HOME/World2/saves/`) |
| Windows | `%APPDATA%/World2/saves/` |
| macOS | `~/Library/Application Support/World2/saves/` |

Each save is a single `.json` file named `save_NNN.json`.

---

## Save File Format (version 2.0)

```json
{
    "version": "2.0",
    "saveGame": {
        "baseScene": "scene.json",
        "timestamp": "2026-05-19 12:34:56",
        "slotName": "My Save",
        "playTime": 3600.0,
        "playerCharacterId": 42
    },
    "characterRegistry": { ... },
    "containerState": { ... },
    "itemState": { ... },
    "runtimeEntities": [ ... ],
    "characterRuntimeData": { ... },
    "luaData": { ... }
}
```

### Top-level fields

| Field | Type | Description |
|-------|------|-------------|
| `version` | string | Format version (`"2.0"`) |
| `saveGame` | object | Metadata (see below) |
| `characterRegistry` | object | Serialized `CharacterRegistry` (stats, skills, needs, levels, XP) |
| `containerState` | object | Serialized `ContainerStateRegistry` (chest/loot contents) |
| `itemState` | object | Serialized `ItemStateRegistry` (world item pickup state) |
| `runtimeEntities` | array | Runtime-spawned entities (dropped items, etc.) |
| `characterRuntimeData` | object | Runtime component overrides per character (inventory, GOAP state, animation state) |
| `luaData` | object | Data collected from Lua save callbacks |

### `saveGame` metadata

| Field | Type | Description |
|-------|------|-------------|
| `baseScene` | string | Path to the base scene file (e.g. `"scene.json"`) |
| `timestamp` | string | ISO-8601 timestamp of save |
| `slotName` | string | User-visible slot name |
| `playTime` | float | Accumulated play time in seconds |
| `playerCharacterId` | number | Registry ID of the player character |

---

## C++ API

### `EditorApp`

```cpp
// Save the current game state to a file.
// slotPath: Full path to the save file.
// slotName: Human-readable name for the save slot.
void EditorApp::saveGame(const std::string &slotPath,
                         const std::string &slotName);

// Load a game state from a file.
// slotPath: Full path to the save file.
void EditorApp::loadGame(const std::string &slotPath);
```

### `SaveLoadSystem` (static utility)

```cpp
// Get the OS-dependent save directory (creates it if missing).
static std::string SaveLoadSystem::getSaveDirectory();

// List all existing save files.
static std::vector<SaveInfo> SaveLoadSystem::listSaves();

// Generate a unique filename for a new save.
static std::string SaveLoadSystem::generateSaveFilename();

// Delete a save file.
static bool SaveLoadSystem::deleteSave(const std::string &path);

// Write a JSON object to a save file.
static bool SaveLoadSystem::writeSaveFile(
    const std::string &path, const nlohmann::json &data);

// Read a JSON object from a save file.
static bool SaveLoadSystem::readSaveFile(
    const std::string &path, nlohmann::json &outData);

// Get current ISO-8601 timestamp.
static std::string SaveLoadSystem::getCurrentTimestamp();
```

### `SaveLoadDialog` (ImGui modal)

```cpp
// Open the save/load dialog.
static void SaveLoadDialog::show(EditorApp *editorApp, Mode mode);

// Render the dialog every frame.
static void SaveLoadDialog::render(EditorApp *editorApp);

// Check if the dialog is open.
static bool SaveLoadDialog::isOpen();

// Close the dialog.
static void SaveLoadDialog::close();
```

### `SceneSerializer` (entity serialization)

```cpp
// Serialize a single entity (with all components) to JSON.
nlohmann::json SceneSerializer::serializeEntity(flecs::entity entity);

// Deserialize all components from JSON onto an existing entity.
void SceneSerializer::deserializeEntityComponents(
    flecs::entity entity,
    const nlohmann::json &json,
    flecs::entity parent,
    EditorUISystem *uiSystem,
    bool createNew);
```

---

## Lua API

### `ecs.register_save_callback(name, function)`

Register a Lua function that will be called during save to collect custom data.

**Parameters:**
- `name` *(string)* — Unique identifier for this callback
- `function` *(function)* — Must return a Lua table (or nil)

**Example:**
```lua
ecs.register_save_callback('my_quest_data', function()
    return {
        current_quest = 'find_the_artifact',
        quest_stage = 3,
        npc_flags = { merchant_helped = true, guard_spoken = false }
    }
end)
```

### `ecs.register_load_callback(name, function)`

Register a Lua function that will be called during load with previously saved data.

**Parameters:**
- `name` *(string)* — Must match the name used in `register_save_callback`
- `function` *(function)* — Receives one argument: the table that was returned by the save callback

**Example:**
```lua
ecs.register_load_callback('my_quest_data', function(data)
    if data then
        current_quest = data.current_quest
        quest_stage = data.quest_stage
        npc_flags = data.npc_flags
    end
end)
```

---

## Events

The save/load system fires events on the `EventBus` at key points:

| Event | When |
|-------|------|
| `save_game_requested` | Before save data is collected |
| `game_saved` | After save file is written successfully |
| `load_game_requested` | Before load begins |
| `game_loaded` | After load completes and game state is restored |

Lua scripts can subscribe to these events:

```lua
ecs.subscribe_event('game_saved', function()
    print('Game was saved!')
end)

ecs.subscribe_event('game_loaded', function()
    print('Game was loaded!')
end)
```

---

## Save Flow

```
User clicks "Save" in pause menu
    │
    ▼
SaveLoadDialog::show() opens modal
    │
    ▼
User enters slot name, clicks Save
    │
    ▼
SaveLoadDialog::doSave()
    │
    ▼
EditorApp::saveGame(slotPath, slotName)
    │
    ├── EventBus: "save_game_requested"
    ├── Sync character positions from SceneNodes
    ├── Identify player character registry ID
    ├── Serialize CharacterRegistry
    ├── Serialize ContainerStateRegistry
    ├── Serialize ItemStateRegistry
    ├── Serialize runtime entities (dropped items, etc.)
    ├── Serialize character runtime component overrides
    ├── Collect Lua save callback data
    ├── Write JSON file via SaveLoadSystem::writeSaveFile()
    └── EventBus: "game_saved"
```

## Load Flow

```
User clicks "Load" in startup/pause menu
    │
    ▼
SaveLoadDialog::show() opens modal
    │
    ▼
User selects a save slot, clicks Load
    │
    ▼
SaveLoadDialog::doLoad()
    │
    ▼
EditorApp::loadGame(slotPath)
    │
    ├── EventBus: "load_game_requested"
    ├── Read JSON file via SaveLoadSystem::readSaveFile()
    ├── Clear current scene
    ├── Load base scene (scene.json)
    ├── Resolve prefab instances
    ├── Restore CharacterRegistry
    ├── Restore ContainerStateRegistry
    ├── Restore ItemStateRegistry
    ├── Destroy all existing character entities
    ├── Spawn persistent characters from registry
    ├── Restore character runtime component overrides
    ├── Restore runtime entities (match by instanceId or name)
    ├── Apply Lua load callback data
    ├── Set game state to Playing
    └── EventBus: "game_loaded"
```

---

## Registries

### CharacterRegistry

Persists character stats, skills, needs, level, XP, class, position, and
rotation. Auto-saves to `character_registry.json` after every mutation.

### ContainerStateRegistry

Persists container slot overrides keyed by `containerId`. Auto-saves to
`container_state.json`. Used by chests, shops, and loot crates.

### ItemStateRegistry

Persists world item state (picked up / disabled) keyed by `instanceId`.
Auto-saves to `item_state.json`.

---

## Runtime Entities

Entities marked with `RuntimeMarkerComponent` are spawned at runtime (not
placed in the editor). These include:

- Dropped items
- Spawned NPCs
- Temporary objects

During save, runtime entities are serialized individually. During load, the
system tries to match them to existing scene entities by `instanceId` (for
items) or by `EntityNameComponent`. If no match is found, a new entity is
created.

---

## Character Runtime Data

Characters have two layers of data:

1. **Registry data** (persisted in `CharacterRegistry`): stats, skills,
   needs, level, class, position.
2. **Runtime component overrides** (persisted in `characterRuntimeData`):
   inventory contents, GOAP blackboard state, GOAP planner/runner state,
   path following state, behavior tree state, animation state.

During save, the runtime overrides are extracted per character. During load,
characters are first spawned from the registry, then the overrides are
applied on top.
