Files
UE5-Modular-Game-Framework/docs/game/save-checkpoints.md
Lefteris Notas 040db37720 Add UI Overrides and Weapons Index documentation for Project Void
- Created ui-overrides.md detailing game-specific Widget Blueprint overrides, including purpose, widget index, visual styling, and accessibility requirements.
- Established weapons-index.md outlining all held weapon actors, including their components, logic, and comparisons for gameplay mechanics.
2026-05-21 22:27:57 +03:00

388 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.*