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

15 KiB
Raw Blame History

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 for full game structure. See 35_SS_SaveManager.md for save subsystem spec.