# I_Persistable — Blueprint Interface

**File:** [`Content/Framework/Save/I_Persistable`](Content/Framework/Save/I_Persistable.uasset)

> **⚡ C++ Status: Full Implementation** — Defined in `Source/PG_Framework/Public/Core/I_InterfaceLibrary.h` as `BlueprintNativeEvent`. **Do NOT create a Blueprint Interface asset.** On any actor that needs save/load: Class Settings → Interfaces → Add → `UPersistable`. Override `On Save`, `On Load`, `Get Save Tag`, `Needs Save`. See `docs/developer/cpp-integration-guide.md`.
>
> ---

**Purpose:** Interface for any Actor or Component in the world that needs to save or restore per-instance state. Implemented by doors, containers, lootable objects, hidden collectibles, puzzle elements, destructibles, and any other persistent world actor.

**Depends On:** None
**Used By:** [`SS_SaveManager`](35_SS_SaveManager.md), All persistent world actors

---

## Interface Functions

| Function | Inputs | Outputs | Description |
|----------|--------|---------|-------------|
| `CollectState` | — | `S_WorldObjectState` | The implementer packs its current state into a generic key-value struct |
| `RestoreState` | StateData: `S_WorldObjectState` | — | The implementer unpacks the struct and reapplies its state |
| `GetPersistenceTag` | — | GameplayTag | Returns the unique gameplay tag that identifies this instance in the save manifest |
| `ShouldPersist` | — | Bool | Returns whether this actor should be serialised (e.g., false for temporary actors spawned via FX) |
| `OnPreSave` | — | — | Called before the save operation begins; used to flush transient state |
| `OnPostLoad` | — | — | Called after all state has been distributed; used to trigger animations, re-enable physics, etc. |

---

## Required Variable (on implementer)

| Name | Type | Description |
|------|------|-------------|
| `PersistenceTag` | GameplayTag | Set in Construction Script or Defaults. Must be unique within the level. Convention: `Save.<LevelName>.<ActorClassName>.<Index>` |

---

## Implementation Pattern

```mermaid
flowchart LR
    A[SS_SaveManager.CollectWorldState] --> B[Iterate registered I_Persistable actors]
    B --> C[Call actor.ShouldPersist]
    C --> D{Should persist?}
    D -->|No| E[Skip]
    D -->|Yes| F[Call actor.OnPreSave]
    F --> G[Call actor.CollectState]
    G --> H[Store in WorldStateDelta map]
    H --> I[Save to disk]

    J[SS_SaveManager.DistributeWorldState] --> K[Iterate WorldStateDelta entries]
    K --> L[Find actor by ObjectTag]
    L --> M{Found?}
    M -->|No| N[Log warning]
    M -->|Yes| O[Call actor.RestoreState]
    O --> P[Call actor.OnPostLoad]
    P --> Q[Continue]
```

---

## Example: BP_DoorWithState implements I_Persistable

```cpp
// On CollectState
State.bDestroyed = bIsDestroyed;
State.CustomData.Add("bOpen", bIsOpen);
State.CustomData.Add("bLocked", bIsLocked);
State.CustomData.Add("Health", Health);
State.CustomData.Add("PuzzleSolved", bPuzzleSolved);
return State;

// On RestoreState(State)
bIsDestroyed = State.bDestroyed;
bIsOpen = State.CustomData["bOpen"].AsBool();
bIsLocked = State.CustomData["bLocked"].AsBool();
Health = State.CustomData["Health"].AsFloat();
bPuzzleSolved = State.CustomData["PuzzleSolved"].AsBool();
```

---

## PersistenceTag Convention

| Tag Pattern | Example | Used For |
|-------------|---------|----------|
| `Save.LevelName.ActorClass.Index` | `Save.Hospital.DoorHeavy.03` | Level-dense unique ID |
| `Save.Persistent.ActorName` | `Save.Persistent.MansionKey` | Unique collectibles that span levels |
| `Save.Global.FlagName` | `Save.Global.EndingUnlocked` | Cross-save narrative flags |

---

## Communication Matrix

| Target System | Method | Why |
|---------------|--------|------|
| `SS_SaveManager` | Interface call | State collection and distribution |
| `SS_SaveManager` | Register/Unregister | Lifecycle tracking (BeginPlay/EndPlay) |

---

## Reuse Notes

- Keep `CustomData` entries simple: only Bool, Float, Int, String, or Name values
- For complex state (arrays of structs), serialise to a Json string and store as a single `CustomData` entry
- Use `OnPostLoad` to re-trigger timelines or timelines that drive animations (e.g., door opening animation after restoring closed state)
- Implement `ShouldPersist` to return `false` for actors that are editor-placed but runtime-destroyed without state significance (e.g., particle emitters)
- All interface functions are BlueprintImplementableEvent — no logic required in the interface itself