# I_Persistable — Blueprint Interface **File:** [`Content/Framework/Save/I_Persistable`](Content/Framework/Save/I_Persistable.uasset) **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...` | --- ## 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