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.
This commit is contained in:
387
docs/game/save-checkpoints.md
Normal file
387
docs/game/save-checkpoints.md
Normal file
@@ -0,0 +1,387 @@
|
||||
# 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.*
|
||||
Reference in New Issue
Block a user