add blueprints

This commit is contained in:
Lefteris Notas
2026-05-19 13:22:27 +03:00
parent f71bc678b2
commit 411edea8ce
138 changed files with 23330 additions and 0 deletions

View File

@@ -0,0 +1,216 @@
# SS_SaveManager — GameInstance Subsystem
**File:** [`Content/Framework/Save/SS_SaveManager`](Content/Framework/Save/SS_SaveManager.uasset)
**Purpose:** The single authority for all serialisation and deserialisation of game state to disk. Supports multiple save slots, checkpoint saves, hard saves, auto-saves, and world object persistence.
**Depends On:** [`GI_GameFramework`](../01-core/04_GI_GameFramework.md), [`I_Persistable`](30_I_Persistable.md)
**Used By:** [`BPC_CheckpointSystem`](31_BPC_CheckpointSystem.md), [`WBP_SaveLoadMenu`](../06-ui/), [`BPC_PlayerRespawnSystem`](31_BPC_CheckpointSystem.md)
---
## Enums
```cpp
// E_SaveType — Categorizes the trigger that initiated a save
E_SaveType
{
Checkpoint, // Auto-triggered by crossing a checkpoint volume
HardSave, // Player-initiated manual save
AutoSave, // Periodic or event-triggered auto save
DeathStateSave // Snapshot created when the player dies (for death loop continuity)
}
```
---
## Structs
```cpp
// S_SaveSlotInfo — Metadata entry for one save slot in the manifest
S_SaveSlotInfo
{
SlotIndex: Integer // 0-based slot number
DisplayName: FText // Player-chosen or auto-generated name
Timestamp: FDateTime // UTC timestamp of last save
ChapterTag: GameplayTag // Which chapter/level was active
ThumbnailBytes: Byte Array // Compressed screenshot for UI preview
PlaytimeSeconds: Float // Total playtime from GI_GameFramework
}
// S_WorldObjectState — Delta record for one persistent world actor
S_WorldObjectState
{
ObjectTag: GameplayTag // Unique identifier matching the actor's tag
bDestroyed: Bool // Was the actor destroyed?
CustomData: Map (Name String) // Generic key-value store for any component state
}
// S_PlayerSnapshot — Complete capture of player state at save time
S_PlayerSnapshot
{
// Core vitals
Health: Float // Current HP
Stress: Float // Current stress level
Stamina: Float // Current stamina pool
// Transform
Position: Vector // World location
Rotation: Rotator // World rotation
// Inventory
InventoryData: Array of S_InventorySlot // From BPC_InventorySystem
QuickSlotData: Array of S_QuickSlotEntry // From BPC_InventoryQuickSlot
EquipmentData: Map (GameplayTag S_EquipmentSlot) // From BPC_EquipmentSystem
// Narrative
NarrativeFlags: Map (GameplayTag Bool) // From BPC_NarrativeStateSystem
NarrativeValues: Map (GameplayTag Float)
// Metrics
DeathCount: Integer // From BPC_PlayerMetricsTracker
ItemsCollected: Integer
DistanceTravelled: Float
// Additional
ActiveObjectiveTags: Array of GameplayTag // From BPC_ObjectiveSystem
HidingState: Bool // Was player hiding?
CurrentHidingSpotTag: GameplayTag
AltDeathSpaceEnterCount: Integer
}
```
---
## Variables
| Name | Type | Description |
|------|------|-------------|
| `SlotManifest` | Array of `S_SaveSlotInfo` | Cached metadata for all available save slots |
| `ActiveSaveObject` | SaveGame Object Reference | The currently loaded save data object in memory |
| `bIsSaving` | Bool | Prevents concurrent save operations |
| `bIsLoading` | Bool | Prevents concurrent load operations |
| `SaveVersion` | Integer | Current schema version for migration detection |
| `WorldStateDelta` | Map (Name → `S_WorldObjectState`) | Live delta of changed world objects since last save |
| `PersistentActors` | Array of Actor Reference | All registered `I_Persistable` actors in the current level |
---
## Functions / Events
| Name | Inputs | Outputs | Description |
|------|--------|---------|-------------|
| `SaveToSlot` | SlotIndex: Int, SaveType: E_SaveType | Bool Success | Orchestrates full save: collects all state, writes SaveGame to disk, updates manifest |
| `LoadFromSlot` | SlotIndex: Int | Bool Success | Reads SaveGame from disk, migrates if needed, distributes state to all systems |
| `DeleteSlot` | SlotIndex: Int | — | Removes save file and manifest entry |
| `GetSlotManifest` | — | Array of `S_SaveSlotInfo` | Returns all slot metadata for UI display |
| `GetSaveFileSize` | SlotIndex: Int | Int64 Bytes | Returns the on-disk file size for the given slot |
| `RegisterPersistableActor` | Actor: Actor Reference | — | Adds an `I_Persistable` actor to the tracked list |
| `UnregisterPersistableActor` | Actor: Actor Reference | — | Removes an `I_Persistable` actor from the tracked list |
| `CollectWorldState` | — | — | Iterates all registered `I_Persistable` actors and calls `CollectState()` |
| `DistributeWorldState` | — | — | Iterates all registered `I_Persistable` actors and calls `RestoreState()` |
| `CollectPlayerSnapshot` | — | `S_PlayerSnapshot` | Gathers player state from all relevant components |
| `DistributePlayerSnapshot` | Snapshot: S_PlayerSnapshot | — | Restores player state to all relevant components |
| `MigrateSaveVersion` | OldVersion: Int, Data: SaveGame Object | SaveGame Object | Upgrades old save schema to current version |
| `SaveCheckpoint` | CheckpointTag: GameplayTag | — | Saves checkpoint data to the active slot without prompting the player |
| `SaveDeathState` | — | — | Saves a special death-state snapshot for death loop continuity |
| `LoadDeathState` | — | Bool Success | Loads the death-state snapshot |
| `HasSaveData` | SlotIndex: Int | Bool | Returns whether a save file exists for the slot |
| `GetActiveSaveSizeInfo` | — | FText | Formatted string with playtime, chapter, timestamp for HUD |
---
## Event Dispatchers
| Name | Parameters | Fired When |
|------|-----------|------------|
| `OnSaveComplete` | SlotIndex: Int, SaveType: E_SaveType | Save operation finishes successfully |
| `OnLoadComplete` | SlotIndex: Int | Load operation finishes successfully |
| `OnSaveFailed` | SlotIndex: Int, ErrorCode: Int | Save fails (disk full, write error, etc.) |
| `OnLoadFailed` | SlotIndex: Int, ErrorCode: Int | Load fails (corrupt file, version mismatch, etc.) |
| `OnWorldStateCollected` | ActorCount: Int | After all `I_Persistable` actors collected state |
| `OnWorldStateDistributed` | ActorCount: Int | After all `I_Persistable` actors restored state |
| `OnSaveVersionMigration` | OldVersion: Int, NewVersion: Int | When a save file is migrated to a newer schema |
---
## Blueprint Flow Diagram
```mermaid
flowchart TD
A[SaveToSlot called] --> B{bIsSaving?}
B -->|Yes| C[Return False]
B -->|No| D[Set bIsSaving = true]
D --> E[CollectPlayerSnapshot]
E --> F[CollectWorldState]
F --> G[Write SaveGame to disk]
G --> H[Update SlotManifest]
H --> I[Set bIsSaving = false]
I --> J[Broadcast OnSaveComplete]
J --> K[Return True]
L[LoadFromSlot called] --> M{bIsLoading?}
M -->|Yes| N[Return False]
M -->|No| O[Set bIsLoading = true]
O --> P[Read SaveGame from disk]
P --> Q{File valid?}
Q -->|No| R[Broadcast OnLoadFailed]
R --> S[Set bIsLoading = false]
S --> T[Return False]
Q -->|Yes| U[Check SaveVersion]
U --> V{Needs migration?}
V -->|Yes| W[MigrateSaveVersion]
W --> X[DistributePlayerSnapshot]
V -->|No| X
X --> Y[DistributeWorldState]
Y --> Z[Set bIsLoading = false]
Z --> AA[Broadcast OnLoadComplete]
AA --> AB[Return True]
```
---
## Save File Structure
```
SaveGame Object (USaveGame-derived)
├── Header
│ ├── SaveVersion: Integer
│ └── SlotInfo: S_SaveSlotInfo
├── Player Snapshot
│ └── S_PlayerSnapshot
├── World State
│ └── WorldStateDelta: Map (Name → S_WorldObjectState)
└── Death State
└── DeathStateData: S_PlayerSnapshot (optional, only in DeathStateSave)
```
---
## Communication Matrix
| Target System | Method | Why |
|---------------|--------|-----|
| All `I_Persistable` actors | Interface call (`CollectState`, `RestoreState`) | World object persistence |
| `BPC_HealthSystem` / Stress / Stamina | Via `S_PlayerSnapshot` | Restore player vitals |
| `BPC_InventorySystem` | Via `S_PlayerSnapshot` | Restore inventory state |
| `BPC_NarrativeStateSystem` | Via `S_PlayerSnapshot` | Restore narrative flags |
| `BPC_PlayerMetricsTracker` | Via `S_PlayerSnapshot` | Restore session metrics |
| `BPC_ObjectiveSystem` | Via `S_PlayerSnapshot` | Restore objective state |
| `BPC_EquipmentSystem` | Via `S_PlayerSnapshot` | Restore equipment state |
| `BPC_InventoryQuickSlot` | Via `S_PlayerSnapshot` | Restore quick slot layout |
| `BPC_CheckpointSystem` | Dispatcher `OnLoadComplete` | Notify checkpoint system to reset |
| `WBP_SaveLoadMenu` | Dispatcher `OnSaveComplete`/`OnLoadComplete` | UI refresh after operation |
| `GI_GameFramework` | Direct (owned by) | Access active slot index, update phase |
| `BPC_DeathHandlingSystem` | Direct call `SaveDeathState`/`LoadDeathState` | Death loop continuity |
---
## Reuse Notes
- Add new fields to `S_PlayerSnapshot` per project without breaking existing saves (migration handles missing fields)
- The `WorldStateDelta` map handles any `I_Persistable` actor generically — no per-actor serialisation code needed
- Save file naming convention: `SaveSlot_{SlotIndex}.sav`
- All disk I/O uses UE5's `UGameplayStatics::SaveGameToSlot` / `LoadGameFromSlot`
- Register/unregister `I_Persistable` actors in `BeginPlay` / `EndPlay` of the actor
- For multiplayer, the `GM_CoreGameMode` should be the Server-authorised caller of `SaveToSlot` / `LoadFromSlot`

View File

@@ -0,0 +1,103 @@
# 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.<LevelName>.<ActorClassName>.<Index>` |
---
## 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

View File

@@ -0,0 +1,169 @@
# BP_Checkpoint — Checkpoint Actor
**File:** [`Content/Framework/Save/BP_Checkpoint`](Content/Framework/Save/BP_Checkpoint.uasset)
**Purpose:** Manages checkpoint volumes in the world, tracks the most recent checkpoint crossed by the player, and triggers save operations via [`SS_SaveManager`](35_SS_SaveManager.md). Also stores respawn transform data for use by the death handling systems.
**Depends On:** [`SS_SaveManager`](35_SS_SaveManager.md), [`GM_CoreGameMode`](../01-core/05_GM_CoreGameMode.md)
**Used By:** Player Controller or Game Mode (placed at game setup)
---
## Enums
```cpp
// E_CheckpointActivationMode — Determines when a checkpoint triggers
E_CheckpointActivationMode
{
OnOverlap, // Player overlaps the checkpoint volume
OnInteract, // Player must press an interact key at the checkpoint
Automated // Triggered by external event (e.g., completing a puzzle)
}
```
---
## Structs
```cpp
// S_CheckpointData — Runtime data for one checkpoint
S_CheckpointData
{
CheckpointTag: GameplayTag // Unique identifier for this checkpoint
DisplayName: FText // "Save Room - Floor 2", etc.
RespawnLocation: Transform // Where the player spawns on reload
CameraCutTransform: Transform // Optional camera cut point for respawn transition
bHasBeenUsed: Bool // Has the player crossed this checkpoint?
ActivationMode: E_CheckpointActivationMode
bAutoSave: Bool // Does crossing this checkpoint trigger an auto-save?
LinkedObjectives: Array of GameplayTag // Objectives to mark complete on activation
}
// S_RespawnCache — Lightweight in-memory respawn data (not saved to disk)
S_RespawnCache
{
LastCheckpointTag: GameplayTag
LastCheckpointLocation: Transform
bIsRespawnFromDeath: Bool // False = checkpoint load, True = death respawn
DeathCountAtCheckpoint: Integer // Snapshot of death count for metrics
}
```
---
## Variables
| Name | Type | Description |
|------|------|-------------|
| `RegisteredCheckpoints` | Map (GameplayTag → `S_CheckpointData`) | All checkpoint volumes in the current level |
| `ActiveCheckpoint` | `S_CheckpointData` | The most recently activated checkpoint |
| `RespawnCache` | `S_RespawnCache` | Lightweight cache for respawn without full load |
| `bCanCrossCheckpoint` | Bool | Cooldown guard to prevent double-trigger (reset after 0.5s) |
| `DefaultRespawnTransform` | Transform | Fallback spawn point if no checkpoint exists (level start) |
| `CheckpointCooldownTimer` | Float | Timer handle for cooldown (0.5s default) |
---
## Functions / Events
| Name | Inputs | Outputs | Description |
|------|--------|---------|-------------|
| `RegisterCheckpoint` | Data: `S_CheckpointData` | — | Adds a checkpoint to the registered map |
| `UnregisterCheckpoint` | CheckpointTag: GameplayTag | — | Removes a checkpoint from the map |
| `ActivateCheckpoint` | CheckpointTag: GameplayTag | — | Sets the given checkpoint as active; triggers save if `bAutoSave` is true |
| `GetActiveCheckpoint` | — | `S_CheckpointData` | Returns the currently active checkpoint |
| `GetRespawnTransform` | — | Transform | Returns the respawn location (active checkpoint or fallback) |
| `HasActiveCheckpoint` | — | Bool | Returns whether a checkpoint has been crossed |
| `GetCheckpointList` | — | Array of `S_CheckpointData` | Returns all registered checkpoints (for UI map / fast travel) |
| `ClearCheckpointData` | — | — | Resets all checkpoint state (on new game) |
| `SetDefaultRespawn` | Transform: Transform | — | Sets the fallback respawn point when no checkpoint exists |
| `OnPlayerDeath` | — | — | Fired by GM_CoreGameMode; caches current checkpoint as death respawn point |
| `OnPlayerRespawn` | — | — | Fired after respawn; clears death flags |
---
## Event Dispatchers
| Name | Parameters | Fired When |
|------|-----------|------------|
| `OnCheckpointActivated` | CheckpointTag: GameplayTag, DisplayName: FText | A checkpoint is crossed and becomes active |
| `OnCheckpointSaveTriggered` | CheckpointTag: GameplayTag | An auto-save was triggered by this checkpoint |
| `OnRespawnPointSet` | RespawnTransform: Transform | The respawn point has changed |
| `OnCheckpointListChanged` | CheckpointCount: Int | A checkpoint was added or removed |
---
## Blueprint Flow Diagram
```mermaid
flowchart TD
A[Player overlaps checkpoint volume] --> B{Volume implements\nI_CheckpointVolume?}
B -->|No| C[Ignore]
B -->|Yes| D[Get checkpoint GameplayTag]
D --> E{Can cross?\nbCanCrossCheckpoint}
E -->|No| F[Ignore]
E -->|Yes| G[Set bCanCrossCheckpoint = false]
G --> H[Lookup in RegisteredCheckpoints map]
H --> I{Found?}
I -->|No| J[Log warning: unregistered checkpoint]
I -->|Yes| K[Set ActiveCheckpoint = found data]
K --> L[Set RespawnCache.LastCheckpointTag]
L --> M[Set RespawnCache.LastCheckpointLocation]
M --> N[Broadcast OnCheckpointActivated]
N --> O{bAutoSave?}
O -->|Yes| P[SS_SaveManager.SaveCheckpoint\nCheckpointTag]
P --> Q[Broadcast OnCheckpointSaveTriggered]
O -->|No| R[Skip save]
R --> S[Start cooldown timer 0.5s]
Q --> S
S --> T[Timer expires -> set bCanCrossCheckpoint = true]
```
---
## Communication Matrix
| Target System | Method | Why |
|---------------|--------|-----|
| `SS_SaveManager` | Direct call `SaveCheckpoint` | Trigger checkpoint auto-save |
| `SS_SaveManager` | Bind to `OnLoadComplete` | Restore active checkpoint tag after load |
| `GM_CoreGameMode` | Bind to death/respawn events | Receive death and respawn notifications |
| `BPC_PlayerRespawnSystem` | Provide `GetRespawnTransform` | Supply respawn location on death |
| `BPC_DeathHandlingSystem` | Provide `RespawnCache` data | Death handling reads cache for appropriate flow |
| Checkpoint Volume Actors | Interface call | Register/unregister via BeginPlay/EndPlay |
| `WBP_CheckpointNotification` | Dispatcher `OnCheckpointActivated` | Show "Checkpoint Saved" HUD notification |
| `WBP_MapWidget` | Function `GetCheckpointList` | Display discovered checkpoints on map |
---
## Checkpoint Volume Setup
```
BP_CheckpointVolume (Box Collision, Render CustomDepth)
└── Collision: OverlapOnly (PlayerOnly)
└── OnComponentBeginOverlap
└── Get owner's CheckpointTag (GameplayTag)
└── Call BP_Checkpoint.ActivateCheckpoint(Tag)
```
**Checkpoint Volume Variables:**
| Variable | Type | Source |
|----------|------|--------|
| `CheckpointTag` | GameplayTag | Set per-instance in level |
| `DisplayName` | FText | Set in defaults (e.g., "Hospital Lobby") |
| `RespawnLocation` | Transform | Arrow component on the volume |
| `CameraCutTransform` | Transform | Optional arrow component |
| `ActivationMode` | E_CheckpointActivationMode | Overlap by default |
| `bAutoSave` | Bool | True by default |
---
## Reuse Notes
- Each checkpoint volume creates itself by calling `RegisterCheckpoint` in `BeginPlay` with its configured `S_CheckpointData`
- Single-player only — multiplayer requires a server-authorised version with per-player checkpoint tracking
- The 0.5s cooldown prevents double-activation from overlapping two volumes simultaneously
- `DefaultRespawnTransform` should be set by `GM_CoreGameMode` on level start from the PlayerStart actor
- `CheckpointTag` naming convention: `Checkpoint.<LevelName>.<AreaName>`
- Renamed from `BPC_CheckpointSystem` to `BP_Checkpoint` per Master naming convention.

View File

@@ -0,0 +1,95 @@
# BPC_AltDeathSpaceSystem — Actor Component (Alt Death Space)
**File:** [`Content/Framework/Save/BPC_AltDeathSpaceSystem`](Content/Framework/Save/BPC_AltDeathSpaceSystem.uasset)
**Purpose:** Manages the limbo / between-world death space where the player can wander, find clues, or trigger special mechanics before being returned to the living world.
**Depends On:** [`BPC_DeathHandlingSystem`](BPC_DeathHandlingSystem.md), [`BP_Checkpoint`](BP_Checkpoint.md), [`SS_SaveManager`](35_SS_SaveManager.md)
**Used By:** [`BPC_DeathHandlingSystem`](BPC_DeathHandlingSystem.md) (orchestrator)
---
## Variables
| Name | Type | Description |
|------|------|-------------|
| `AltDeathSpaceMap` | Map (GameplayTag → Level Soft Reference) | Tag-to-level mapping for alt death space variants |
| `CurrentAltDeathSpace` | Level Soft Reference | The active alt death space level |
| `AltDeathSpaceEnterCount` | Integer | How many times the player has entered alt space this run |
| `ExitPortalTransform` | Transform | Where the player emerges back into the living world |
| `bIsInAltDeathSpace` | Bool | Is the player currently in the alt death space? |
| `AltSpaceTimeline` | Float | Current time-in-alt-space (for atmosphere changes) |
| `MaxTimeInAltSpace` | Float | Max seconds before forced exit (default 60.0) |
---
## Functions
| Name | Inputs | Outputs | Description |
|------|--------|---------|-------------|
| `EnterAltDeathSpace` | Context: S_DeathContext | — | Loads the alt death space level, moves player |
| `ExitAltDeathSpace` | — | — | Unloads alt space, returns player to ExitPortalTransform |
| `GetCurrentAltDeathSpaceTag` | — | GameplayTag | Returns which variant of alt space the player is in |
| `SetExitPortal` | Transform: Transform | — | Sets where the player exits alt space |
| `IsInAltDeathSpace` | — | Bool | Public getter for bIsInAltDeathSpace |
---
## Event Dispatchers
| Name | Parameters | Fired When |
|------|-----------|------------|
| `OnEnteredAltDeathSpace` | SpaceTag: GameplayTag | Player enters alt death space |
| `OnExitedAltDeathSpace` | — | Player exits alt death space back to living world |
| `OnAltSpaceTimeLow` | RemainingSeconds: Float | 10s remaining before forced exit (every 1s tick) |
| `OnAltSpaceForcedExit` | — | Timer expired, player forcibly returned |
---
## Blueprint Flow
```
[EnterAltDeathSpace]
└─► Set bIsInAltDeathSpace = true
└─► Increment AltDeathSpaceEnterCount
└─► Lookup CurrentAltDeathSpace from AltDeathSpaceMap by Context tag
└─► Load stream level (CurrentAltDeathSpace)
└─► Move player to level entry point
└─► Set ExitPortalTransform to Context.CurrentCheckpoint respawn location
└─► Start AltSpaceTimeline countdown
└─► Broadcast OnEnteredAltDeathSpace
[ExitAltDeathSpace]
└─► Unload alt death space stream level
└─► Move player to ExitPortalTransform
└─► Set bIsInAltDeathSpace = false
└─► Broadcast OnExitedAltDeathSpace
[Tick: AltSpaceTimeline]
└─► If TimeRemaining <= 10.0:
Broadcast OnAltSpaceTimeLow(TimeRemaining)
└─► If TimeRemaining <= 0:
Broadcast OnAltSpaceForcedExit
ExitAltDeathSpace()
```
---
## Communications With
| Target System | Method | Why |
|---------------|--------|-----|
| [`BPC_DeathHandlingSystem`](BPC_DeathHandlingSystem.md) | Direct call | Orchestrator triggers enter/exit |
| [`BP_Checkpoint`](BP_Checkpoint.md) | Direct call | Get respawn transform for exit portal |
| [`SS_SaveManager`](35_SS_SaveManager.md) | Direct call | Save alt space state |
| [`BPC_AtmosphereStateController`](../10-adaptive/BPC_AtmosphereStateController.md) | Dispatcher | Atmosphere changes based on time in alt space |
| [`SS_AudioManager`](../10-adaptive/132_SS_AudioManager.md) | Direct call | Audio ambience for alt space (`PlayAmbient()`, `SetRoomPreset()`) |
---
## Reuse Notes
- Alt death space levels are loaded via `UGameplayStatics::LoadStreamLevel` and must be in the persistent level's Streaming Levels list
- The ExitPortalTransform is set to the last checkpoint location by default, but can be overridden by alt space mechanics
- Alt space variants are data-driven via AltDeathSpaceMap — designers add new entries without code changes
- The MaxTimeInAltSpace timer prevents the player from being stuck indefinitely
- Split from original bundled `31_BPC_DeathHandlingSystem.md` per Clean Slate refactoring plan

View File

@@ -0,0 +1,170 @@
# BPC_DeathHandlingSystem — Actor Component (Death Orchestrator)
**File:** [`Content/Framework/Save/BPC_DeathHandlingSystem`](Content/Framework/Save/BPC_DeathHandlingSystem.uasset)
**Purpose:** The single authority for the player death flow. Determines the death outcome (standard respawn, game over, or alt death space), manages the death animation/camera sequence, coordinates corpse persistence, tracks run history, and triggers respawn. Sits on the Player Controller alongside four sub-systems that each handle one death-related concern.
**Depends On:** [`SS_SaveManager`](35_SS_SaveManager.md), [`BP_Checkpoint`](BP_Checkpoint.md), [`BPC_HealthSystem`](../02-player/08_BPC_HealthSystem.md), [`GM_CoreGameMode`](../01-core/05_GM_CoreGameMode.md), [`BPC_AltDeathSpaceSystem`](BPC_AltDeathSpaceSystem.md), [`BPC_PersistentCorpseSystem`](BPC_PersistentCorpseSystem.md), [`BPC_PlayerRespawnSystem`](BPC_PlayerRespawnSystem.md), [`BPC_RunHistoryTracker`](BPC_RunHistoryTracker.md)
**Used By:** Player Controller (spawned by `GM_CoreGameMode`)
---
## Enums
```cpp
// E_DeathOutcome — Determined by game rules + player state at death moment
E_DeathOutcome
{
StandardRespawn, // Normal death at a checkpoint — full respawn
GameOver, // No respawn possible — screen shows game over / main menu
AltDeathSpace // Player enters the limbo / between-world death space
}
// E_DeathAnimationStage — Controls the death anim sequence timeline
E_DeathAnimationStage
{
PreDeath, // Brief pause before death anim (player can see the killer)
DeathAnim, // Death animation playing (screen fade, camera pull back)
PostDeath, // Screen fully dark, system state being evaluated
RespawnTransition // Fade-up to respawn location
}
```
---
## Structs
```cpp
// S_DeathContext — Captured at the moment of death for all sub-systems
S_DeathContext
{
DeathLocation: Vector // World location where health hit 0
KillerTag: GameplayTag // What caused the death (DamageType.Tag)
bIsInHidingSpot: Bool // Was the player hiding when killed?
bWasFearCritical: Bool // Was stress/fear at critical level?
DeathTimestamp: FDateTime // UTC timestamp
DeathCount: Integer // Running total (from BPC_PlayerMetricsTracker)
CurrentCheckpoint: GameplayTag // Most recent checkpoint at death
bHasAltDeathSpace: Bool // Can this death use alt death space?
}
// S_DeathOutcomeRules — Configurable rules for determining death outcome
S_DeathOutcomeRules
{
bEnableAltDeathSpace: Bool // Master toggle for alt death space feature
MaxStandardDeaths: Integer // After X standard deaths, force GameOver
MaxAltDeathSpaceEntrances: Integer // After Y alt space entrances, force GameOver
AltDeathSpaceCheckpoint: GameplayTag // Which checkpoint to respawn to after alt space
bGameOverOnBossDeath: Bool // Boss kills always trigger game over
bGameOverInNarrativeClimax:Bool // Death at narrative climax = game over
}
```
---
## Core Component Variables (BPC_DeathHandlingSystem)
| Name | Type | Description |
|------|------|-------------|
| `DeathContext` | `S_DeathContext` | Captured at death moment; used by all sub-systems |
| `OutcomeRules` | `S_DeathOutcomeRules` | Configurable rules for outcome determination |
| `CurrentOutcome` | `E_DeathOutcome` | The determined death outcome |
| `AnimStage` | `E_DeathAnimationStage` | Current stage of the death animation sequence |
| `bIsDying` | Bool | True from death trigger until full respawn |
| `bIsRespawning` | Bool | True during the respawn process |
| `ActiveAltDeathSpaceSystem` | BPC_AltDeathSpaceSystem | Reference to sub-system component (must be on same actor) |
| `ActiveCorpseSystem` | BPC_PersistentCorpseSystem | Reference to sub-system component |
| `ActiveRespawnSystem` | BPC_PlayerRespawnSystem | Reference to sub-system component |
| `ActiveRunHistory` | BPC_RunHistoryTracker | Reference to sub-system component |
---
## Core Functions (BPC_DeathHandlingSystem)
| Name | Inputs | Outputs | Description |
|------|--------|---------|-------------|
| `OnPlayerDeath` | KillerTag: GameplayTag, DamageAmount: Float | — | Entry point — called by BPC_HealthSystem when HP reaches 0 |
| `DetermineDeathOutcome` | Context: S_DeathContext | E_DeathOutcome | Evaluates rules to decide StandardRespawn / GameOver / AltDeathSpace |
| `ExecuteDeathSequence` | Outcome: E_DeathOutcome | — | Drives the full death animation timeline |
| `ExecuteStandardRespawn` | Context: S_DeathContext | — | Saves death state, runs respawn via BPC_PlayerRespawnSystem |
| `ExecuteGameOver` | Context: S_DeathContext | — | Saves run history, triggers game over UI, disables input |
| `ExecuteAltDeathSequence` | Context: S_DeathContext | — | Spawns corpse, enters alt death space via BPC_AltDeathSpaceSystem |
| `AbortDeathSequence` | — | — | Cancels death flow (for debug / dev cheats) |
---
## Blueprint Flow Diagram
```mermaid
flowchart TD
A[BPC_HealthSystem: HP = 0] --> B[Capture S_DeathContext]
B --> C[Broadcast OnPlayerDeath]
C --> D[BPC_DeathHandlingSystem.OnPlayerDeath]
D --> E[DetermineDeathOutcome]
E --> F{Outcome?}
F -->|StandardRespawn| G[DeathAnimStage = DeathAnim]
G --> H[Spawn corpse via BPC_PersistentCorpseSystem]
H --> I[SS_SaveManager.SaveDeathState]
I --> J[DeathAnimStage = PostDeath]
J --> K[Delay RespawnDelay seconds]
K --> L[BPC_PlayerRespawnSystem.RespawnPlayer]
L --> M[DeathAnimStage = RespawnTransition]
M --> N[Fade in at checkpoint location]
N --> O[ApplyRespawnState]
O --> P[DeathAnimStage = null]
P --> Q[Broadcast OnRespawnCompleted]
F -->|AltDeathSpace| R[DeathAnimStage = DeathAnim]
R --> S[Spawn corpse via BPC_PersistentCorpseSystem]
S --> T[SS_SaveManager.SaveDeathState]
T --> U[BPC_AltDeathSpaceSystem.EnterAltDeathSpace]
U --> V[Set exit portal to current checkpoint]
V --> W[DeathAnimStage = null]
W --> X[Player wanders alt space]
X --> Y{Exit condition met?}
Y -->|Optional: find exit portal| Z[BPC_AltDeathSpaceSystem.ExitAltDeathSpace]
Y -->|Timer expired| AA[BPC_AltDeathSpaceSystem.ExitAltDeathSpace]
Z --> AB[Respawn at exit portal transform]
AA --> AB
AB --> AC[ApplyRespawnState]
AC --> AD[Broadcast OnRespawnCompleted]
F -->|GameOver| AE[DeathAnimStage = PostDeath]
AE --> AF[BPC_RunHistoryTracker.EndRun]
AF --> AG[Disable player input]
AG --> AH[Show Game Over screen]
AH --> AI[Offer: Load / Main Menu / Quit]
```
---
## Communication Matrix
| Target System | Method | Why |
|---------------|--------|-----|
| `BPC_HealthSystem` | Dispatcher bind (HP reached 0) | Receives death trigger |
| `SS_SaveManager` | Direct call `SaveDeathState` / `LoadDeathState` | Saves/loads death state for continuity |
| `BP_Checkpoint` | Direct call `GetRespawnTransform` | Gets respawn location |
| `BP_Checkpoint` | Direct call `OnPlayerDeath` / `OnPlayerRespawn` | Notifies checkpoint system of death cycle |
| `GM_CoreGameMode` | Dispatcher `OnPlayerDeath` / `OnRespawnCompleted` | Game mode tracks death flow for game phase |
| `GS_CoreGameState` | Direct set `CurrentGamePhase` = GameOver | Game state reflects game over condition |
| `PC_PlayerController` | SetIgnoreMoveInput / SetIgnoreLookInput | Disable input during death sequence |
| `WBP_GameOverScreen` | Dispatcher via GM or direct | Show game over UI |
| `WBP_DeathScreen` | Dispatcher `OnPlayerDeath` | Show death animation overlay |
| `WBP_RunSummary` | Function `GetRunSummary` | Display death recap on game over |
| `BPC_PlayerMetricsTracker` | Read `DeathCount` | Include in death context |
| `I_Persistable` corpse actors | Register/unregister with `SS_SaveManager` | Corpse persistence |
---
## Reuse Notes
- All five sub-systems (DeathHandling, AltDeathSpace, Corpse, Respawn, RunHistory) sit on the same actor — the Player Controller
- The death outcome determination (`S_DeathOutcomeRules`) can be swapped per-level or per-chapter via `DA_LevelConfig`
- Corpse persistence across saves requires the corpse actor to implement `I_Persistable`
- Alt death space levels are loaded via `UGameplayStatics::LoadStreamLevel` and must be in the persistent level's Streaming Levels list
- Run history is session-only and NOT serialised — it resets on game restart
- `AbortDeathSequence` is useful for debug cheats: `GodMode` or `RespawnNow` console commands
- For multiplayer, the server must be the authority: all death trigger logic should run on the server via `HasAuthority()`

View File

@@ -0,0 +1,88 @@
# BPC_PersistentCorpseSystem — Actor Component (Persistent Corpse)
**File:** [`Content/Framework/Save/BPC_PersistentCorpseSystem`](Content/Framework/Save/BPC_PersistentCorpseSystem.uasset)
**Purpose:** Spawns a persistent corpse actor at the death location. The corpse remains in the world and can be interacted with (looted, examined) after respawn.
**Depends On:** [`BPC_DeathHandlingSystem`](BPC_DeathHandlingSystem.md), [`SS_SaveManager`](35_SS_SaveManager.md), [`I_Persistable`](36_I_Persistable.md)
**Used By:** [`BPC_DeathHandlingSystem`](BPC_DeathHandlingSystem.md) (orchestrator)
---
## Variables
| Name | Type | Description |
|------|------|-------------|
| `CorpseActorClass` | Actor Class Reference | Blueprint class for the corpse actor (e.g., BP_PlayerCorpse) |
| `ActiveCorpse` | Actor Reference | The spawned corpse in the world |
| `bSpawnCorpseOnDeath` | Bool | Toggle: should a corpse be spawned? |
| `CorpseLifetime` | Float | Seconds before corpse auto-destroys (default 300.0) |
| `bPersistCorpseAcrossSaves` | Bool | If true, corpse is saved via I_Persistable |
---
## Functions
| Name | Inputs | Outputs | Description |
|------|--------|---------|-------------|
| `SpawnCorpse` | Context: S_DeathContext | Actor Reference | Spawns the corpse BP at DeathLocation |
| `DestroyCorpse` | — | — | Removes the corpse from the world |
| `GetCorpseLocation` | — | Vector | Returns the active corpse's world location |
| `HasActiveCorpse` | — | Bool | Is there a corpse in the world? |
---
## Event Dispatchers
| Name | Parameters | Fired When |
|------|-----------|------------|
| `OnCorpseSpawned` | CorpseActor: Actor Reference | Corpse appears in the world |
| `OnCorpseDestroyed` | — | Corpse is cleaned up |
| `OnCorpseLooted` | CorpseActor: Actor Reference | Player loots the corpse |
---
## Blueprint Flow
```
[SpawnCorpse: Context]
└─► If !bSpawnCorpseOnDeath → return null
└─► Spawn Actor of CorpseActorClass at Context.DeathLocation
└─► Set corpse appearance based on DeathContext (pose, damage visuals)
└─► Set ActiveCorpse = spawned actor
└─► If bPersistCorpseAcrossSaves:
Register corpse with SS_SaveManager via I_Persistable
└─► Start CorpseLifetime auto-destroy timer
└─► Broadcast OnCorpseSpawned
[DestroyCorpse]
└─► If ActiveCorpse is valid:
If bPersistCorpseAcrossSaves: Unregister from SS_SaveManager
Destroy ActiveCorpse actor
Set ActiveCorpse = null
Broadcast OnCorpseDestroyed
[CorpseLifetime Timer]
└─► When timer expires:
DestroyCorpse()
```
---
## Communications With
| Target System | Method | Why |
|---------------|--------|-----|
| [`BPC_DeathHandlingSystem`](BPC_DeathHandlingSystem.md) | Direct call | Orchestrator triggers spawn/destroy |
| [`SS_SaveManager`](35_SS_SaveManager.md) | Direct call | Persist corpse across save/load |
| [`I_Persistable`](36_I_Persistable.md) | Interface | Corpse actor implements for serialization |
| [`BPC_InteractionDetector`](../03-interaction/16_BPC_InteractionDetector.md) | Dispatcher | Player can interact with corpse (loot) |
| [`BPC_InventorySystem`](../04-inventory/BPC_InventorySystem.md) | Direct call | Transfer loot items to player |
---
## Reuse Notes
- Corpse persistence across saves requires the corpse actor to implement `I_Persistable`
- CorpseLifetime default of 300 seconds can be configured per level
- Loot interaction routes through InteractionDetector and InventorySystem
- Split from original bundled `31_BPC_DeathHandlingSystem.md` per Clean Slate refactoring plan

View File

@@ -0,0 +1,89 @@
# BPC_PersistentWorldStateRecorder — Actor Component
**File:** [`Content/Framework/Save/BPC_PersistentWorldStateRecorder`](Content/Framework/Save/BPC_PersistentWorldStateRecorder.uasset)
**Parent Class:** `UActorComponent`
**Dependencies:** [`I_Persistable`](36_I_Persistable.md), [`SS_SaveManager`](35_SS_SaveManager.md)
**Purpose:** Tracks all world-object state changes throughout a session so they can be serialised on save and restored on load. Maintains the delta map of changed world objects and coordinates save/load with all registered `I_Persistable` actors.
---
## Variables
| Name | Type | Description |
|------|------|-------------|
| `PersistableActors` | Array of Actor Reference | All registered `I_Persistable` actors |
| `WorldStateDelta` | Map (Name → `S_WorldObjectState`) | Changed state records since last save |
| `bIsCollecting` | Bool | Prevent re-entrant collect operations |
| `bIsDistributing` | Bool | Prevent re-entrant distribute operations |
## Structs
```cpp
S_WorldObjectState
{
ObjectTag: GameplayTag // Unique identifier matching the actor's tag
bDestroyed: Bool // Was the actor destroyed?
CustomData: Map (Name String) // Generic key-value store for any component state
}
```
## Functions / Events
| Name | Inputs | Outputs | What it does |
|------|--------|---------|--------------|
| `RegisterPersistableActor` | Actor: Actor Reference | — | Adds an `I_Persistable` actor to the tracked list |
| `UnregisterPersistableActor` | Actor: Actor Reference | — | Removes an `I_Persistable` actor from the tracked list |
| `CollectWorldState` | — | — | Iterates all registered actors and calls `I_Persistable.CollectState()` |
| `DistributeWorldState` | — | — | Iterates all registered actors and calls `I_Persistable.RestoreState()` |
| `GetWorldStateDelta` | — | Map (Name → S_WorldObjectState) | Returns full delta map for serialisation |
| `ClearWorldStateDelta` | — | — | Clears the delta after save |
| `HasPersistableActor` | Actor: Actor Reference | Bool | Check if actor is registered |
## Event Dispatchers
| Name | Parameters | Fired when |
|------|-----------|-----------|
| `OnWorldStateCollected` | ActorCount: Integer | All registered actors collected |
| `OnWorldStateDistributed` | ActorCount: Integer | All registered actors restored |
## Blueprint Flow Diagram
```mermaid
flowchart TD
A[RegisterPersistableActor] --> B[Add to PersistableActors array]
C[SS_SaveManager.SaveToSlot called] --> D[Call CollectWorldState]
D --> E[bIsCollecting?]
E -->|Yes| F[Return]
E -->|No| G[Set bIsCollecting = true]
G --> H[Iterate PersistableActors]
H --> I[Call I_Persistable.CollectState on each]
I --> J[Store in WorldStateDelta]
J --> K[Set bIsCollecting = false]
K --> L[Broadcast OnWorldStateCollected]
M[SS_SaveManager.LoadFromSlot called] --> N[Call DistributeWorldState]
N --> O[bIsDistributing?]
O -->|Yes| P[Return]
O -->|No| Q[Set bIsDistributing = true]
Q --> R[Iterate PersistableActors]
R --> S[Call I_Persistable.RestoreState with WorldStateDelta entry]
S --> T[Set bIsDistributing = false]
T --> U[Broadcast OnWorldStateDistributed]
```
## Communications With
| Target System | Method | Why |
|---------------|--------|-----|
| All `I_Persistable` actors | Interface calls | Collect/restore world state |
| `SS_SaveManager` | Direct | Provides world state delta for save |
| `GI_GameFramework` | Direct | Lifecycle bindings |
## Reuse Notes
- Actors register themselves in `BeginPlay` and unregister in `EndPlay`
- The `CustomData` map allows any component to store arbitrary state without modifying the struct
- Adding new `I_Persistable` actor types requires zero changes to this system
- For spawned/destroyed actors mid-session, register/unregister when the actor is created/destroyed
- The `WorldStateDelta` is cleared after each save to track only changes since last save

View File

@@ -0,0 +1,97 @@
# BPC_PlayerRespawnSystem — Actor Component (Respawn)
**File:** [`Content/Framework/Save/BPC_PlayerRespawnSystem`](Content/Framework/Save/BPC_PlayerRespawnSystem.uasset)
**Purpose:** Handles the actual respawn mechanics after death outcome is determined. Reads respawn transform from `BP_Checkpoint`, applies state restoration from `SS_SaveManager`, and runs the respawn transition.
**Depends On:** [`BPC_DeathHandlingSystem`](BPC_DeathHandlingSystem.md), [`BP_Checkpoint`](BP_Checkpoint.md), [`SS_SaveManager`](35_SS_SaveManager.md), [`BPC_HealthSystem`](../02-player/08_BPC_HealthSystem.md), [`BPC_StaminaSystem`](../02-player/09_BPC_StaminaSystem.md), [`BPC_StressSystem`](../02-player/10_BPC_StressSystem.md)
**Used By:** [`BPC_DeathHandlingSystem`](BPC_DeathHandlingSystem.md) (orchestrator)
---
## Variables
| Name | Type | Description |
|------|------|-------------|
| `RespawnDelay` | Float | Seconds before respawn after death (default 2.0) |
| `RespawnFadeDuration` | Float | Seconds for camera fade (default 1.0) |
| `bRestoreHealthOnRespawn` | Bool | Should health be fully restored? (default true) |
| `HealthRestorePercent` | Float | If bRestoreHealthOnRespawn is false, what % HP to restore (0.0-1.0) |
| `bClearStressOnRespawn` | Bool | Reset stress to minimum on respawn? (default true) |
| `bRestoreStaminaOnRespawn` | Bool | Reset stamina to maximum on respawn? (default true) |
---
## Functions
| Name | Inputs | Outputs | Description |
|------|--------|---------|-------------|
| `RespawnPlayer` | Outcome: E_DeathOutcome, Context: S_DeathContext | — | Orchestrates the full respawn sequence |
| `GetRespawnTransform` | — | Transform | Delegates to BP_Checkpoint |
| `ApplyRespawnState` | — | — | Restores health, stamina, stress via their respective components |
| `ResetPlayerAtTransform` | Transform: Transform | — | Moves the character to the respawn location |
| `PlayRespawnTransition` | — | — | Camera fade-in, post-process effects |
---
## Event Dispatchers
| Name | Parameters | Fired When |
|------|-----------|------------|
| `OnRespawnStarted` | Outcome: E_DeathOutcome | Respawn sequence begins |
| `OnRespawnCompleted` | — | Player is fully respawned and controllable |
| `OnRespawnFailed` | Reason: FText | Respawn could not be completed |
---
## Blueprint Flow
```
[RespawnPlayer: Outcome, Context]
└─► Broadcast OnRespawnStarted(Outcome)
└─► Delay RespawnDelay seconds
└─► GetRespawnTransform() → RespawnLocation
└─► If RespawnLocation is invalid:
Broadcast OnRespawnFailed("No valid respawn point")
return
└─► ResetPlayerAtTransform(RespawnLocation)
└─► ApplyRespawnState()
└─► PlayRespawnTransition()
└─► Re-enable player input
└─► Broadcast OnRespawnCompleted
[ApplyRespawnState]
└─► If bRestoreHealthOnRespawn:
BPC_HealthSystem.SetHealth(MaxHealth)
└─► Else:
BPC_HealthSystem.SetHealth(MaxHealth * HealthRestorePercent)
└─► If bClearStressOnRespawn:
BPC_StressSystem.ResetStress()
└─► If bRestoreStaminaOnRespawn:
BPC_StaminaSystem.SetStamina(MaxStamina)
[GetRespawnTransform]
└─► Get BP_Checkpoint from Owner
└─► Return BP_Checkpoint.GetRespawnTransform()
```
---
## Communications With
| Target System | Method | Why |
|---------------|--------|-----|
| [`BPC_DeathHandlingSystem`](BPC_DeathHandlingSystem.md) | Direct call | Orchestrator triggers respawn |
| [`BP_Checkpoint`](BP_Checkpoint.md) | Direct call | Get respawn transform |
| [`BPC_HealthSystem`](../02-player/08_BPC_HealthSystem.md) | Direct call | Restore health on respawn |
| [`BPC_StaminaSystem`](../02-player/09_BPC_StaminaSystem.md) | Direct call | Restore stamina on respawn |
| [`BPC_StressSystem`](../02-player/10_BPC_StressSystem.md) | Direct call | Reset stress on respawn |
| [`SS_SaveManager`](35_SS_SaveManager.md) | Direct call | Load respawn state |
| [`WBP_HUDController`](../06-ui/WBP_HUDController.md) | Dispatcher | Fade transitions |
---
## Reuse Notes
- RespawnDelay and RespawnFadeDuration are configurable per difficulty level
- State restoration values (health %, stress reset, stamina) can be tuned via data assets
- Split from original bundled `31_BPC_DeathHandlingSystem.md` per Clean Slate refactoring plan

View File

@@ -0,0 +1,90 @@
# BPC_RunHistoryTracker — Actor Component (Run History)
**File:** [`Content/Framework/Save/BPC_RunHistoryTracker`](Content/Framework/Save/BPC_RunHistoryTracker.uasset)
**Purpose:** Records session-level death and run data for the run summary screen (death recap, stats, journal entries tied to the run). Not saved to the main save file but stored per-session for display at game over.
**Depends On:** [`BPC_DeathHandlingSystem`](BPC_DeathHandlingSystem.md)
**Used By:** [`BPC_DeathHandlingSystem`](BPC_DeathHandlingSystem.md) (orchestrator), UI (Run Summary screen)
---
## Variables
| Name | Type | Description |
|------|------|-------------|
| `RunHistory` | Array of `S_DeathContext` | Every death this run, in order |
| `RunStartTime` | FDateTime | When the current run started |
| `RunEndTime` | FDateTime | When the run ended (game over or quit) |
| `TotalDeathsThisRun` | Integer | Convenience counter |
| `bIsNewRun` | Bool | True until the first death occurs |
---
## Functions
| Name | Inputs | Outputs | Description |
|------|--------|---------|-------------|
| `RecordDeath` | Context: S_DeathContext | — | Appends death to run history |
| `GetRunHistory` | — | Array of S_DeathContext | Returns full death list |
| `GetRunDuration` | — | FTimespan | RunStartTime to now |
| `GetDeathCountThisRun` | — | Integer | Returns TotalDeathsThisRun |
| `StartNewRun` | — | — | Clears history, sets RunStartTime to now |
| `EndRun` | — | — | Sets RunEndTime to now |
| `GetRunSummary` | — | FText | Formatted summary text for run recap UI |
---
## Event Dispatchers
| Name | Parameters | Fired When |
|------|-----------|------------|
| `OnDeathRecorded` | DeathIndex: Integer, Context: S_DeathContext | A death was added to history |
| `OnRunStarted` | StartTime: FDateTime | StartNewRun was called |
| `OnRunEnded` | Duration: FTimespan, DeathCount: Integer | EndRun was called |
---
## Blueprint Flow
```
[RecordDeath: Context]
└─► Append Context to RunHistory array
└─► TotalDeathsThisRun = RunHistory.Length
└─► bIsNewRun = false
└─► Broadcast OnDeathRecorded(TotalDeathsThisRun - 1, Context)
[StartNewRun]
└─► Clear RunHistory array
└─► Set RunStartTime = CurrentDateTime
└─► Set TotalDeathsThisRun = 0
└─► Set bIsNewRun = true
└─► Broadcast OnRunStarted(RunStartTime)
[EndRun]
└─► Set RunEndTime = CurrentDateTime
└─► Broadcast OnRunEnded(GetRunDuration(), TotalDeathsThisRun)
[GetRunSummary]
└─► Format: "Run Duration: {duration}\nDeaths: {count}\nDeath 1: {context.KillerTag} at {context.DeathLocation}"
└─► Return formatted FText
```
---
## Communications With
| Target System | Method | Why |
|---------------|--------|-----|
| [`BPC_DeathHandlingSystem`](BPC_DeathHandlingSystem.md) | Direct call | Orchestrator records deaths and ends runs |
| [`GM_CoreGameMode`](../01-core/05_GM_CoreGameMode.md) | Direct call | Start new run on game start |
| UI (WBP_RunSummary) | Dispatcher / Function | Display run summary on game over |
| [`BPC_EndingAccumulator`](../07-narrative/BPC_EndingAccumulator.md) | Direct call | Death count feeds into ending evaluation |
---
## Reuse Notes
- Run history is session-only and NOT serialised — it resets on game restart
- The RunHistory array grows unbounded within a session (typically < 20 entries)
- RunSummary text formatting can be overridden per project for localization
- Split from original bundled `31_BPC_DeathHandlingSystem.md` per Clean Slate refactoring plan