# Save System & Checkpoints — Persistence, Death, Void Space

**Game:** Project Void | **Build Phases:** 15
**Framework Systems:** 35_SS_SaveManager, 36_I_Persistable, 37_BP_Checkpoint, 38_BPC_AltDeathSpaceSystem, 39_BPC_DeathHandlingSystem, 40_BPC_PersistentCorpseSystem, 41_BPC_PersistentWorldStateRecorder, 42_BPC_PlayerRespawnSystem, 43_BPC_RunHistoryTracker

---

## Purpose

Defines the complete save/load, checkpoint, death handling, and void space systems. Every framework save system is demonstrated.

---

## Save Slots & Save Manager

### SS_SaveManager Configuration

```
SS_SaveManager (35) — initialized by GI_HorrorGame on boot

Save Slots:
  ├─ SaveSlot_1 → AutoSave (checkpoint-based)
  ├─ SaveSlot_2 → QuickSave (pause menu quick save)
  ├─ SaveSlot_3 → ManualSave (pause menu manual save)
  └─ (Up to 10 slots configurable via DA_SaveSettings)

Save Structure (per slot):
  ├─ SaveHeader
  │    ├─ SlotName (Text)
  │    ├─ TimeStamp (DateTime)
  │    ├─ PlaytimeSeconds (Float)
  │    ├─ ChapterTag (GameplayTag)
  │    ├─ PlayerTransform (Transform)
  │    ├─ Thumbnail (Texture2D*) — screenshot at save time
  │    └─ VersionNumber (Int32)
  │
  ├─ PlayerState (PS_HorrorPlayerState)
  │    ├─ Health (Float)
  │    ├─ Stamina (Float)
  │    ├─ Stress (Float)
  │    ├─ InventorySlots (Array<FInventorySlot>)
  │    ├─ AmmoPool (Map<GameplayTag, Int32>)
  │    ├─ EquipmentState (Map<GameplayTag, DA_ItemData*>)
  │    ├─ NarrativeFlags (Map<GameplayTag, Boolean>)
  │    ├─ Objectives (Array<ObjectiveState>)
  │    ├─ DeathsThisChapter (Int32)
  │    └─ FearHistory (Array<GameplayTag>)
  │
  └─ WorldState (BPC_PersistentWorldStateRecorder)
       ├─ DoorStates (Array<FDoorState>) — position, locked, barricaded
       ├─ PickupStates (Array<FPickupState>) — collected or not
       ├─ EnemyStates (Array<FEnemyState>) — alive/dead, position
       ├─ PuzzleStates (Array<FPuzzleState>) — solved/unsolved, progress
       └─ GlobalFlags (Map<GameplayTag, Boolean>)
```

### Save Trigger Points

| Trigger | Save Type | When |
|---------|----------|------|
| `BP_Checkpoint` overlap | AutoSave (Slot 1) | Player touches checkpoint |
| `PauseMenu → Save` | ManualSave (Slot 3) | Player manually saves |
| `GM_HorrorGameMode.HandlePlayerDead` | AutoSave (Slot 1) | Death → respawn |
| `TransitionToChapter` | AutoSave (Slot 1) | Level transition |
| `GI_HorrorGame.QuitToDesktop` | QuickSave (Slot 2) | Clean shutdown |
| `SS_SettingsSystem.OnSettingsChanged` | — | Settings saved separately (not slot-based) |

---

## Checkpoint System

### BP_Checkpoint Actors

| Checkpoint | Level | Location | Features |
|-----------|-------|----------|----------|
| `BP_Checkpoint_SafeRoom` | Entry, WardA, WardB, Basement | Safe rooms | Full restore, safe zone, stress decay |
| `BP_Checkpoint_Mirror` | WardA (Nurses Station) | Thematic | Also stores reflection (visual) |
| `BP_Checkpoint_BasementGate` | Basement | Before Shade zone | Warning: "Danger ahead" |
| `BP_Checkpoint_VoidThreshold` | Void Space | One-time save | Disappears after first use |

### Checkpoint Blueprint Flow

```
BP_Checkpoint — Event ActorBeginOverlap (Player)
   │
   ├─ [Already Activated?]
   │    └─ bActivated AND not bReusable? → Return
   │
   ├─ [Activation]
   │    ├─ Set bActivated = true
   │    ├─ Play activation effect:
   │    │    ├─ Light pulse (warm amber glow)
   │    │    ├─ Sound: soft chime
   │    │    └─ Particle: floating dust motes
   │    │
   │    ├─ [Save State]
   │    │    ├─ SS_SaveManager.CreateAutoSave(SlotName = ChapterTag)
   │    │    ├─ BPC_PersistentWorldStateRecorder.SaveWorldState()
   │    │    └─ Store player transform + all state data
   │    │
   │    ├─ [Restore Player]
   │    │    ├─ BPC_HealthSystem.RestoreFullHealth()
   │    │    ├─ BPC_StaminaSystem.RestoreFullStamina()
   │    │    └─ (Stress is NOT restored — checkpoint only saves, doesn't heal stress)
   │    │
   │    ├─ [Notify Player]
   │    │    ├─ WBP_NotificationToast.Show("Checkpoint Reached")
   │    │    ├─ WBP_ScreenEffectController.PulseVignette(0.3s)
   │    │    └─ SS_AudioManager.PlaySFX("checkpoint")
   │    │
   │    └─ [Update Run History]
   │         ├─ BPC_RunHistoryTracker.RecordCheckpoint(ChapterTag)
   │         └─ Set GS_HorrorGameState → SaveGlobalFlag("Checkpoint_Entered", true)
   │
   └─ (Safe Room only) → BPC_StressSystem.StartRapidStressDecay()
        └─ 5× normal decay rate while in safe room
```

---

## Death Handling System

### Death Flow (Complete)

```
BPC_HealthSystem.Health <= 0
   │
   ├─ BPC_DeathHandlingSystem.TriggerDeath(DeathCause, Killer)
   │
   ├─ [Immediate Effects]
   │    ├─ BPC_StateManager.ForcePushState(Death)
   │    ├─ Play death animation montage (GASP)
   │    │    └─ Duration: 2.0 seconds
   │    ├─ BPC_CameraStateLayer.SetDeathCamera()
   │    │    └─ Camera falls to floor, tilts, fades to black
   │    ├─ WBP_ScreenEffectController.PlayDeathFade()
   │    │    └─ Red vignette fades in → black
   │    └─ SS_AudioManager.TriggerDeathAudio(DeathCause)
   │         ├─ SFX: heartbeat flatline
   │         ├─ Music: fade out over 2s
   │         └─ Ambience: dampen to near-silence
   │
   ├─ [Death Classification]
   │    │
   │    ├─ Determine DeathCause from damage info:
   │    │    ├─ "Physical" → Patient/Orderly melee
   │    │    ├─ "Void" → Shade touch, void fall
   │    │    ├─ "Environmental" → Falling debris, trap
   │    │    └─ "Self" → (future: suicide? sanity loss?)
   │    │
   │    ├─ DeathCause == "Void" AND NOT GI_HorrorGame.GetSessionFlag("VoidSpaceVisited")?
   │    │    ├─ True → [VOID DEATH] — redirect to void space (see below)
   │    │    └─ False → [NORMAL DEATH] — respawn at checkpoint
   │    │
   │    └─ [Normal Death → Respawn]
   │         │
   │         ├─ Increment PS_HorrorPlayerState.DeathsThisChapter
   │         ├─ Increment GI_HorrorGame.TotalDeaths
   │         ├─ BPC_RunHistoryTracker.LogDeath(DeathCause, Location, Time)
   │         │
   │         ├─ BPC_PersistentCorpseSystem.LeaveCorpse(Location, DeathCause)
   │         │    └─ Spawns ragdoll corpse at death location
   │         │         └─ Persists until level transition or save clear
   │         │
   │         ├─ [Lose Items?]
   │         │    ├─ Check DA_GameSettings.bLoseItemsOnDeath
   │         │    └─ If true: remove random 1-3 non-key items from inventory
   │         │
   │         ├─ [Show Death Screen]
   │         │    └─ WBP_DeathScreen.ShowDeathScreen(DeathCause, bVoidQualified=false)
   │         │         └─ Player clicks "Continue" → respawn
   │         │
   │         ├─ [Respawn]
   │         │    ├─ SS_SaveManager.LoadSaveSlot(SaveSlot_1) // auto-save
   │         │    ├─ BPC_PlayerRespawnSystem.RespawnAtCheckpoint()
   │         │    │    ├─ Load last checkpoint location
   │         │    │    ├─ Restore health (checkpoint value, not full)
   │         │    │    ├─ Restore inventory (minus death losses)
   │         │    │    └─ Restore world state (doors, items, enemies)
   │         │    │
   │         │    ├─ WBP_DeathScreen.HideDeathScreen()
   │         │    ├─ WBP_ScreenEffectController.PlayRespawnFadeIn()
   │         │    └─ BPC_StateManager.RestorePreviousState()
   │         │
   │         └─ BPC_NarrativeStateSystem.SetFlag("PlayerDied")
```

---

## Void Space / Alt Death Space

### Entry Conditions

```
Player dies with DeathCause == "Void" (killed by Shade)
   OR
Player interacts with void portal in Basement
   AND
GI_HorrorGame.GetSessionFlag("VoidSpaceVisited") == false (first time only)

→ BPC_AltDeathSpaceSystem.EnterVoidSpace()
```

### Void Space Flow

```
EnterVoidSpace()
   │
   ├─ BPC_StateManager.ForcePushState(AltDeathSpace)
   ├─ GM_HorrorGameMode → Set bVoidSpaceActive = true
   ├─ GS_HorrorGameState → SetVoidActive(true)
   │
   ├─ [Transition]
   │    ├─ SS_SaveManager.QuickSave("PreVoid") // save real-world state
   │    ├─ GM_HorrorGameMode.TransitionToChapter(Chapter.VoidSpace)
   │    └─ Level loads: L_Asylum_VoidSpace
   │
   ├─ [Void Space Gameplay]
   │    ├─ Environment: inverted lighting, non-Euclidean geometry
   │    ├─ No map/HUD compass
   │    ├─ Objective: "Find the light. Escape the void."
   │    │
   │    ├─ Puzzle: Activate 3 Memory Shrines
   │    │    ├─ Shrine 1: Reflection shows death moment
   │    │    ├─ Shrine 2: Voice whispers memory fragment
   │    │    └─ Shrine 3: Touch the light
   │    │
   │    ├─ Threat: Shade pursues (same entity, void-empowered)
   │    │    └─ Contact = restart at Shrine 1
   │    │
   │    └─ Timer: 5 minutes (Trial Scenario System)
   │         └─ If time expires: Shade becomes unavoidable → restart
   │
   └─ [Exit Void Space]
        │
        ├─ Player reaches exit portal (all 3 shrines activated)
        │
        ├─ BPC_AltDeathSpaceSystem.ExitVoidSpace()
        │
        ├─ [Return to Real World]
        │    ├─ SS_SaveManager.LoadSaveSlot("PreVoid") // restore pre-void state
        │    ├─ TransitionToChapter(OriginalChapter)
        │    ├─ Spawn player at original death location (or nearest safe spot)
        │    │
        │    ├─ [Void-Touched Benefits]
        │    │    ├─ GI_HorrorGame.SetSessionFlag("VoidSpaceVisited", true)
        │    │    ├─ Player gains passive: +10% damage vs enemies
        │    │    ├─ Player can see hidden void objects in real world
        │    │    └─ New dialogue option: "I've touched the void"
        │    │
        │    ├─ Health/Inventory restored to pre-death state
        │    └─ WBP_NotificationToast.Show("You are now Void-Touched")
        │
        └─ Achievement: "Touched the Void" unlocked
```

---

## I_Persistable Implementation

Every actor that needs to save/restore its state implements `I_Persistable`:

### Persistent Actor Types

| Actor | Saved Data | Restored On |
|-------|-----------|------------|
| `BP_Door_*` | bIsOpen, bIsLocked, bIsBarricaded, DoorRotation | Level load, death respawn |
| `BP_Pickup_*` | bIsCollected (if true → destroy on load) | Level load, death respawn |
| `BP_Enemy_*` | bIsDead, CorpseLocation, CorpseRotation | Level load, death respawn |
| `BP_Checkpoint_*` | bActivated, ActivationCount | Level load |
| `BP_PuzzleDeviceActor_*` | bIsSolved, CurrentStep, PuzzleStateData | Level load, death respawn |
| `BP_ContainerInventory` (chests/desks) | IsOpen, LootedItems, ContainerState | Level load, death respawn |
| `BP_NPC_Survivor` | Location, bIsFollowing, bIsAlive | Level load |

### I_Persistable Interface Functions

```
SaveState() → FPersistentData
   ├─ Actor serializes its current state into a struct
   └─ Returns FPersistentData (includes ActorClass, ActorName, StatePayload)

LoadState(FPersistentData)
   ├─ Actor restores its state from saved data
   └─ Returns Boolean (success/fail)

GetPersistenceID() → FName
   └─ Returns unique ID for this actor instance (level name + actor name)
```

---

## Run History Tracker

### BPC_RunHistoryTracker Data

```
Current Run Stats:
  ├─ TotalDeaths (Int32)
  ├─ DeathLocations (Array<FDeathRecord>)
  │    └─ { Location, Cause, Chapter, Timestamp }
  ├─ CheckpointsActivated (Array<GameplayTag>)
  ├─ TimePerChapter (Map<GameplayTag, Float>)
  ├─ ItemsFound (Int32 / TotalItems)
  ├─ EnemiesKilled (Int32)
  ├─ ScaresExperienced (Int32)
  └─ PlaystyleTag (GameplayTag)

Displayed on:
  ├─ WBP_DeathScreen → Deaths + Chapter + Time
  ├─ WBP_GamePauseMenu → Chapter + Deaths + Time
  └─ WBP_DeathScreen (post-credits) → Full summary
```

---

## Save/Load UI Flow

### Save Game Menu

```
SS_UIManager.PushMenu(SaveLoadMenu)
   │
   ├─ [Display Save Slots]
   │    └─ For each slot in SS_SaveManager.GetAllSlots():
   │         ├─ Show slot thumbnail, chapter, playtime, timestamp
   │         ├─ Empty slot → "Empty — [Create New Save]"
   │         └─ Occupied slot → [Load] [Delete (hold to confirm)]
   │
   ├─ [Load Game]
   │    ├─ Selected slot → SS_SaveManager.LoadSaveSlot(SlotIndex)
   │    ├─ Get SaveHeader.ChapterTag
   │    ├─ TransitionToChapter(ChapterTag)
   │    └─ On level loaded: BPC_PlayerRespawnSystem.RestoreState()
   │
   ├─ [Delete Save]
   │    ├─ Hold button for 1.5s to confirm (prevents accidental deletion)
   │    ├─ SS_SaveManager.DeleteSaveSlot(SlotIndex)
   │    └─ Refresh slot list
   │
   └─ [New Save — Manual]
        └─ SS_SaveManager.CreateManualSave(SlotIndex, "Manual")
```

---

## Blueprint Wiring Checklist

### Save System
- [ ] Configure `SS_SaveManager` with 3 default save slots
- [ ] Implement `I_Persistable` on all 7 persistent actor types
- [ ] Wire `BPC_PersistentWorldStateRecorder` to capture world state at checkpoints/death
- [ ] Wire save/load UI through `WBP_GamePauseMenu` and `WBP_GameMainMenu`

### Checkpoints
- [ ] Create `BP_Checkpoint_SafeRoom`, `BP_Checkpoint_Mirror`, `BP_Checkpoint_BasementGate`, `BP_Checkpoint_VoidThreshold`
- [ ] Wire overlap → auto-save + health/stamina restore + notification

### Death
- [ ] Wire `BPC_DeathHandlingSystem` → death animation → death screen → respawn
- [ ] Wire `BPC_PersistentCorpseSystem` → leave corpse at death location
- [ ] Wire `BPC_PlayerRespawnSystem` → load checkpoint + restore state
- [ ] Wire `WBP_DeathScreen` with Continue/MainMenu/Void options

### Void Space
- [ ] Wire `BPC_AltDeathSpaceSystem` → enter void on Shade death
- [ ] Create void puzzle (3 memory shrines) in `L_Asylum_VoidSpace`
- [ ] Wire exit → restore pre-void state + void-touched benefits

### Run History
- [ ] Wire `BPC_RunHistoryTracker` to log deaths, checkpoints, chapter times
- [ ] Display stats on death screen + post-credits summary

---

## Notes for Expansion

- Add **cloud saves** (platform-specific: Steam Cloud, PSN, Xbox Live)
- Add **save file encryption** to prevent tampering (anti-cheat)
- Add **ironman/permadeath mode**: disable all saves except checkpoint auto-save, one life only
- Add **save transfer**: export/import saves for cross-platform
- Add **death replay**: camera pans to show how you died (like a replay of last 5 seconds)
- Void Space could have **multiple exits** that lead to different real-world locations
- Multiplayer: save states must be server-authoritative, replicated to joining clients

---

*Save System & Checkpoints for Project Void. See [GAMEINDEX.md](GAMEINDEX.md) for full game structure. See [35_SS_SaveManager.md](../blueprints/05-saveload/35_SS_SaveManager.md) for save subsystem spec.*
