add blueprints
This commit is contained in:
189
docs/blueprints/01-core/01_GI_GameTagRegistry.md
Normal file
189
docs/blueprints/01-core/01_GI_GameTagRegistry.md
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
# GI_GameTagRegistry — Blueprint Specification
|
||||||
|
|
||||||
|
> **Asset Type:** Data Asset (derives from `UPrimaryDataAsset`)
|
||||||
|
> **UE Version:** 5.5–5.7
|
||||||
|
> **Category:** 01-Core / Foundation
|
||||||
|
> **Build Phase:** Phase 0 — Item 1
|
||||||
|
> **Dependencies:** None (this is the first system)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Purpose
|
||||||
|
|
||||||
|
Centralises every `GameplayTag` namespace used across the framework in a single asset. All other systems reference tags from this registry — never raw strings, never `FName` comparisons, never hardcoded booleans for state.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Class Settings
|
||||||
|
|
||||||
|
| Setting | Value |
|
||||||
|
|---------|-------|
|
||||||
|
| **Parent Class** | `UPrimaryDataAsset` |
|
||||||
|
| **Blueprint Type** | Data Asset |
|
||||||
|
| **Asset Path** | `/Game/Framework/Core/DA_GameTagRegistry` |
|
||||||
|
| **Is Abstract** | No |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Enums
|
||||||
|
|
||||||
|
None. This Data Asset uses only **Gameplay Tags** as the canonical state identifiers. No enums are defined here.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Structs
|
||||||
|
|
||||||
|
None. The registry is a flat collection of tag declarations.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Variables
|
||||||
|
|
||||||
|
| Name | Type | Category | Instance Editable | Description |
|
||||||
|
|------|------|----------|-------------------|-------------|
|
||||||
|
| `TagNamespace` | `FText` | Documentation | Yes | Human-readable description of the tag namespace (e.g. "Player.State") |
|
||||||
|
| `bIsFrameworkTag` | `bool` | Documentation | Yes | `true` for framework-defined tags, `false` for project-specific overrides |
|
||||||
|
|
||||||
|
**NOTE:** The tag definitions themselves live in the project's `DefaultGameplayTags.ini` file (or via the **Project Settings → Gameplay Tags** editor). This asset provides a centralised **documentation anchor** for those tags. The Blueprint graph does not hold tag data.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Functions
|
||||||
|
|
||||||
|
### 6.1 Blueprint Pure Functions
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Category | Description |
|
||||||
|
|------|--------|---------|----------|-------------|
|
||||||
|
| `GetAllRegisteredTags` | — | `Array<FGameplayTag>` | Query | Returns all tags defined in this registry (reads from the project's tag table) |
|
||||||
|
| `GetTagDisplayName` | `Tag: FGameplayTag` | `FText` | Query | Returns the human-readable display name from the tag table |
|
||||||
|
| `ValidateTag` | `Tag: FGameplayTag` | `bool` | Validation | Returns `true` if the tag exists in the project's registered tag table |
|
||||||
|
|
||||||
|
### 6.2 Blueprint Callable Functions
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Category | Description |
|
||||||
|
|------|--------|---------|----------|-------------|
|
||||||
|
| `LogAllTags` | — | — | Debug | Prints all registered tags to the output log (Editor-only) |
|
||||||
|
| `ExportTagNamespace` | `NamespacePrefix: FString` | `FString` | Tooling | Exports all tags matching a namespace prefix as a formatted string (for documentation generation) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Event Dispatchers
|
||||||
|
|
||||||
|
None. This Data Asset is passive — it has no runtime events.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Overridden Events
|
||||||
|
|
||||||
|
| Event | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `OnAssetLoaded` | Logs a validation warning if no Gameplay Tags are registered in the project |
|
||||||
|
| `PreSaveGameplayTags` | Ensures the tag table is synchronised with the asset's documented namespaces |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Blueprint Graph Logic
|
||||||
|
|
||||||
|
### 9.1 Initialisation Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[OnAssetLoaded]
|
||||||
|
└─► Call GetAllRegisteredTags()
|
||||||
|
└─► Length == 0?
|
||||||
|
├─► Yes → Print Warning: "No Gameplay Tags registered! /Game/Framework/Core/DA_GameTagRegistry is empty."
|
||||||
|
└─► No → Log: "N tags registered."
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9.2 Validation Function Logic (ValidateTag)
|
||||||
|
|
||||||
|
```
|
||||||
|
[ValidateTag(Tag)]
|
||||||
|
└─► Use GameplayTag::RequestGameplayTag(Tag.TagName)
|
||||||
|
└─► IsValid?
|
||||||
|
├─► Yes → return true
|
||||||
|
└─► No → Print Warning: "Invalid Tag: {Tag}" → return false
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Tag Namespace Reference (Framework Canonical)
|
||||||
|
|
||||||
|
All framework-level tags are defined in `DefaultGameplayTags.ini`. The following namespaces are **reserved and documented** by this asset:
|
||||||
|
|
||||||
|
```
|
||||||
|
Player.State.Alive
|
||||||
|
Player.State.Dead
|
||||||
|
Player.State.Hidden
|
||||||
|
Player.State.Interacting
|
||||||
|
Player.Stress.Low / Mid / High / Critical
|
||||||
|
Player.Posture.Standing / Crouching / Prone / Vaulting
|
||||||
|
|
||||||
|
Interaction.Type.Pickup / Door / Drawer / Container / Inspect / Climb / Hide / Use / Combine
|
||||||
|
Interaction.Context.Requires.Key / Requires.Item / Locked / Disabled
|
||||||
|
|
||||||
|
Item.Type.Weapon / Consumable / KeyItem / Document / Collectible / Ammo / Tool
|
||||||
|
Item.Slot.PrimaryWeapon / SecondaryWeapon / Flashlight / Shield / Active
|
||||||
|
|
||||||
|
Narrative.Flag.* ← game-specific flags
|
||||||
|
Narrative.Phase.* ← story chapters / acts
|
||||||
|
Narrative.Choice.* ← choice consequence tags
|
||||||
|
Narrative.Ending.* ← ending conditions
|
||||||
|
|
||||||
|
Objective.Status.Active / Complete / Failed / Hidden
|
||||||
|
|
||||||
|
AI.Alert.None / Suspicious / Alerted / Engaged
|
||||||
|
AI.Archetype.Patrol / Ambush / Stalker / Passive
|
||||||
|
|
||||||
|
Save.Type.Checkpoint / HardSave / AutoSave
|
||||||
|
|
||||||
|
Achievement.* ← per-achievement tags
|
||||||
|
|
||||||
|
Environment.Atmosphere.* ← atmosphere state tags
|
||||||
|
Environment.Scare.* ← scare event tags
|
||||||
|
|
||||||
|
DeathSpace.Active ← triggers alt death space layer
|
||||||
|
```
|
||||||
|
|
||||||
|
**Usage Rule:** All systems must add the prefix `Framework.` to framework tags in code comments. Project-specific tags use `Game.` prefix. Example: `Framework.Interaction.Type.Pickup` vs `Game.Narrative.Flag.BasementDoorOpened`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Communication Matrix
|
||||||
|
|
||||||
|
| Target System | Method | Direction | Data |
|
||||||
|
|---------------|--------|-----------|------|
|
||||||
|
| All other systems | `GameplayTag` comparisons | Read-only | Framework-defined tag values |
|
||||||
|
|
||||||
|
This asset does not talk to other systems directly. All communication is passive — other systems read tag values from the project's tag table.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Validation Checklist
|
||||||
|
|
||||||
|
- [ ] All namespace prefixes documented in the Data Asset's `TagNamespace` text field
|
||||||
|
- [ ] `bIsFrameworkTag` set to `true` for framework namespaces
|
||||||
|
- [ ] At least one project-specific tag exists (e.g. `Game.Narrative.Flag.PrologueComplete`) to validate the workflow
|
||||||
|
- [ ] `DefaultGameplayTags.ini` contains ALL documented tags
|
||||||
|
- [ ] No duplicate tag names across namespaces
|
||||||
|
- [ ] No raw string comparisons exist in any other system (all must use `HasTag` / `MatchesTag`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Reuse Notes
|
||||||
|
|
||||||
|
- The `Game.` namespace is reserved for **project-specific** tags and is empty in the framework distribution.
|
||||||
|
- To add new tags: edit `DefaultGameplayTags.ini` or use the **Gameplay Tags** editor under **Project Settings**.
|
||||||
|
- The Data Asset is purely documentary — the real tag data lives in the project's tag table. This file exists so systems have a single place to look up "what tags exist in the framework" without reading the `.ini` file directly.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. Blueprint Implementation Steps (for Blueprint Agent)
|
||||||
|
|
||||||
|
1. Create a new **Data Asset** class.
|
||||||
|
2. Parent: `UPrimaryDataAsset`
|
||||||
|
3. Name: `DA_GameTagRegistry`
|
||||||
|
4. Save to: `/Game/Framework/Core/DA_GameTagRegistry`
|
||||||
|
5. Add variables `TagNamespace` (FText) and `bIsFrameworkTag` (bool).
|
||||||
|
6. Implement the functions listed in Section 6 as Blueprint Callable / Pure nodes.
|
||||||
|
7. Document all tag namespaces from Section 10 in the asset's description.
|
||||||
|
8. Save and run the validation test.
|
||||||
140
docs/blueprints/01-core/02_FL_GameUtilities.md
Normal file
140
docs/blueprints/01-core/02_FL_GameUtilities.md
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
# FL_GameUtilities — Blueprint Function Library
|
||||||
|
|
||||||
|
## Asset Details
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Class** | [`FL_GameUtilities`] |
|
||||||
|
| **Parent** | [`BlueprintFunctionLibrary`] |
|
||||||
|
| **Folder** | [`Framework/Core/`] |
|
||||||
|
| **Categorization** | [Core\Framework] |
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
A static Blueprint Function Library providing common utility functions available everywhere in the project without needing an object reference. Every Blueprint, component, widget, and AI asset can call these functions directly from the context menu.
|
||||||
|
|
||||||
|
## Does NOT Handle
|
||||||
|
|
||||||
|
- Game logic or state management
|
||||||
|
- Actor spawning or lifecycle
|
||||||
|
- Data asset lookups (those belong to the specific system)
|
||||||
|
- Any operation that requires a persistent reference or tick
|
||||||
|
|
||||||
|
## Structs
|
||||||
|
|
||||||
|
No structs are defined in this library. All input/output types are native Blueprint types or interface-derived.
|
||||||
|
|
||||||
|
## Enums
|
||||||
|
|
||||||
|
No enums are defined in this library.
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
This is a pure Function Library. It has **no variables, no event dispatchers, no tick**. All functions are static and context-free (with the exception of `WorldContextObject` for world-dependent operations).
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|
||||||
|
### Utility — Null-Safe Subsystem Retrieval
|
||||||
|
|
||||||
|
| Function | Category | Inputs | Outputs | Description |
|
||||||
|
|----------|----------|--------|---------|-------------|
|
||||||
|
| [`GetSubsystemSafe`] | Utility\Subsystems | `WorldContextObject: Object`, `SubsystemClass: Class` | `Subsystem: Object` | Null-safe subsystem retrieval. Returns `None` instead of crashing if the subsystem doesn't exist. |
|
||||||
|
| [`GetGameFramework`] | Utility\Subsystems | `WorldContextObject: Object` | `GameInstance: GI_GameFramework Object` | Typed cast to [`GI_GameFramework`](04_GI_GameFramework.md). Returns `None` if cast fails. Includes error log. |
|
||||||
|
| [`GetPlayerController`] | Utility\Subsystems | `WorldContextObject: Object` | `Controller: PC_CoreController Object` | Typed cast to [`PC_CoreController`](02-player/01_PC_CoreController.md). Returns `None` if cast fails. Includes error log. |
|
||||||
|
|
||||||
|
### Actor Utility
|
||||||
|
|
||||||
|
| Function | Category | Inputs | Outputs | Description |
|
||||||
|
|----------|----------|--------|---------|-------------|
|
||||||
|
| [`FindComponentByInterface`] | Actor\Components | `TargetActor: Actor`, `InterfaceClass: Interface` | `Component: ActorComponent` | Returns the first component on the given actor that implements the specified interface. Returns `None` if none found. |
|
||||||
|
| [`FindNearestActorWithTag`] | Actor\Spatial | `Origin: Vector`, `Radius: Float`, `Tag: GameplayTag`, `ActorsToIgnore: Array Actor` | `NearestActor: Actor`, `Distance: Float` | Scans all actors within radius for those with the given gameplay tag. Returns the closest one. |
|
||||||
|
|
||||||
|
### Math Utilities
|
||||||
|
|
||||||
|
| Function | Category | Inputs | Outputs | Description |
|
||||||
|
|----------|----------|--------|---------|-------------|
|
||||||
|
| [`RemapFloat`] | Math\Float | `Value: Float`, `InMin: Float`, `InMax: Float`, `OutMin: Float`, `OutMax: Float` | `Result: Float` | Linear remap from input range to output range. Clamped to output range. |
|
||||||
|
| [`LerpClamped`] | Math\Float | `A: Float`, `B: Float`, `Alpha: Float` | `Result: Float` | Lerp with alpha clamped 0-1. |
|
||||||
|
| [`VectorToAngle2D`] | Math\Vector | `Direction: Vector` | `Angle: Float (Degrees)` | Converts a 2D direction vector to an angle in degrees (0-360). |
|
||||||
|
| [`AngleDifference`] | Math\Float | `AngleA: Float`, `AngleB: Float` | `Difference: Float` | Shortest signed angle between two angles. |
|
||||||
|
|
||||||
|
### Gameplay Tag Utilities
|
||||||
|
|
||||||
|
| Function | Category | Inputs | Outputs | Description |
|
||||||
|
|----------|----------|--------|---------|-------------|
|
||||||
|
| [`HasGameplayTag`] | GameplayTags\Query | `TargetActor: Actor`, `Tag: GameplayTag` | `bHasTag: Bool` | Safe check if an actor's gameplay tag container includes the specified tag. Returns false if actor has no tag container. |
|
||||||
|
| [`AddGameplayTagToActor`] | GameplayTags\Modify | `TargetActor: Actor`, `Tag: GameplayTag` | — | Adds a gameplay tag to the actor's tag container if it implements the interface. Logs warning if missing. |
|
||||||
|
| [`RemoveGameplayTagFromActor`] | GameplayTags\Modify | `TargetActor: Actor`, `Tag: GameplayTag` | — | Removes a gameplay tag from the actor's tag container. |
|
||||||
|
| [`MakeTagFromString`] | GameplayTags\Conversion | `TagString: String` | `Tag: GameplayTag` | Converts a string to a GameplayTag. Returns `None` if the tag doesn't exist in the tag table. |
|
||||||
|
| [`TagContainerHasAny`] | GameplayTags\Query | `Container: GameplayTagContainer`, `TagsToCheck: GameplayTagContainer` | `bHasAny: Bool` | OR query: true if container has any of the specified tags. |
|
||||||
|
|
||||||
|
### String & Text Formatting
|
||||||
|
|
||||||
|
| Function | Category | Inputs | Outputs | Description |
|
||||||
|
|----------|----------|--------|---------|-------------|
|
||||||
|
| [`FormatTime`] | Text\Formatting | `TotalSeconds: Float` | `FormattedText: Text` | Converts seconds to "HH:MM:SS" or "MM:SS" display format. |
|
||||||
|
| [`Pluralise`] | Text\Formatting | `Count: Integer`, `Singular: Text`, `Plural: Text` | `Result: Text` | Returns singular or plural form based on count. |
|
||||||
|
| [`OrdinalSuffix`] | Text\Formatting | `Number: Integer` | `Suffix: String` | Returns "st", "nd", "rd", or "th" for a given number. |
|
||||||
|
| [`TruncateText`] | Text\Formatting | `Input: Text`, `MaxChars: Integer` | `Output: Text`, `bTruncated: Bool` | Truncates text with "..." if exceeding max length. |
|
||||||
|
|
||||||
|
### Screen & Viewport Utilities
|
||||||
|
|
||||||
|
| Function | Category | Inputs | Outputs | Description |
|
||||||
|
|----------|----------|--------|---------|-------------|
|
||||||
|
| [`WorldToScreenSafe`] | Screen\Conversion | `WorldContext: Object`, `WorldLocation: Vector` | `ScreenPosition: Vector2D`, `bIsOnScreen: Bool` | Projects world location to screen coordinates. Returns false if behind camera or off-screen. |
|
||||||
|
| [`ClampToViewport`] | Screen\Conversion | `WidgetPosition: Vector2D`, `Padding: Float` | `ClampedPosition: Vector2D` | Clamps a widget position to stay within the viewport bounds with padding. |
|
||||||
|
|
||||||
|
### Debug Helpers
|
||||||
|
|
||||||
|
| Function | Category | Inputs | Outputs | Description |
|
||||||
|
|----------|----------|--------|---------|-------------|
|
||||||
|
| [`LogDebug`] | Debug\Logging | `Category: Name`, `Message: String`, `Color: LinearColor` | — | Conditional screen + log output. Only fires in editor/development builds. Stripped in shipping builds via `DO_CHECK` flag. |
|
||||||
|
| [`DrawDebugSphere`] | Debug\Drawing | `WorldContext: Object`, `Center: Vector`, `Radius: Float`, `Color: LinearColor`, `Duration: Float` | — | Draws a debug sphere for visual debugging in-editor. Stripped in shipping builds. |
|
||||||
|
| [`DrawDebugString3D`] | Debug\Drawing | `WorldContext: Object`, `Location: Vector`, `Text: String`, `Color: LinearColor`, `Duration: Float` | — | Draws a 3D world-space debug string. Stripped in shipping builds. |
|
||||||
|
|
||||||
|
## Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[Any Blueprint calls FL_GameUtilities function]
|
||||||
|
└─► Function executes immediately (no async)
|
||||||
|
└─► Returns value or performs side effect
|
||||||
|
└─► Caller uses result in next node
|
||||||
|
```
|
||||||
|
|
||||||
|
Since this is a pure static library, there is no lifecycle. Every function is callable from any Blueprint's event graph, construction script, or function.
|
||||||
|
|
||||||
|
## Communicates With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|---------------|--------|-----|
|
||||||
|
| [`GI_GameFramework`](04_GI_GameFramework.md) | Direct cast | `GetGameFramework()` helper |
|
||||||
|
| [`PC_CoreController`](02-player/01_PC_CoreController.md) | Direct cast | `GetPlayerController()` helper |
|
||||||
|
| Any Actor | Interface + Tag | Actor utility functions |
|
||||||
|
|
||||||
|
## Blueprint Editor Usage
|
||||||
|
|
||||||
|
### How to Call
|
||||||
|
|
||||||
|
1. Right-click in any Event Graph or Function.
|
||||||
|
2. Search for the function name (e.g. `RemapFloat`, `FormatTime`).
|
||||||
|
3. The functions appear under their category name in the context menu.
|
||||||
|
4. Wire inputs and use output.
|
||||||
|
|
||||||
|
### Construction Script Usage
|
||||||
|
|
||||||
|
All functions can be called from Construction Scripts since they are pure (no world context required for math/text functions). Functions requiring `WorldContextObject` pass the `Self` reference from the construction script context.
|
||||||
|
|
||||||
|
## Reuse Notes
|
||||||
|
|
||||||
|
- This library ships verbatim to every project.
|
||||||
|
- Add project-specific helpers in a separate `FL_ProjectUtilities` child library that references this one.
|
||||||
|
- Do **not** add game-specific functions here — extend with a new Function Library inheriting from this pattern.
|
||||||
|
- Debug functions are automatically stripped in Shipping builds via `DO_CHECK` preprocessor — no manual removal needed.
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
1. Any Blueprint in the project can call `RemapFloat` from the Math category without errors.
|
||||||
|
2. `GetSubsystemSafe` returns `None` instead of crashing when a subsystem doesn't exist.
|
||||||
|
3. `FormatTime(3661.0)` returns `01:01:01`.
|
||||||
|
4. `LogDebug` prints to both the output log and the viewport in editor builds.
|
||||||
|
5. `LogDebug` produces no output in a shipping build.
|
||||||
396
docs/blueprints/01-core/03_I_InterfaceLibrary.md
Normal file
396
docs/blueprints/01-core/03_I_InterfaceLibrary.md
Normal file
@@ -0,0 +1,396 @@
|
|||||||
|
# I_InterfaceLibrary — Interface Collection
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This document defines all **9 Blueprint Interfaces** that form the backbone of cross-system communication in the framework. Interfaces are the **preferred communication method** over direct references — they decouple the caller from the callee's concrete class. Any Actor or Component can implement any of these interfaces, and any system can query for them and call them without knowing the underlying type.
|
||||||
|
|
||||||
|
## Folder Structure
|
||||||
|
|
||||||
|
All interfaces are placed in: [`Framework/Interfaces/`]
|
||||||
|
|
||||||
|
| Interface | File | Purpose |
|
||||||
|
|-----------|------|---------|
|
||||||
|
| [`I_Interactable`] | I_Interactable | Primary interaction entry point |
|
||||||
|
| [`I_Inspectable`] | I_Inspectable | Inspection/Examine system |
|
||||||
|
| [`I_Damageable`] | I_Damageable | Health, damage, death |
|
||||||
|
| [`I_Holdable`] | I_Holdable | Two-hand hold / use in hand |
|
||||||
|
| [`I_Lockable`] | I_Lockable | Lock/Unlock with key checks |
|
||||||
|
| [`I_UsableItem`] | I_UsableItem | Consumable/equippable item usage |
|
||||||
|
| [`I_Persistable`] | I_Persistable | Save/Load serialization contract |
|
||||||
|
| [`I_Toggleable`] | I_Toggleable | On/Off, Open/Close state machines |
|
||||||
|
| [`I_Adjustable`] | I_Adjustable | Continuous adjustment (pressure, dials) |
|
||||||
|
|
||||||
|
## Dependency Table
|
||||||
|
|
||||||
|
Each interface is independent. None inherit from another. A single actor can implement multiple interfaces (e.g. a door implements [`I_Interactable`], [`I_Toggleable`], [`I_Persistable`]).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## I_Interactable
|
||||||
|
|
||||||
|
### Asset Details
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Class** | [`I_Interactable`] |
|
||||||
|
| **Type** | Blueprint Interface |
|
||||||
|
| **Folder** | [`Framework/Interfaces/`] |
|
||||||
|
| **Category** | Interaction |
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
|
||||||
|
The primary interaction entry point. Any object the player can look at and press a button to interact with must implement this interface. It is the single point of contact for the interaction system.
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Function | Inputs | Outputs | Description |
|
||||||
|
|----------|--------|---------|-------------|
|
||||||
|
| [`OnInteract`] | `Instigator: Actor` | `bSuccess: Bool` | Called when the player presses the interact button. Returns false if interaction failed. |
|
||||||
|
| [`OnFocusBegin`] | `Instigator: Actor` | — | Called when the crosshair hovers over the object. Used to show UI prompts. |
|
||||||
|
| [`OnFocusEnd`] | `Instigator: Actor` | — | Called when the crosshair leaves the object. Hides UI prompt. |
|
||||||
|
| [`GetInteractionPrompt`] | — | `PromptText: Text` | Returns the text to display on the interaction HUD (e.g. "Press E to Open"). |
|
||||||
|
| [`CanInteract`] | `Instigator: Actor` | `bCanInteract: Bool`, `BlockReason: Text` | Query if interaction is currently possible. `BlockReason` provides a why-not string for UI feedback. |
|
||||||
|
| [`GetInteractionType`] | — | `InteractionType: E_InteractionType` | Returns the type of interaction (see enum below). |
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
|
||||||
|
| Enum | Values | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| `E_InteractionType` | `None`, `Pickup`, `Use`, `Examine`, `Talk`, `Open`, `Push`, `Pull`, `Custom` | Categorizes the interaction for UI icon and animation selection. |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[Player presses E]
|
||||||
|
└─► PC_CoreController calls OnInteract on target actor
|
||||||
|
└─► Target implements I_Interactable
|
||||||
|
└─► Returns bSuccess
|
||||||
|
└─► If false, PC plays "Blocked" feedback sound
|
||||||
|
```
|
||||||
|
|
||||||
|
### Communicates With
|
||||||
|
|
||||||
|
| Target | Direction | Why |
|
||||||
|
|--------|-----------|-----|
|
||||||
|
| [`PC_CoreController`](../02-player/01_PC_CoreController.md) | Called by | Interaction trace calls these functions |
|
||||||
|
| [`BPC_InteractionDetector`](../03-interaction/01_BPC_InteractionDetector.md) | Called by | Focus events are driven by the detector component |
|
||||||
|
| [`WBP_InteractionPrompt`](../05-ui/02_WBP_InteractionPrompt.md) | Consumed by | Reads `GetInteractionPrompt` and `GetInteractionType` for UI display |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## I_Inspectable
|
||||||
|
|
||||||
|
### Asset Details
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Class** | [`I_Inspectable`] |
|
||||||
|
| **Type** | Blueprint Interface |
|
||||||
|
| **Folder** | [`Framework/Interfaces/`] |
|
||||||
|
| **Category** | Interaction |
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
|
||||||
|
Controls the **inspection/examine** system. Objects that can be picked up and rotated in front of the camera for close examination implement this.
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Function | Inputs | Outputs | Description |
|
||||||
|
|----------|--------|---------|-------------|
|
||||||
|
| [`StartInspect`] | `Instigator: Actor` | `bSuccess: Bool` | Begins inspection mode. Returns false if inspection not possible. |
|
||||||
|
| [`EndInspect`] | `Instigator: Actor` | — | Ends inspection mode. |
|
||||||
|
| [`RotateInspect`] | `DeltaRotation: Rotator` | — | Applies incremental rotation while in inspection mode. |
|
||||||
|
| [`GetInspectData`] | — | `InspectData: F_InspectData` | Returns struct with inspect-relevant data. |
|
||||||
|
| [`HasInspectInfo`] | — | `bHasInfo: Bool` | Whether this object has additional inspectable lore/text. |
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
| Struct | Fields |
|
||||||
|
|--------|--------|
|
||||||
|
| `F_InspectData` | `InspectDuration: Float`, `bCanRotate: Bool`, `bHasAudioLog: Bool`, `InspectSound: SoundBase` |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[Player holds E to inspect]
|
||||||
|
└─► PC calls StartInspect
|
||||||
|
└─► Object spawns inspect proxy or enters inspect mode
|
||||||
|
└─► Player drags mouse → RotateInspect
|
||||||
|
└─► Player releases → EndInspect
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## I_Damageable
|
||||||
|
|
||||||
|
### Asset Details
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Class** | [`I_Damageable`] |
|
||||||
|
| **Type** | Blueprint Interface |
|
||||||
|
| **Folder** | [`Framework/Interfaces/`] |
|
||||||
|
| **Category** | Combat |
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
|
||||||
|
Any actor that can take damage, heal, or die implements this interface. This includes the player character, enemies, destructibles, and breakable environmental objects.
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Function | Inputs | Outputs | Description |
|
||||||
|
|----------|--------|---------|-------------|
|
||||||
|
| [`TakeDamage`] | `DamageAmount: Float`, `DamageType: F_GameplayTag`, `Instigator: Actor`, `SourceLocation: Vector` | `bSuccess: Bool` | Applies damage to the actor. Returns false if immune. |
|
||||||
|
| [`Heal`] | `HealAmount: Float`, `HealType: F_GameplayTag` | `bSuccess: Bool` | Heals the actor. |
|
||||||
|
| [`IsAlive`] | — | `bIsAlive: Bool` | Whether the actor is alive or destroyed. |
|
||||||
|
| [`GetMaxHealth`] | — | `MaxHealth: Float` | Returns the maximum health value. |
|
||||||
|
| [`GetCurrentHealth`] | — | `CurrentHealth: Float` | Returns the current health value. |
|
||||||
|
| [`OnDeath`] | `Instigator: Actor`, `DamageType: F_GameplayTag` | `bSuccess: Bool` | Called when health reaches zero. Handles death logic. |
|
||||||
|
| [`GetDamageModifier`] | `DamageType: F_GameplayTag` | `DamageMultiplier: Float` | Returns damage multiplier for a given damage type. |
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
|
||||||
|
No enums. Damage types are handled via gameplay tags.
|
||||||
|
|
||||||
|
### Communicates With
|
||||||
|
|
||||||
|
| Target | Direction | Why |
|
||||||
|
|--------|-----------|-----|
|
||||||
|
| [`BPC_HealthComponent`](../07-weapons/01_BPC_HealthComponent.md) | Implements | The health component implements this interface |
|
||||||
|
| Any weapon/damage source | Calls | Calls `TakeDamage` when hitting a target |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## I_Holdable
|
||||||
|
|
||||||
|
### Asset Details
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Class** | [`I_Holdable`] |
|
||||||
|
| **Type** | Blueprint Interface |
|
||||||
|
| **Folder** | [`Framework/Interfaces/`] |
|
||||||
|
| **Category** | Interaction |
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
|
||||||
|
Objects that can be grabbed and held in the player's hands (two-hand hold system). Used for puzzles where objects must be carried and placed.
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Function | Inputs | Outputs | Description |
|
||||||
|
|----------|--------|---------|-------------|
|
||||||
|
| [`OnPickup`] | `Holder: Actor` | `bSuccess: Bool` | Called when the player picks up the object. |
|
||||||
|
| [`OnDrop`] | `Holder: Actor` | — | Called when the player drops the object. |
|
||||||
|
| [`GetHoldTransform`] | — | `HoldLocation: Transform` | Returns the relative transform where the object should be held. |
|
||||||
|
| [`IsHeld`] | — | `bIsHeld: Bool` | Whether the object is currently being held. |
|
||||||
|
| [`OnReleasedFromHold`] | `NewLocation: Vector` | — | Called when the object is released (thrown or placed). |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## I_Lockable
|
||||||
|
|
||||||
|
### Asset Details
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Class** | [`I_Lockable`] |
|
||||||
|
| **Type** | Blueprint Interface |
|
||||||
|
| **Folder** | [`Framework/Interfaces/`] |
|
||||||
|
| **Category** | Interaction |
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
|
||||||
|
Doors, drawers, containers, and any object that can be locked/unlocked with a key item or puzzle solution.
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Function | Inputs | Outputs | Description |
|
||||||
|
|----------|--------|---------|-------------|
|
||||||
|
| [`TryLock`] | `LockingItem: Actor` | `bSuccess: Bool` | Attempt to lock the object with a specific item. |
|
||||||
|
| [`TryUnlock`] | `KeyItem: Actor` | `bSuccess: Bool` | Attempt to unlock with a key item. |
|
||||||
|
| [`IsLocked`] | — | `bIsLocked: Bool` | Whether the object is currently locked. |
|
||||||
|
| [`GetRequiredKeyTag`] | — | `RequiredTag: F_GameplayTag` | Returns the gameplay tag of the required key/item. |
|
||||||
|
| [`OnLockStateChanged`] | `bNewLocked: Bool` | — | Event dispatcher-style notification (implemented via dispatchers in the actor). |
|
||||||
|
|
||||||
|
### Communicates With
|
||||||
|
|
||||||
|
| Target | Direction | Why |
|
||||||
|
|--------|-----------|-----|
|
||||||
|
| [`BPC_InventoryComponent`](../04-inventory/01_BPC_InventoryComponent.md) | Calls | Checks if the player has the required key tag |
|
||||||
|
| [`BP_DoorBase`](../03-interaction/02_BP_DoorBase.md) | Implements | Doors use this interface |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## I_UsableItem
|
||||||
|
|
||||||
|
### Asset Details
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Class** | [`I_UsableItem`] |
|
||||||
|
| **Type** | Blueprint Interface |
|
||||||
|
| **Folder** | [`Framework/Interfaces/`] |
|
||||||
|
| **Category** | Inventory |
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
|
||||||
|
Consumable and usable inventory items (health packs, batteries, keys, quest items). Items implement this to define what happens when used from the inventory screen or hotbar.
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Function | Inputs | Outputs | Description |
|
||||||
|
|----------|--------|---------|-------------|
|
||||||
|
| [`UseItem`] | `User: Actor` | `bConsumed: Bool` | Called when the player uses the item from inventory. Returns true if item should be consumed. |
|
||||||
|
| [`CanUseItem`] | `User: Actor` | `bCanUse: Bool`, `BlockReason: Text` | Whether the item can currently be used. |
|
||||||
|
| [`GetUseDuration`] | — | `Duration: Float` | How long the "use" action takes in seconds. |
|
||||||
|
| [`OnItemUsed`] | `User: Actor` | — | Broadcast event after item is used successfully. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## I_Persistable
|
||||||
|
|
||||||
|
### Asset Details
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Class** | [`I_Persistable`] |
|
||||||
|
| **Type** | Blueprint Interface |
|
||||||
|
| **Folder** | [`Framework/Interfaces/`] |
|
||||||
|
| **Category** | Save System |
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
|
||||||
|
**Save-System Awareness** contract. Every actor that needs to persist state (position, health, inventory contents, lock state, narrative flags) must implement this interface. The save system scans the world for `I_Persistable` implementors and calls these functions.
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Function | Inputs | Outputs | Description |
|
||||||
|
|----------|--------|---------|-------------|
|
||||||
|
| [`OnSave`] | — | `SaveData: F_GameplayTagContainer` | Returns the actor's state as a tag container for serialization. |
|
||||||
|
| [`OnLoad`] | `SaveData: F_GameplayTagContainer` | `bSuccess: Bool` | Restores actor state from saved data. Returns false if load failed. |
|
||||||
|
| [`GetSaveTag`] | — | `SaveTag: F_GameplayTag` | Returns a unique gameplay tag identifying this actor for save/load lookup. |
|
||||||
|
| [`NeedsSave`] | — | `bNeedsSave: Bool` | Whether this actor currently has unsaved changes. |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[AutoSave Triggered]
|
||||||
|
└─► SS_SaveSystem/GS iterates all I_Persistable actors
|
||||||
|
└─► Calls GetSaveTag for unique ID
|
||||||
|
└─► Calls OnSave to get state data
|
||||||
|
└─► Serializes tag + data to save file
|
||||||
|
|
||||||
|
[Game Loaded]
|
||||||
|
└─► SS_SaveSystem reads save file
|
||||||
|
└─► Finds actors by GetSaveTag
|
||||||
|
└─► Calls OnLoad with saved data
|
||||||
|
```
|
||||||
|
|
||||||
|
### Communicates With
|
||||||
|
|
||||||
|
| Target | Direction | Why |
|
||||||
|
|--------|-----------|-----|
|
||||||
|
| [`SS_SaveSystem`](../04-save/02_SS_SaveSystem.md) | Called by | Save system iterates and invokes |
|
||||||
|
| [`GS_CoreGameState`](06_GS_CoreGameState.md) | Called by | GameState can trigger save sweeps |
|
||||||
|
| Any world actor | Implements | Doors, items, enemies, narrative triggers |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## I_Toggleable
|
||||||
|
|
||||||
|
### Asset Details
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Class** | [`I_Toggleable`] |
|
||||||
|
| **Type** | Blueprint Interface |
|
||||||
|
| **Folder** | [`Framework/Interfaces/`] |
|
||||||
|
| **Category** | Interaction |
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
|
||||||
|
Objects with binary states: on/off, open/closed, active/inactive. Lights, doors, buttons, switches, and machines implement this interface.
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Function | Inputs | Outputs | Description |
|
||||||
|
|----------|--------|---------|-------------|
|
||||||
|
| [`Toggle`] | `Instigator: Actor` | `bNewState: Bool` | Toggles the object to its opposite state. Returns the new state. |
|
||||||
|
| [`SetState`] | `Instigator: Actor`, `bNewState: Bool` | `bSuccess: Bool` | Sets the object to a specific state. |
|
||||||
|
| [`GetCurrentState`] | — | `bState: Bool` | Returns the current state. |
|
||||||
|
| [`GetStateLabel`] | — | `StateText: Text` | Human-readable label for the current state (e.g. "Open" / "Closed"). |
|
||||||
|
| [`OnStateChanged`] | `bNewState: Bool`, `Instigator: Actor` | — | Event dispatched when state changes (for animation/delegation). |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## I_Adjustable
|
||||||
|
|
||||||
|
### Asset Details
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Class** | [`I_Adjustable`] |
|
||||||
|
| **Type** | Blueprint Interface |
|
||||||
|
| **Folder** | [`Framework/Interfaces/`] |
|
||||||
|
| **Category** | Interaction |
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
|
||||||
|
Objects with continuous adjustment values rather than binary states. Pressure valves, dials, sliders, tuning knobs, and analog controls.
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Function | Inputs | Outputs | Description |
|
||||||
|
|----------|--------|---------|-------------|
|
||||||
|
| [`Adjust`] | `Delta: Float`, `Instigator: Actor` | `bSuccess: Bool` | Adjusts the value by delta. Positive = increase, negative = decrease. |
|
||||||
|
| [`SetValue`] | `NewValue: Float`, `Instigator: Actor` | `bSuccess: Bool` | Sets the value directly. |
|
||||||
|
| [`GetCurrentValue`] | — | `CurrentValue: Float` | Returns current value. |
|
||||||
|
| [`GetMinValue`] | — | `MinValue: Float` | Minimum allowed value. |
|
||||||
|
| [`GetMaxValue`] | — | `MaxValue: Float` | Maximum allowed value. |
|
||||||
|
| [`OnValueChanged`] | `NewValue: Float`, `Instigator: Actor` | — | Event dispatched when value changes (for visual/audio feedback). |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Communication Overview
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────────────┐
|
||||||
|
│ Any Blueprint │
|
||||||
|
│ (Player Controller, Widget, AI, Level Blueprint, etc.) │
|
||||||
|
└──────────┬───────────────────────────────────────────────┘
|
||||||
|
│ Calls Interface Function
|
||||||
|
▼
|
||||||
|
┌──────────────────────┐
|
||||||
|
│ Interface Function │ (e.g. OnInteract, Toggle, TakeDamage)
|
||||||
|
└──────────┬───────────┘
|
||||||
|
│ Dispatched to implementor
|
||||||
|
▼
|
||||||
|
┌────────────────────────────────────┐
|
||||||
|
│ Actor / Component implementing │
|
||||||
|
│ the interface │
|
||||||
|
│ (e.g. BP_DoorBase, BP_HealthPack)│
|
||||||
|
└────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
│ May call back to other systems
|
||||||
|
▼
|
||||||
|
┌──────────────────────────────┐
|
||||||
|
│ Other systems via interface │
|
||||||
|
│ or event dispatcher │
|
||||||
|
└──────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reuse Notes
|
||||||
|
|
||||||
|
- All interfaces are minimal — each function has a single clear responsibility.
|
||||||
|
- Implementors should **never** call interface functions on themselves. Callers should always query for the interface using `Does Implement Interface` or `Cast to Interface` nodes.
|
||||||
|
- New interfaces should be added here by following the same pattern: define functions, default return values (typically `false` or `0.0f`), and clear documentation.
|
||||||
|
- If a system needs additional interface functions, consider creating a child interface or extending the existing one. Prefer child interfaces to keep base interfaces stable.
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
1. An actor with [`I_Interactable`] implemented responds to `OnInteract` when detected by the interaction system.
|
||||||
|
2. An actor with [`I_Persistable`] saves and restores its state correctly through the save system.
|
||||||
|
3. An actor can implement multiple interfaces simultaneously without conflicts.
|
||||||
|
4. Calling an interface function on an actor that doesn't implement it safely returns the default value (no crash).
|
||||||
|
5. All interface functions are accessible via the Blueprint node context menu under their category name.
|
||||||
217
docs/blueprints/01-core/04_GI_GameFramework.md
Normal file
217
docs/blueprints/01-core/04_GI_GameFramework.md
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
# 04 — Game Instance Kernel (`GI_GameFramework`)
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
The single persistent object that lives for the entire application session. It owns global subsystems, manages save slot ownership, and provides the canonical entry point for service resolution.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **Requires:** GameplayTags plugin enabled, all `SS_` Subsystems
|
||||||
|
- **Required By:** `GM_CoreGameMode`, `WBP_HUDController`, `WBP_MenuFlowController`, any system that resolves a subsystem
|
||||||
|
- **Engine/Plugin Requirements:** GameplayTags, DeveloperSettings
|
||||||
|
|
||||||
|
## Class Info
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Parent Class** | `GameInstance` |
|
||||||
|
| **Class Type** | Blueprint (BlueprintType) |
|
||||||
|
| **Asset Path** | `Content/Framework/Core/GI_GameFramework` |
|
||||||
|
| **Implements Interfaces** | None |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Enums
|
||||||
|
|
||||||
|
### `E_GamePhase`
|
||||||
|
| Value | Display Name | Description |
|
||||||
|
|-------|-------------|-------------|
|
||||||
|
| `MainMenu` = 0 | Main Menu | Game at main menu, no session active |
|
||||||
|
| `Loading` = 1 | Loading | Transition between levels or phases |
|
||||||
|
| `InGame` = 2 | In Game | Normal gameplay active |
|
||||||
|
| `Paused` = 3 | Paused | Game logic paused via pause menu or system |
|
||||||
|
| `Cutscene` = 4 | Cutscene | Sequencer-driven cutscene playing |
|
||||||
|
| `DeathLoop` = 5 | Death Loop | Player died and death loop is active |
|
||||||
|
| `AltDeathSpace` = 6 | Alt Death Space | Alternate death/purgatory space active |
|
||||||
|
| `Credits` = 7 | Credits | End credits playing |
|
||||||
|
| `PostGame` = 8 | Post Game | After credits, meta/post-game state |
|
||||||
|
|
||||||
|
### `E_PlatformType`
|
||||||
|
| Value | Display Name | Description |
|
||||||
|
|-------|-------------|-------------|
|
||||||
|
| `Steam` = 0 | Steam | Steam platform services |
|
||||||
|
| `PlayStation` = 1 | PlayStation | PlayStation platform services |
|
||||||
|
| `Xbox` = 2 | Xbox | Xbox platform services |
|
||||||
|
| `Switch` = 3 | Nintendo Switch | Nintendo platform services |
|
||||||
|
| `Generic` = 4 | Generic | Fallback for unknown/unlisted platforms |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Structs
|
||||||
|
|
||||||
|
*No structs defined in this class. All data is stored as direct variables or via subsystems.*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
|
||||||
|
### Configuration (Instance Editable, Expose On Spawn)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `ActiveSaveSlotIndex` | `Integer` | `0` | `Save` | Which save slot is currently loaded (0\u2013N) |
|
||||||
|
| `PlatformType` | `E_PlatformType` | `Generic` | `Platform` | Identifies the current platform for service abstraction |
|
||||||
|
| `bFirstLaunch` | `Boolean` | `true` | `Save` | True on first-ever boot; triggers intro flow |
|
||||||
|
|
||||||
|
### Internal (Private / Protected, No Expose)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `SessionFlags` | `Map<GameplayTag, Boolean>` | `Empty Map` | `Session` | Transient flags that do not persist to disk |
|
||||||
|
| `CurrentGamePhase` | `E_GamePhase` | `MainMenu` | `State` | Tracks the top-level game phase for routing |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
### Public Functions
|
||||||
|
|
||||||
|
#### `GetSubsystem` → `Object Reference`
|
||||||
|
- **Description:** Returns the requested subsystem by GameplayTag lookup. This is the canonical service resolver for all systems.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Tag` | `GameplayTag` | The tag identifying which subsystem to return |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
- **Flow:**
|
||||||
|
1. Look up subsystem class reference mapped to the input tag
|
||||||
|
2. Call `GetSubsystem()` with resolved class
|
||||||
|
3. Return the subsystem reference (null if not found)
|
||||||
|
4. If null, log a warning via `FL_GameUtilities.LogDebug`
|
||||||
|
|
||||||
|
#### `SetSessionFlag` → `Void`
|
||||||
|
- **Description:** Sets a transient session flag that does not persist to disk.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Tag` | `GameplayTag` | Flag identifier |
|
||||||
|
| `Value` | `Boolean` | Flag value |
|
||||||
|
- **Flow:** `SessionFlags.Add(Tag, Value)`
|
||||||
|
|
||||||
|
#### `GetSessionFlag` → `Boolean`
|
||||||
|
- **Description:** Reads a transient session flag value.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Tag` | `GameplayTag` | Flag identifier |
|
||||||
|
- **Flow:** Return `SessionFlags.Find(Tag, false)`
|
||||||
|
|
||||||
|
#### `SetActiveSlot` → `Void`
|
||||||
|
- **Description:** Designates the active save slot index. Does not trigger a save or load.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `SlotIndex` | `Integer` | Slot index to set active |
|
||||||
|
|
||||||
|
#### `SetGamePhase` → `Void`
|
||||||
|
- **Description:** Updates `CurrentGamePhase` and broadcasts the `OnGamePhaseChanged` dispatcher. This is the single point of authority for phase transitions.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Phase` | `E_GamePhase` | New phase value |
|
||||||
|
- **Flow:**
|
||||||
|
1. `CurrentGamePhase = Phase`
|
||||||
|
2. Broadcast `OnGamePhaseChanged`
|
||||||
|
|
||||||
|
#### `InitPlatformServices` → `Void`
|
||||||
|
- **Description:** Called during `Init()`; routes to platform-specific service wrappers based on `PlatformType`.
|
||||||
|
- **Flow:**
|
||||||
|
1. Switch on `PlatformType`
|
||||||
|
2. Call platform init stubs (empty in editor)
|
||||||
|
3. Broadcast `OnPlatformReady`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Parameters | Bind Access | Description |
|
||||||
|
|------------|-----------|-------------|-------------|
|
||||||
|
| `OnGamePhaseChanged` | `NewPhase: E_GamePhase` | `Public` | Fired on any phase transition |
|
||||||
|
| `OnPlatformReady` | `PlatformType: E_PlatformType` | `Public` | Fired when platform services confirm ready |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Overridden Events / Custom Events
|
||||||
|
|
||||||
|
### Event: `Init()`
|
||||||
|
- **Description:** Override of `GameInstance.Init()`. Called automatically when the game instance is created.
|
||||||
|
- **Flow:**
|
||||||
|
1. Call parent `Init()`
|
||||||
|
2. Call `InitPlatformServices()`
|
||||||
|
3. All `SS_` Subsystems are automatically initialized by UE (they are GameInstanceSubsystems)
|
||||||
|
4. Load persistent settings via `SS_SettingsSystem.PopulateDefaults()`
|
||||||
|
5. Check existing save data via `SS_SaveSystem.GetSlotManifest()`
|
||||||
|
6. Call `SetGamePhase(MainMenu)`
|
||||||
|
7. Broadcast `OnPlatformReady`
|
||||||
|
|
||||||
|
### Event: `Shutdown()`
|
||||||
|
- **Description:** Override of `GameInstance.Shutdown()`. Called when the game instance is destroyed.
|
||||||
|
- **Flow:**
|
||||||
|
1. Save persistent settings via `SS_SettingsSystem`
|
||||||
|
2. Call parent `Shutdown()`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blueprint Graph Logic Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[GameInstance Init] --> B[Call InitPlatformServices]
|
||||||
|
B --> C{Platform identified?}
|
||||||
|
C -->|Yes| D[Platform init stubs]
|
||||||
|
C -->|No| E[Set PlatformType = Generic]
|
||||||
|
D --> F[SS_SettingsSystem.PopulateDefaults]
|
||||||
|
E --> F
|
||||||
|
F --> G[SS_SaveSystem.GetSlotManifest]
|
||||||
|
G --> H[SetGamePhase MainMenu]
|
||||||
|
H --> I[Broadcast OnGamePhaseChanged]
|
||||||
|
I --> J[Broadcast OnPlatformReady]
|
||||||
|
|
||||||
|
K[Any system calls SetGamePhase] --> L[Update CurrentGamePhase]
|
||||||
|
L --> M[Broadcast OnGamePhaseChanged]
|
||||||
|
M --> N[Receivers: GM_CoreGameMode, WBP_HUDController, etc.]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Communication Matrix
|
||||||
|
|
||||||
|
| Who Talks | How | What Is Sent |
|
||||||
|
|-----------|-----|-------------|
|
||||||
|
| `GI_GameFramework` | `Direct Reference (owns)` | Manages lifecycle of all `SS_` Subsystems |
|
||||||
|
| `GI_GameFramework` | `Dispatcher: OnGamePhaseChanged` | `NewPhase: E_GamePhase` to `GM_CoreGameMode`, `WBP_HUDController` |
|
||||||
|
| `GI_GameFramework` | `Dispatcher: OnPlatformReady` | `PlatformType: E_PlatformType` to all listeners |
|
||||||
|
| Any Blueprint | `Direct GetSubsystem()` | Subsystem reference by GameplayTag lookup |
|
||||||
|
| `GM_CoreGameMode` | `Direct Reference` | Reads `CurrentGamePhase`, calls `SetGamePhase` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Validation / Testing Checklist
|
||||||
|
|
||||||
|
- [ ] `Init()` fires without errors and all SS_ Subsystems are created
|
||||||
|
- [ ] `SetGamePhase()` updates `CurrentGamePhase` and fires `OnGamePhaseChanged`
|
||||||
|
- [ ] `GetSubsystem()` returns correct subsystem for each registered tag
|
||||||
|
- [ ] `GetSubsystem()` returns null and logs warning for unknown tags
|
||||||
|
- [ ] `SetSessionFlag()` and `GetSessionFlag()` round-trip correctly
|
||||||
|
- [ ] `SetActiveSlot()` updates slot index without side effects
|
||||||
|
- [ ] `InitPlatformServices()` routes to correct platform stub
|
||||||
|
- [ ] `bFirstLaunch` stays true until explicitly cleared by onboarding flow
|
||||||
|
- [ ] Edge case: calling `SetGamePhase()` with same value does not fire dispatcher (if desired)
|
||||||
|
- [ ] Edge case: `GetSubsystem()` during `PreInit()` returns null (graceful)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Reuse Notes
|
||||||
|
|
||||||
|
- Replace `E_PlatformType` routing with new platform entries for additional platforms.
|
||||||
|
- Add new subsystems to the init list; they are auto-initialized by UE's GameInstanceSubsystem system.
|
||||||
|
- The `SessionFlags` map lets you add per-project transient state without modifying this class.
|
||||||
|
- For multi-world / split-screen, treat this as a single-instance singleton per application session.
|
||||||
|
- `PlatformType` can be overridden via command-line switch `-Platform=Steam` in packaging scripts.
|
||||||
181
docs/blueprints/01-core/05_GM_CoreGameMode.md
Normal file
181
docs/blueprints/01-core/05_GM_CoreGameMode.md
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
# GM_CoreGameMode — Core GameMode
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Asset Details
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|-------|-------|
|
||||||
|
| **Asset Type** | Blueprint Class |
|
||||||
|
| **Parent Class** | GameModeBase |
|
||||||
|
| **Folder** | Framework/Core/ |
|
||||||
|
| **UE Version** | 5.5–5.7 |
|
||||||
|
| **Prefix** | GM_ |
|
||||||
|
| **Icon** | GameMode icon |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Sets the rules of the game session: which pawn, controller, player state, HUD, and game state classes to use. Manages level-phase transitions, win/loss routing, and chapter transitions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
| Dependency | Type | Why |
|
||||||
|
|------------|------|-----|
|
||||||
|
| [`GI_GameFramework`](01-core/04_GI_GameFramework.md) | Direct reference | Phase transition coordination |
|
||||||
|
| [`GS_CoreGameState`](01-core/06_GS_CoreGameState.md) | Owned/managed | Session state store |
|
||||||
|
| [`PC_CoreController`](../02-player/08_PC_CoreController.md) | Spawned | Default player controller |
|
||||||
|
| [`BPC_DeathHandlingSystem`](../08-saveload/23_BPC_DeathHandlingSystem.md) | Dispatcher (receives) | Receives death event |
|
||||||
|
| [`SS_SaveSystem`](../08-saveload/21_SS_SaveSystem.md) | Direct | Checkpoint load on respawn |
|
||||||
|
| [`BPC_NarrativeSystem`](../06-narrative/37_BPC_NarrativeSystem.md) | Interface | Chapter start/end hooks |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Enums
|
||||||
|
|
||||||
|
No enums defined on this class directly. References [`E_GamePhase`](01-core/04_GI_GameFramework.md) from [`GI_GameFramework`](01-core/04_GI_GameFramework.md) and [`E_DeathCause`](../05-weapons/18_BPC_DeathCauseTracker.md) from death handling.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Structs
|
||||||
|
|
||||||
|
None. All data is primitive or class references.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
| Name | Type | Description | Access |
|
||||||
|
|------|------|-------------|--------|
|
||||||
|
| `DefaultPawnClass` | Class Reference | GASP-based player pawn class | Public |
|
||||||
|
| `DefaultControllerClass` | Class Reference | [`PC_CoreController`](../02-player/08_PC_CoreController.md) | Public |
|
||||||
|
| `DefaultPlayerStateClass` | Class Reference | [`PS_CorePlayerState`](../02-player/09_PS_CorePlayerState.md) | Public |
|
||||||
|
| `DefaultGameStateClass` | Class Reference | [`GS_CoreGameState`](01-core/06_GS_CoreGameState.md) | Public |
|
||||||
|
| `DefaultHUDClass` | Class Reference | [`WBP_HUDController`](../05-ui/31_WBP_HUDController.md) | Public |
|
||||||
|
| `bPauseAllowed` | Boolean | Runtime flag — disables pause during scripted moments | Public |
|
||||||
|
| `CurrentChapterTag` | GameplayTag | Active chapter/level identifier | Public |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Functions / Events
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `TransitionToChapter` | ChapterTag: GameplayTag | — | Loads next level, sets chapter tag, triggers narrative hooks |
|
||||||
|
| `TriggerGameOver` | Cause: E_DeathCause | — | Routes to death handler or alt death space |
|
||||||
|
| `TriggerEnding` | EndingTag: GameplayTag | — | Passes ending tag to [`BPC_EndingAccumulatorSystem`](../06-narrative/42_BPC_EndingAccumulatorSystem.md) |
|
||||||
|
| `SetPauseAllowed` | bAllowed: Boolean | — | Enables/disables pause permission |
|
||||||
|
| `HandlePlayerDead` | — | — | Called by [`BPC_DeathHandlingSystem`](../08-saveload/23_BPC_DeathHandlingSystem.md); routes respawn or game-over |
|
||||||
|
| `SetChapterTag` | ChapterTag: GameplayTag | — | Updates CurrentChapterTag and syncs to [`GS_CoreGameState`](01-core/06_GS_CoreGameState.md) |
|
||||||
|
| `GetCurrentChapter` | — | GameplayTag | Returns CurrentChapterTag |
|
||||||
|
|
||||||
|
### Function Details
|
||||||
|
|
||||||
|
#### `TransitionToChapter`
|
||||||
|
|
||||||
|
```
|
||||||
|
Input: ChapterTag (GameplayTag)
|
||||||
|
Output: none
|
||||||
|
Flow:
|
||||||
|
└─► Set ChapterTag on GM
|
||||||
|
└─► Set GamePhase(Loading) on GI_GameFramework
|
||||||
|
└─► OpenLevel / StreamLevel by ChapterTag
|
||||||
|
└─► On level loaded:
|
||||||
|
├─► Sync chapter to GS_CoreGameState
|
||||||
|
├─► Fire BPC_NarrativeSystem.OnChapterStart
|
||||||
|
└─► SetGamePhase(InGame)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `HandlePlayerDead`
|
||||||
|
|
||||||
|
```
|
||||||
|
Input: none (triggered by dispatcher)
|
||||||
|
Output: none
|
||||||
|
Flow:
|
||||||
|
└─► bPauseAllowed = false
|
||||||
|
└─► SetGamePhase(DeathLoop) on GI_GameFramework
|
||||||
|
└─► Decision:
|
||||||
|
├─► Alt death space configured? → TriggerEnding(AltDeathSpace)
|
||||||
|
└─► No → SetGamePhase(Loading)
|
||||||
|
→ SS_SaveSystem.LoadCheckpoint()
|
||||||
|
→ On load → BPC_PlayerRespawnSystem.Respawn()
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired When |
|
||||||
|
|------|-----------|------------|
|
||||||
|
| `OnChapterTransition` | NewChapterTag: GameplayTag | Before level loads for chapter transition |
|
||||||
|
| `OnGameOverTriggered` | Cause: E_DeathCause | Game-over sequence begins |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Blueprint Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[BeginPlay / Init] --> B[Set Default Classes]
|
||||||
|
B --> C{Has Save Data?}
|
||||||
|
C -->|Yes| D[SS_SaveSystem.LoadFromSlot]
|
||||||
|
C -->|No| E[Start New Game]
|
||||||
|
D --> F[OnLoadComplete -> SetGamePhase InGame]
|
||||||
|
E --> G[TransitionToChapter first chapter]
|
||||||
|
|
||||||
|
H[Player Dies] --> I[BPC_DeathHandlingSystem dispatches]
|
||||||
|
I --> J[HandlePlayerDead]
|
||||||
|
J --> K{Alt Death Space?}
|
||||||
|
K -->|Yes| L[SetGamePhase AltDeathSpace]
|
||||||
|
K -->|No| M[SetGamePhase Loading]
|
||||||
|
M --> N[SS_SaveSystem.LoadCheckpoint]
|
||||||
|
N --> O[Respawn Player]
|
||||||
|
O --> P[SetGamePhase InGame]
|
||||||
|
|
||||||
|
Q[Chapter Trigger] --> R[TransitionToChapter]
|
||||||
|
R --> S[SetGamePhase Loading]
|
||||||
|
S --> T[Load Chapter Level]
|
||||||
|
T --> U[Update GS_CoreGameState]
|
||||||
|
U --> V[SetGamePhase InGame]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Communication Matrix
|
||||||
|
|
||||||
|
| Target System | Method | Direction | Why |
|
||||||
|
|---------------|--------|-----------|-----|
|
||||||
|
| [`GI_GameFramework`](01-core/04_GI_GameFramework.md) | Direct reference | Both ways | Phase transitions, active slot index |
|
||||||
|
| [`GS_CoreGameState`](01-core/06_GS_CoreGameState.md) | Direct reference | Write | Sync chapter tag and objective state |
|
||||||
|
| [`BPC_DeathHandlingSystem`](../08-saveload/23_BPC_DeathHandlingSystem.md) | Dispatcher | Inbound | Receives OnPlayerDied event |
|
||||||
|
| [`SS_SaveSystem`](../08-saveload/21_SS_SaveSystem.md) | Direct reference | Outbound | Load checkpoint on respawn |
|
||||||
|
| [`BPC_NarrativeSystem`](../06-narrative/37_BPC_NarrativeSystem.md) | Interface call | Outbound | Chapter start/end narrative hooks |
|
||||||
|
| [`PC_CoreController`](../02-player/08_PC_CoreController.md) | Direct spawn | Create | Default controller class |
|
||||||
|
| All menu widgets | Phase dispatcher | Outbound | Phase transitions via GI |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Validation Checklist
|
||||||
|
|
||||||
|
- [ ] All default class references point to valid Blueprint classes
|
||||||
|
- [ ] `TransitionToChapter` correctly loads the level associated with the chapter tag
|
||||||
|
- [ ] `TriggerGameOver` properly freezes input and shows game-over screen
|
||||||
|
- [ ] `HandlePlayerDead` correctly routes to checkpoint load or alt death space
|
||||||
|
- [ ] `bPauseAllowed` is respected by pause menu widget
|
||||||
|
- [ ] Chapter transitions trigger narrative system hooks
|
||||||
|
- [ ] Player respawn restores proper game state
|
||||||
|
- [ ] No hardcoded references — all class selectors are exposed as variables
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Reuse Notes
|
||||||
|
|
||||||
|
- **Override** default class references per project (different pawn, different controller)
|
||||||
|
- **`TransitionToChapter`** can use level streaming or full level load — configure via project settings
|
||||||
|
- **`TriggerEnding`** is the only project-specific hook — override to handle unique ending sequences
|
||||||
|
- **`bPauseAllowed`** can be extended to a tag-based system for finer control
|
||||||
|
- For multiplayer, extend to derive from a replicated GameMode base
|
||||||
|
- The entire class is project-agnostic — drop into any UE5 project and configure class references
|
||||||
270
docs/blueprints/01-core/06_GS_CoreGameState.md
Normal file
270
docs/blueprints/01-core/06_GS_CoreGameState.md
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
# 06 — Core GameState (`GS_CoreGameState`)
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
The single replicated game state object visible to all players (and potential future co-op clients). Tracks session-level progression data such as elapsed play time, active chapter, narrative phase, encounter activity, and active objectives.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **Requires:** `GI_GameFramework` (parent `GameStateBase`), `GameplayTag` system
|
||||||
|
- **Required By:** `GM_CoreGameMode`, `BPC_EncounterDirector`, `WBP_HUDController`, `WBP_ObjectiveDisplay`
|
||||||
|
- **Engine/Plugin Requirements:** GameplayTags, Networking (replication stubs)
|
||||||
|
|
||||||
|
## Class Info
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Parent Class** | `GameStateBase` |
|
||||||
|
| **Class Type** | Blueprint (GameState) |
|
||||||
|
| **Asset Path** | `Content/Framework/Core/GS_CoreGameState` |
|
||||||
|
| **Implements Interfaces** | `I_Persistable` (optional – for persisting elapsed time across saves) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Enums
|
||||||
|
|
||||||
|
No enums defined by this class. Enums consumed:
|
||||||
|
- `E_GamePhase` (from `GI_GameFramework`) — for phase sync
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Structs
|
||||||
|
|
||||||
|
No custom structs defined by this class.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
|
||||||
|
### Configuration (Instance Editable, Expose On Spawn)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| (none) | | | | |
|
||||||
|
|
||||||
|
### Internal (Private, No Expose)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `ElapsedPlayTime` | `Float` | `0.0` | Session | Session seconds accumulated across chapters |
|
||||||
|
| `ActiveChapterTag` | `GameplayTag` | `None` | Session | Current story chapter identifier |
|
||||||
|
| `ActiveNarrativePhase` | `GameplayTag` | `None` | Session | Sub-chapter narrative phase tag |
|
||||||
|
| `bEncounterActive` | `Bool` | `false` | Session | Is an AI encounter currently running |
|
||||||
|
| `ActiveObjectiveTags` | `Array of GameplayTag` | `Empty` | Session | Tags of currently active objectives |
|
||||||
|
|
||||||
|
### Replicated (if multiplayer)
|
||||||
|
|
||||||
|
| Variable | Type | Condition | Description |
|
||||||
|
|----------|------|-----------|-------------|
|
||||||
|
| `ElapsedPlayTime` | `Float` | `Replicated Using OnRep_ElapsedPlayTime` | Replicated session time |
|
||||||
|
| `ActiveChapterTag` | `GameplayTag` | `Replicated Using OnRep_ActiveChapterTag` | Replicated chapter tag |
|
||||||
|
| `ActiveNarrativePhase` | `GameplayTag` | `Replicated Using OnRep_ActiveNarrativePhase` | Replicated phase tag |
|
||||||
|
| `bEncounterActive` | `Bool` | `Replicated Using OnRep_EncounterActive` | Replicated encounter flag |
|
||||||
|
| `ActiveObjectiveTags` | `Array of GameplayTag` | `Replicated Using OnRep_ActiveObjectiveTags` | Replicated objective list |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
### Public Functions
|
||||||
|
|
||||||
|
#### `SetElapsedPlayTime` → `void`
|
||||||
|
- **Description:** Sets the elapsed play time (called by time-tick timer).
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `NewTime` | `Float` | New elapsed time in seconds |
|
||||||
|
- **Blueprint Authority:** Server
|
||||||
|
- **Flow:** Update internal variable, broadcast `OnElapsedPlayTimeUpdated`.
|
||||||
|
|
||||||
|
#### `GetElapsedPlayTime` → `Float`
|
||||||
|
- **Description:** Returns current elapsed play time.
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
- **Flow:** Return `ElapsedPlayTime`.
|
||||||
|
|
||||||
|
#### `SetActiveChapter` → `void`
|
||||||
|
- **Description:** Sets the current chapter tag and fires dispatcher.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `ChapterTag` | `GameplayTag` | New chapter identifier |
|
||||||
|
- **Blueprint Authority:** Server
|
||||||
|
- **Flow:** Update variable, broadcast `OnChapterChanged`.
|
||||||
|
|
||||||
|
#### `GetActiveChapter` → `GameplayTag`
|
||||||
|
- **Description:** Returns the active chapter tag.
|
||||||
|
- **Flow:** Return `ActiveChapterTag`.
|
||||||
|
|
||||||
|
#### `SetActiveNarrativePhase` → `void`
|
||||||
|
- **Description:** Sets the sub-chapter narrative phase tag.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `PhaseTag` | `GameplayTag` | New narrative phase |
|
||||||
|
- **Blueprint Authority:** Server
|
||||||
|
- **Flow:** Update variable, broadcast `OnNarrativePhaseChanged`.
|
||||||
|
|
||||||
|
#### `GetActiveNarrativePhase` → `GameplayTag`
|
||||||
|
- **Description:** Returns the active narrative phase tag.
|
||||||
|
- **Flow:** Return `ActiveNarrativePhase`.
|
||||||
|
|
||||||
|
#### `SetEncounterActive` → `void`
|
||||||
|
- **Description:** Sets whether an encounter is currently running.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `bActive` | `Bool` | Encounter active state |
|
||||||
|
- **Blueprint Authority:** Server
|
||||||
|
- **Flow:** Update variable, broadcast `OnEncounterActiveStateChanged`.
|
||||||
|
|
||||||
|
#### `GetEncounterActive` → `Bool`
|
||||||
|
- **Description:** Returns encounter active state.
|
||||||
|
- **Flow:** Return `bEncounterActive`.
|
||||||
|
|
||||||
|
#### `AddActiveObjectiveTag` → `void`
|
||||||
|
- **Description:** Adds an objective tag to the active list.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `ObjectiveTag` | `GameplayTag` | Objective to add |
|
||||||
|
- **Blueprint Authority:** Server
|
||||||
|
- **Flow:** Add to array (if not already present), broadcast `OnObjectiveTagsChanged`.
|
||||||
|
|
||||||
|
#### `RemoveActiveObjectiveTag` → `void`
|
||||||
|
- **Description:** Removes an objective tag from the active list.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `ObjectiveTag` | `GameplayTag` | Objective to remove |
|
||||||
|
- **Blueprint Authority:** Server
|
||||||
|
- **Flow:** Remove from array, broadcast `OnObjectiveTagsChanged`.
|
||||||
|
|
||||||
|
#### `GetActiveObjectiveTags` → `Array of GameplayTag`
|
||||||
|
- **Description:** Returns the array of active objective tags.
|
||||||
|
- **Flow:** Return `ActiveObjectiveTags`.
|
||||||
|
|
||||||
|
### Protected / Private Functions
|
||||||
|
|
||||||
|
#### `OnRep_ElapsedPlayTime` → `void`
|
||||||
|
- **Description:** Replication notification for `ElapsedPlayTime`.
|
||||||
|
- **Flow:** Broadcast `OnElapsedPlayTimeUpdated`.
|
||||||
|
|
||||||
|
#### `OnRep_ActiveChapterTag` → `void`
|
||||||
|
- **Description:** Replication notification for `ActiveChapterTag`.
|
||||||
|
- **Flow:** Broadcast `OnChapterChanged`.
|
||||||
|
|
||||||
|
#### `OnRep_ActiveNarrativePhase` → `void`
|
||||||
|
- **Description:** Replication notification for `ActiveNarrativePhase`.
|
||||||
|
- **Flow:** Broadcast `OnNarrativePhaseChanged`.
|
||||||
|
|
||||||
|
#### `OnRep_EncounterActive` → `void`
|
||||||
|
- **Description:** Replication notification for `bEncounterActive`.
|
||||||
|
- **Flow:** Broadcast `OnEncounterActiveStateChanged`.
|
||||||
|
|
||||||
|
#### `OnRep_ActiveObjectiveTags` → `void`
|
||||||
|
- **Description:** Replication notification for `ActiveObjectiveTags`.
|
||||||
|
- **Flow:** Broadcast `OnObjectiveTagsChanged`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Parameters | Bind Access | Description |
|
||||||
|
|------------|-----------|-------------|-------------|
|
||||||
|
| `OnElapsedPlayTimeUpdated` | `NewTime: Float` | Public | Fires whenever elapsed time changes |
|
||||||
|
| `OnChapterChanged` | `NewChapterTag: GameplayTag` | Public | Fires when the active chapter changes |
|
||||||
|
| `OnNarrativePhaseChanged` | `NewPhaseTag: GameplayTag` | Public | Fires when narrative phase transitions |
|
||||||
|
| `OnEncounterActiveStateChanged` | `bEncounterActive: Bool` | Public | Fires when encounter state toggles |
|
||||||
|
| `OnObjectiveTagsChanged` | `ActiveTags: Array of GameplayTag` | Public | Fires when objective list changes |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Overridden Events / Custom Events
|
||||||
|
|
||||||
|
### Event: `BeginPlay`
|
||||||
|
- **Description:** Initialise game state defaults and bind to `GI_GameFramework.OnGamePhaseChanged` to reset elapsed time on new game.
|
||||||
|
- **Flow:**
|
||||||
|
1. Call parent `BeginPlay`
|
||||||
|
2. Set `ElapsedPlayTime = 0`
|
||||||
|
3. Bind to `GI_GameFramework.OnGamePhaseChanged` dispatcher
|
||||||
|
4. If `I_Persistable` implemented, register with `SS_SaveSystem`
|
||||||
|
|
||||||
|
### Event: `Tick`
|
||||||
|
- **Description:** Accumulate `ElapsedPlayTime` while game phase is `InGame`.
|
||||||
|
- **Flow:**
|
||||||
|
1. Check `GI_GameFramework.CurrentGamePhase == InGame`
|
||||||
|
2. If true: `ElapsedPlayTime += DeltaTime`
|
||||||
|
3. If change exceeds 1 second threshold, broadcast `OnElapsedPlayTimeUpdated`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blueprint Graph Logic Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[BeginPlay] --> B[Init variables to 0 / None]
|
||||||
|
B --> C[Bind to GI_GameFramework.OnGamePhaseChanged]
|
||||||
|
C --> D[Tick - Is GamePhase InGame?]
|
||||||
|
D -->|Yes| E[Accumulate ElapsedPlayTime]
|
||||||
|
D -->|No| F[Skip time accumulation]
|
||||||
|
E --> G[Fire OnElapsedPlayTimeUpdated]
|
||||||
|
G --> H[Wait next Tick]
|
||||||
|
H --> D
|
||||||
|
F --> H
|
||||||
|
|
||||||
|
I[GM_CoreGameMode calls SetActiveChapter] --> J[Update ActiveChapterTag]
|
||||||
|
J --> K[Broadcast OnChapterChanged]
|
||||||
|
K --> L[UI + Narrative systems react]
|
||||||
|
|
||||||
|
M[BPC_EncounterDirector sets EncounterActive] --> N[Update bEncounterActive]
|
||||||
|
N --> O[Broadcast OnEncounterActiveStateChanged]
|
||||||
|
O --> P[HUD shows encounter status]
|
||||||
|
|
||||||
|
Q[BPC_ObjectiveSystem adds/removes objective] --> R[Update ActiveObjectiveTags]
|
||||||
|
R --> S[Broadcast OnObjectiveTagsChanged]
|
||||||
|
S --> T[WBP_ObjectiveDisplay refreshes]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Communication Matrix
|
||||||
|
|
||||||
|
| Who Talks | How | What Is Sent |
|
||||||
|
|-----------|-----|-------------|
|
||||||
|
| `GI_GameFramework` | `OnGamePhaseChanged` Dispatcher | Game phase events |
|
||||||
|
| `GM_CoreGameMode` | Direct method call | `SetActiveChapter`, `SetActiveNarrativePhase` |
|
||||||
|
| `BPC_EncounterDirector` | Direct method call | `SetEncounterActive` |
|
||||||
|
| `BPC_ObjectiveSystem` | Direct method call | `AddActiveObjectiveTag`, `RemoveActiveObjectiveTag` |
|
||||||
|
| `WBP_HUDController` | `OnChapterChanged` / `OnElapsedPlayTimeUpdated` Dispatchers | Chapter and time data |
|
||||||
|
| `WBP_ObjectiveDisplay` | `OnObjectiveTagsChanged` Dispatcher | Active objective list |
|
||||||
|
| `BPC_RunHistoryTracker` | Direct read | `ElapsedPlayTime`, `ActiveObjectiveTags` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Validation / Testing Checklist
|
||||||
|
|
||||||
|
- [ ] `BeginPlay` initialises all variables to correct defaults
|
||||||
|
- [ ] `ElapsedPlayTime` increments only when `GamePhase == InGame`
|
||||||
|
- [ ] `SetActiveChapter` updates variable and fires `OnChapterChanged`
|
||||||
|
- [ ] `SetActiveNarrativePhase` updates variable and fires `OnNarrativePhaseChanged`
|
||||||
|
- [ ] `SetEncounterActive` toggles correctly and fires `OnEncounterActiveStateChanged`
|
||||||
|
- [ ] `AddActiveObjectiveTag` / `RemoveActiveObjectiveTag` modify array and fire dispatcher
|
||||||
|
- [ ] Duplicate objective tags are not added twice
|
||||||
|
- [ ] `OnRep_*` functions fire corresponding dispatchers on clients
|
||||||
|
- [ ] Edge case: rapid chapter changes in the same tick (dispatchers fire correctly)
|
||||||
|
- [ ] Edge case: empty objective list and removal from empty list (no crash)
|
||||||
|
- [ ] Edge case: time accumulation stops correctly on phase change to Paused/Loading
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Reuse Notes
|
||||||
|
|
||||||
|
- This class is intentionally lightweight. It is a singleton per session.
|
||||||
|
- For multiplayer: mark all variables `Replicated Using` with `OnRep` notification functions. Currently stubbed as single-player only.
|
||||||
|
- For save persistence: implement `I_Persistable` to save/restore `ElapsedPlayTime` and `ActiveChapterTag` via `SS_SaveSystem`.
|
||||||
|
- The `ActiveObjectiveTags` array is read-only for UI. All modification happens through `BPC_ObjectiveSystem` which calls `GS_CoreGameState` methods.
|
||||||
|
- `bEncounterActive` is written by `BPC_EncounterDirector` and read by UI and adaptive atmosphere controllers.
|
||||||
|
- No new enums or structs are needed — all data is simple types or `GameplayTag` arrays.
|
||||||
|
- To add new replicated state (e.g. `bIsInCombat`), follow the same pattern: variable + setter + dispatcher + `OnRep`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Blueprint Spec v1.0 — GS_CoreGameState for UE5.5-5.7*
|
||||||
185
docs/blueprints/01-core/07_DA_ItemData.md
Normal file
185
docs/blueprints/01-core/07_DA_ItemData.md
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
# 07 — Item Data Asset (`DA_ItemData`)
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
The single source of truth for every item in the game. Each item is represented by one `DA_ItemData` Primary Data Asset. No item data lives in Blueprint logic; all item definitions are data-driven from these assets.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **Requires:** `GameplayTag` system (`GI_GameTagRegistry`), `UPrimaryDataAsset` (engine base)
|
||||||
|
- **Required By:** `BPC_InventorySystem`, `BPC_EquipmentSlotSystem`, `BPC_ConsumableSystem`, `BPC_AmmoResourceSystem`, `BPC_ItemCombineSystem`, `BPC_KeyItemSystem`, `BPC_ActiveItemSystem`, `DA_ItemDatabase` (collection)
|
||||||
|
- **Engine/Plugin Requirements:** `GameplayTags`, `AssetManager` (Primary Data Asset registration)
|
||||||
|
- **Parent Class:** `UPrimaryDataAsset`
|
||||||
|
|
||||||
|
## Class Info
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Parent Class** | `UPrimaryDataAsset` |
|
||||||
|
| **Class Type** | Blueprint Function Library (Data Asset) |
|
||||||
|
| **Asset Path** | `Content/Data/Items/DA_Item_[Name]` |
|
||||||
|
| **Implements Interfaces** | None |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Enums
|
||||||
|
|
||||||
|
### `E_ItemType`
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Weapon` | Equippable weapon (melee or ranged) |
|
||||||
|
| `Ammo` | Ammunition for weapons |
|
||||||
|
| `Consumable` | Single-use health/stamina/stress item |
|
||||||
|
| `KeyItem` | Story progression gating item (non-droppable) |
|
||||||
|
| `Document` | Readable document / note / file |
|
||||||
|
| `Collectible` | Optional hidden item for completion |
|
||||||
|
| `Tool` | Equipment item (flashlight, lockpick, etc.) |
|
||||||
|
| `Resource` | Crafting / repair material |
|
||||||
|
| `Misc` | Other (quest flags, no-function items) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Structs
|
||||||
|
|
||||||
|
No custom structs defined in this Data Asset class. Consumed structs from other files:
|
||||||
|
|
||||||
|
- `S_EquipmentData` (from `BPC_EquipmentSlotSystem`) — optional, if item is equippable
|
||||||
|
- `S_ConsumableData` (from `BPC_ConsumableSystem`) — optional, if item is consumable
|
||||||
|
- `S_InspectData` (from `BPC_InspectItemSystem`) — optional, if item has 3D inspect model
|
||||||
|
- `S_AmmoData` (from `BPC_AmmoResourceSystem`) — optional, if item is ammo type
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
|
||||||
|
### Instance Editable (Category: "Item Definition")
|
||||||
|
|
||||||
|
| Variable | Type | Default | Description |
|
||||||
|
|----------|------|---------|-------------|
|
||||||
|
| `ItemTag` | `GameplayTag` | `None` | Unique identifier for this item (e.g. `Item.MedKit`) |
|
||||||
|
| `DisplayName` | `FText` | `""` | Player-visible item name |
|
||||||
|
| `Description` | `FText` | `""` | Item description text |
|
||||||
|
| `Icon` | `Texture2D (Soft)` | `None` | Inventory/UI icon |
|
||||||
|
| `WorldMesh` | `StaticMesh (Soft)` | `None` | 3D mesh when dropped in world |
|
||||||
|
| `Weight` | `Float` | `0.0` | Weight units (for carry limit) |
|
||||||
|
| `StackLimit` | `Integer` | `1` | Max stack count in a single inventory slot |
|
||||||
|
| `ItemType` | `E_ItemType` | `Misc` | Item classification |
|
||||||
|
| `bIsKeyItem` | `Bool` | `false` | Protected from drop/consume/clear |
|
||||||
|
| `CombinesWith` | `Array of GameplayTag` | `Empty` | Items this can combine with (item tags) |
|
||||||
|
| `CombineResult` | `GameplayTag` | `None` | Produced item tag on successful combine |
|
||||||
|
| `CustomProperties` | `Map (Name -> String)` | `Empty` | Per-project custom data (any key-value pairs) |
|
||||||
|
|
||||||
|
### Optional Sub-Data (EditCondition / Visible if applicable)
|
||||||
|
|
||||||
|
| Variable | Type | Default | EditCondition | Description |
|
||||||
|
|----------|------|---------|---------------|-------------|
|
||||||
|
| `EquipmentData` | `S_EquipmentData` | Default | `ItemType == Weapon || ItemType == Tool` | Weapon/tool equipment properties |
|
||||||
|
| `ConsumableData` | `S_ConsumableData` | Default | `ItemType == Consumable` | Health/stamina/stress restore values |
|
||||||
|
| `InspectData` | `S_InspectData` | Default | `bHasInspectMode` | 3D inspect rotation anchors and secrets |
|
||||||
|
| `AmmoData` | `S_AmmoData` | Default | `ItemType == Ammo` | Ammo type tag and per-pickup count |
|
||||||
|
|
||||||
|
### Metadata / Editor
|
||||||
|
|
||||||
|
| Variable | Type | Default | Description |
|
||||||
|
|----------|------|---------|-------------|
|
||||||
|
| `bHasInspectMode` | `Bool` | `false` | Whether item supports 3D inspection |
|
||||||
|
| `bCanBeDropped` | `Bool` | `true` | Allow dropping from inventory |
|
||||||
|
| `AssetSearchTags` | `Array of FName` | `Empty` | Editor search keywords |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
No runtime functions. This is a pure data container. The only accessible operations are direct variable reads from Blueprints that hold a reference to the asset.
|
||||||
|
|
||||||
|
### Editor-Only Functions (for content team validation)
|
||||||
|
|
||||||
|
#### `ValidateItemData` → `Bool` (BlueprintCallable, Editor only)
|
||||||
|
- **Description:** Runs editor validation checks (tag uniqueness, required fields filled).
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Flow:**
|
||||||
|
1. Check `ItemTag != None` — if None, log warning
|
||||||
|
2. Check `DisplayName != ""` — if empty, log warning
|
||||||
|
3. If `StackLimit < 1`, reset to 1
|
||||||
|
4. If `bIsKeyItem` then `bCanBeDropped = false` (forced)
|
||||||
|
5. Return true if all validations pass
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
None. Data Assets are passive data containers; they do not fire events.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Overridden Events / Custom Events
|
||||||
|
|
||||||
|
No event overrides. Data Assets do not tick or have BeginPlay.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blueprint Graph Logic Flow
|
||||||
|
|
||||||
|
No blueprint graph. This asset is created and edited in the Content Browser via "Create Advanced Asset -> Blueprint -> Data Asset -> DA_ItemData".
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
A[Content Browser] --> B[Right-click > Data Asset > DA_ItemData]
|
||||||
|
B --> C[Name: DA_Item_MedKit]
|
||||||
|
C --> D[Fill ItemTag: Item.MedKit]
|
||||||
|
D --> E[Fill DisplayName: Med Kit]
|
||||||
|
E --> F[Assign Icon, Mesh]
|
||||||
|
F --> G[Set ItemType: Consumable]
|
||||||
|
G --> H[Fill ConsumableData]
|
||||||
|
H --> I[Save Asset]
|
||||||
|
I --> J[Registered in Asset Manager]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Communication Matrix
|
||||||
|
|
||||||
|
| Who Talks | How | What Is Sent |
|
||||||
|
|-----------|-----|-------------|
|
||||||
|
| `BPC_InventorySystem` | Direct asset reference | Reads `ItemTag`, `DisplayName`, `Weight`, `StackLimit`, `bIsKeyItem`, `Icon` |
|
||||||
|
| `BPC_ConsumableSystem` | Direct asset reference | Reads `ConsumableData` (HealthRestore, StressReduce, etc.) |
|
||||||
|
| `BPC_EquipmentSlotSystem` | Direct asset reference | Reads `EquipmentData` (slot type, allowed types) |
|
||||||
|
| `BPC_AmmoResourceSystem` | Direct asset reference | Reads `AmmoData` (AmmoTypeTag, PerPickupCount) |
|
||||||
|
| `BPC_ItemCombineSystem` | Direct asset reference | Reads `CombinesWith`, `CombineResult` to validate combinations |
|
||||||
|
| `BPC_ActiveItemSystem` | Direct asset reference | Reads `ItemType` to route to correct use system |
|
||||||
|
| `BPC_InspectItemSystem` | Direct asset reference | Reads `InspectData`, `bHasInspectMode` |
|
||||||
|
| `WBP_InventoryMenu` | Via `BPC_InventorySystem` | Reads `DisplayName`, `Description`, `Icon`, `ItemType` for display |
|
||||||
|
| `WBP_InteractionPromptDisplay` | Via `I_Interactable` on world pickup | Reads `DisplayName` for "Pick up [ItemName]" prompt |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Validation / Testing Checklist
|
||||||
|
|
||||||
|
- [ ] Each `DA_ItemData` asset has a unique `ItemTag` (no two items share the same tag)
|
||||||
|
- [ ] `DisplayName` and `Description` are non-empty for all player-facing items
|
||||||
|
- [ ] `Icon` is assigned for all items that appear in inventory UI
|
||||||
|
- [ ] `WorldMesh` is assigned for items that have a world pickup representation
|
||||||
|
- [ ] `StackLimit` >= 1 for all items
|
||||||
|
- [ ] `bIsKeyItem` items have `bCanBeDropped = false`
|
||||||
|
- [ ] Consumable items have `ConsumableData` filled with at least one effect value > 0
|
||||||
|
- [ ] Weapon/Tool items have `EquipmentData` filled with correct slot tag
|
||||||
|
- [ ] Ammo items have `AmmoData` filled with `AmmoTypeTag` and `PerPickupCount > 0`
|
||||||
|
- [ ] Combine items: both sources reference the exact result tag in `CombineResult`
|
||||||
|
- [ ] `CustomProperties` map is empty for framework-defined items; used only for project-specific extensions
|
||||||
|
- [ ] Asset is registered with Primary Asset Manager (label: `Item`)
|
||||||
|
- [ ] Asset path follows convention: `Content/Data/Items/DA_Item_[Name]`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Reuse Notes
|
||||||
|
|
||||||
|
- Create one `DA_ItemData` per item. Name convention: `DA_Item_[ShortName]` (e.g. `DA_Item_MedKit`, `DA_Item_Flashlight`, `DA_Item_KeyCard_Omega`).
|
||||||
|
- All `ItemTag` values must be registered in `GI_GameTagRegistry` before use.
|
||||||
|
- The `CustomProperties` map future-proofs any per-project additions without modifying the base asset class.
|
||||||
|
- For non-stackable items (weapons, key items, tools), set `StackLimit = 1`.
|
||||||
|
- For stacks, choose a sensible `StackLimit` (e.g. ammo = 999, consumables = 5).
|
||||||
|
- The `AssetManager` should be configured with `PrimaryAssetType = Item` and `PrimaryAssetLabel = Item` for async loading support.
|
||||||
|
- Document items (`E_ItemType.Document`) additionally populate `BPC_DocumentArchiveSystem` with their content; the `Description` field serves as document body text.
|
||||||
|
- To add a new item property across all items, add a new variable to `DA_ItemData`. Do not create a separate asset class per item type — use the `ItemType` enum for branching logic.
|
||||||
|
- The `ValidateItemData` editor function can be exposed as a Python command for batch validation on check-in.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Blueprint Spec v1.0 — DA_ItemData for UE5.5-5.7*
|
||||||
340
docs/blueprints/02-player/08_BPC_HealthSystem.md
Normal file
340
docs/blueprints/02-player/08_BPC_HealthSystem.md
Normal file
@@ -0,0 +1,340 @@
|
|||||||
|
# 08 — Health System (`BPC_HealthSystem`)
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
Manages the player's health pool, damage application with type-based resistance, health regeneration, death state, and critical-health events. Serves as the central survivability component on the player character.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **Requires:** `FL_GameUtilities` (for safe interface casts), `I_Damageable` (interface on the owner), `GI_GameFramework` (for GamePhase queries)
|
||||||
|
- **Required By:** `BPC_StressSystem` (health below threshold triggers fear events), `BPC_PlayerMetricsTracker` (death tracking), `WBP_HUD` (health bar), `AI_EnemyController` (player health as awareness trigger), `BPC_InteractionDetector` (low-health checks)
|
||||||
|
- **Engine/Plugin Requirements:** GameplayTags, DeveloperSettings
|
||||||
|
|
||||||
|
## Class Info
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Parent Class** | `ActorComponent` |
|
||||||
|
| **Class Type** | Blueprint Component |
|
||||||
|
| **Asset Path** | `Content/Framework/Player/BPC_HealthSystem` |
|
||||||
|
| **Implements Interfaces** | `I_Damageable` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Enums
|
||||||
|
|
||||||
|
### `E_DamageType`
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Physical = 0` | Bullets, blunt force, falls |
|
||||||
|
| `Arcane = 1` | Magic, curse, psychic damage |
|
||||||
|
| `Fire = 2` | Burning, environmental heat |
|
||||||
|
| `Poison = 3` | Toxins, venom, chemical |
|
||||||
|
| `Fear = 4` | Psychological damage (stress overflow) |
|
||||||
|
| `Environmental = 5` | Traps, crushing, drowning |
|
||||||
|
| `True = 6` | Bypasses all resistances, no reduction |
|
||||||
|
|
||||||
|
### `E_DeathState`
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Alive = 0` | Normal gameplay |
|
||||||
|
| `Dying = 1` | Downed state, waiting for recovery or final death |
|
||||||
|
| `Dead = 2` | Character is dead, no further actions possible |
|
||||||
|
| `PermaDeath = 3` | Save-scrubbing locked, forced reload |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Structs
|
||||||
|
|
||||||
|
### `S_DamageEvent`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `BaseAmount` | `Float` | Raw damage before resistance |
|
||||||
|
| `DamageType` | `E_DamageType` | Category of damage |
|
||||||
|
| `Instigator` | `Actor` | Who caused the damage |
|
||||||
|
| `HitLocation` | `Vector` | World location of impact |
|
||||||
|
| `DamageTags` | `GameplayTagContainer` | Additional contextual tags |
|
||||||
|
| `bIsCriticalHit` | `Boolean` | True if this is a headshot / weak-point hit |
|
||||||
|
| `SourceDescription` | `Text` | Human-readable source (for log / UI) |
|
||||||
|
|
||||||
|
### `S_DamageResistance`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `DamageType` | `E_DamageType` | Type this resistance applies to |
|
||||||
|
| `Multiplier` | `Float` | 1.0 = normal, 0.5 = half damage, 2.0 = double |
|
||||||
|
| `bIsStackable` | `Boolean` | Can this resistance stack with others? |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
|
||||||
|
### Configuration (Instance Editable, Expose On Spawn)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `MaxHealth` | `Float` | `100.0` | `Health Config` | Maximum hit points |
|
||||||
|
| `bRegenEnabled` | `Boolean` | `true` | `Health Config` | Allow passive health regeneration |
|
||||||
|
| `RegenDelay` | `Float` | `3.0` | `Health Config` | Seconds after last damage before regen starts |
|
||||||
|
| `RegenRate` | `Float` | `2.0` | `Health Config` | HP recovered per second during regen |
|
||||||
|
| `RegenDelayUntil` | `Float` | `0.0` | `Health Config` | World time when regen can resume |
|
||||||
|
| `CriticalHealthPercent` | `Float` | `0.25` | `Health Config` | Below this fraction of MaxHealth, fire critical event |
|
||||||
|
| `DefaultResistances` | `Array<S_DamageResistance>` | `[]` | `Health Config` | Base damage resistances for this character |
|
||||||
|
| `bEnableFriendlyFire` | `Boolean` | `false` | `Health Config` | Can the player damage themselves? |
|
||||||
|
|
||||||
|
### Internal (Private / Protected, No Expose)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `CurrentHealth` | `Float` | `100.0` | `Health State` | Current HP, clamped [0..MaxHealth] |
|
||||||
|
| `DeathState` | `E_DeathState` | `Alive` | `Health State` | Current death state |
|
||||||
|
| `ActiveDamageOverTime` | `Map<FName, FTimerHandle>` | `{}` | `Health State` | Active DoT timers keyed by source name |
|
||||||
|
| `InvincibilityTimer` | `FTimerHandle` | `-` | `Health State` | Timer handle for temporary invincibility |
|
||||||
|
| `RegenTimerHandle` | `FTimerHandle` | `-` | `Health State` | Timer handle for regen tick |
|
||||||
|
| `bIsInvincible` | `Boolean` | `false` | `Health State` | True during invincibility window |
|
||||||
|
| `LastDamageInstigator` | `Actor` | `None` | `Health State` | Most recent damage source (for kill credit) |
|
||||||
|
| `LastDamageTime` | `Float` | `0.0` | `Health State` | World time of last damage taken |
|
||||||
|
|
||||||
|
### Replicated (if multiplayer)
|
||||||
|
|
||||||
|
| Variable | Type | Condition | Description |
|
||||||
|
|----------|------|-----------|-------------|
|
||||||
|
| `CurrentHealth` | `Float` | `RepNotify` | Replicated with OnRep handler for UI updates |
|
||||||
|
| `DeathState` | `E_DeathState` | `RepNotify` | Replicated death state |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
### Public Functions
|
||||||
|
|
||||||
|
#### `ApplyDamage` → `S_DamageResult (custom struct)`
|
||||||
|
- **Description:** Applies damage to the health pool after resistance calculation. Returns effective damage and any side effects.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `DamageEvent` | `S_DamageEvent` | The damage event to process |
|
||||||
|
- **Blueprint Authority:** Server (if MP), Any (single-player)
|
||||||
|
- **Flow:**
|
||||||
|
1. Early-out if DeathState is Dead or bIsInvincible
|
||||||
|
2. Calculate effective damage from BaseAmount * type resistances
|
||||||
|
3. If damage > 0: reset RegenDelay timer, update CurrentHealth
|
||||||
|
4. Fire OnDamageTaken dispatcher
|
||||||
|
5. If CurrentHealth <= 0: call `HandleDeath`
|
||||||
|
6. If CurrentHealth <= CriticalHealthPercent: fire OnHealthCritical
|
||||||
|
7. Return damage result struct
|
||||||
|
|
||||||
|
#### `ApplyHealing` → `Float (effective healing)`
|
||||||
|
- **Description:** Adds health up to MaxHealth. Returns the amount actually healed.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `HealAmount` | `Float` | Raw healing value |
|
||||||
|
| `HealTags` | `GameplayTagContainer` | Contextual tags |
|
||||||
|
- **Blueprint Authority:** Server (if MP), Any (single-player)
|
||||||
|
- **Flow:**
|
||||||
|
1. Early-out if DeathState is Dead
|
||||||
|
2. Clamp: `CurrentHealth = FMath::Min(MaxHealth, CurrentHealth + HealAmount)`
|
||||||
|
3. Fire OnHealthChanged
|
||||||
|
4. Return actual heal amount
|
||||||
|
|
||||||
|
#### `GetHealthNormalised` → `Float [0.0 - 1.0]`
|
||||||
|
- **Description:** Returns NormalisedCurrentHealth for UI widgets.
|
||||||
|
- **Flow:** `Return CurrentHealth / MaxHealth`
|
||||||
|
|
||||||
|
#### `SetMaxHealth` → `void`
|
||||||
|
- **Description:** Changes MaxHealth and optionally heals to match new ratio or clamps current.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `NewMax` | `Float` | New maximum health value |
|
||||||
|
| `bMaintainRatio` | `Boolean` | If true, scale CurrentHealth to same ratio |
|
||||||
|
- **Flow:**
|
||||||
|
1. Clamp NewMax to >= 1.0
|
||||||
|
2. If bMaintainRatio: `CurrentHealth = (CurrentHealth / MaxHealth) * NewMax`
|
||||||
|
3. Else: `CurrentHealth = FMath::Min(CurrentHealth, NewMax)`
|
||||||
|
4. Set MaxHealth = NewMax
|
||||||
|
5. Fire OnHealthChanged
|
||||||
|
|
||||||
|
#### `KillInstant` → `void`
|
||||||
|
- **Description:** Bypasses all resistances, immediately sets health to 0 and triggers death.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Instigator` | `Actor` | Who caused the kill |
|
||||||
|
| `DeathReason` | `FText` | Reason displayed on death screen |
|
||||||
|
- **Blueprint Authority:** Server (if MP), Any (single-player)
|
||||||
|
- **Flow:**
|
||||||
|
1. Set CurrentHealth = 0
|
||||||
|
2. Set DeathState = Dying
|
||||||
|
3. Fire OnHealthChanged with CurrentHealth = 0
|
||||||
|
4. Call HandleDeath with bInstant = true
|
||||||
|
|
||||||
|
#### `ApplyDamageOverTime` → `void`
|
||||||
|
- **Description:** Applies periodic damage with tick interval and total duration.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `DamagePerTick` | `Float` | Damage applied each interval |
|
||||||
|
| `TickInterval` | `Float` | Seconds between ticks |
|
||||||
|
| `TotalDuration` | `Float` | Total seconds the DoT lasts |
|
||||||
|
| `DamageType` | `E_DamageType` | Type of each damage tick |
|
||||||
|
| `SourceName` | `FName` | Unique name to prevent stacking same DoT |
|
||||||
|
| `Instigator` | `Actor` | Who applied the DoT |
|
||||||
|
- **Flow:**
|
||||||
|
1. If SourceName already exists in ActiveDamageOverTime, clear existing timer
|
||||||
|
2. Create a Timer by Event with loop = TickInterval
|
||||||
|
3. On each tick: build S_DamageEvent, call ApplyDamage
|
||||||
|
4. After TotalDuration: clear timer, remove from ActiveDamageOverTime
|
||||||
|
|
||||||
|
#### `IsAlive` → `Boolean`
|
||||||
|
- **Flow:** `Return DeathState == Alive`
|
||||||
|
|
||||||
|
#### `AddResistance` → `void`
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `NewResistance` | `S_DamageResistance` | Resistance to add or update |
|
||||||
|
- **Flow:** If resistance for this DamageType exists, update Multiplier; if not, add to array.
|
||||||
|
|
||||||
|
#### `RemoveResistance` → `void`
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `DamageType` | `E_DamageType` | Type to remove |
|
||||||
|
- **Flow:** Remove all entries matching this DamageType from resistance array.
|
||||||
|
|
||||||
|
### Protected / Private Functions
|
||||||
|
|
||||||
|
#### `HandleDeath` → `void`
|
||||||
|
- **Description:** Internal death processing. Sets state, fires dispatchers, notifies GameMode.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `bInstant` | `Boolean` | If true, skip dying state go straight to dead |
|
||||||
|
- **Flow:**
|
||||||
|
1. Set DeathState = (bInstant ? Dead : Dying)
|
||||||
|
2. Clear all timers (regen, DoT, invincibility)
|
||||||
|
3. Fire OnDeath with DeathState
|
||||||
|
4. Call `I_Damageable::OnOwnerDied` on owner if interface exists
|
||||||
|
5. Get `GM_CoreGameMode` via `GetWorld()->GetAuthGameMode()`
|
||||||
|
6. Call `GM_CoreGameMode::HandlePlayerDead(LastDamageInstigator)`
|
||||||
|
|
||||||
|
#### `RegenTick` → `void`
|
||||||
|
- **Description:** Timer callback — applies one tick of regeneration.
|
||||||
|
- **Flow:**
|
||||||
|
1. If CurrentHealth >= MaxHealth: stop regen timer, return
|
||||||
|
2. `CurrentHealth = FMath::Min(MaxHealth, CurrentHealth + RegenRate * TickInterval)`
|
||||||
|
3. Fire OnHealthChanged
|
||||||
|
4. If CurrentHealth >= MaxHealth: stop regen timer
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Parameters | Bind Access | Description |
|
||||||
|
|------------|-----------|-------------|-------------|
|
||||||
|
| `OnHealthChanged` | `float OldHealth`, `float NewHealth`, `float Delta` | `Public` | Fired when health changes by any amount |
|
||||||
|
| `OnDamageTaken` | `S_DamageEvent DamageEvent`, `float EffectiveDamage` | `Public` | Fired each time damage is applied |
|
||||||
|
| `OnHealingReceived` | `float HealAmount`, `GameplayTagContainer HealTags` | `Public` | Fired each time healing is applied |
|
||||||
|
| `OnHealthCritical` | `float CurrentHealth`, `float MaxHealth` | `Public` | Fired when health drops below critical threshold |
|
||||||
|
| `OnDeath` | `E_DeathState DeathState`, `Actor Instigator` | `Public` | Fired when character dies or enters dying state |
|
||||||
|
| `OnDeathStateChanged` | `E_DeathState OldState`, `E_DeathState NewState` | `Public` | Fired on any death state transition |
|
||||||
|
| `OnInvincibilityChanged` | `bool bIsInvincible` | `Public` | Fired when invincibility state toggles |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Overridden Events / Custom Events
|
||||||
|
|
||||||
|
### Event: `BeginPlay`
|
||||||
|
- **Description:** Initializes health, loads saved health if persisting, starts regen timer if enabled.
|
||||||
|
- **Flow:**
|
||||||
|
1. Set CurrentHealth = MaxHealth
|
||||||
|
2. Check if owner implements `I_Persistable`
|
||||||
|
3. If yes, try to load saved health value
|
||||||
|
4. If bRegenEnabled: start regen timer with initial delay = 0
|
||||||
|
5. Bind to any relevant GamePhase change dispatchers
|
||||||
|
|
||||||
|
### Event: `OnComponentDestroyed`
|
||||||
|
- **Description:** Clean up all active timers.
|
||||||
|
- **Flow:**
|
||||||
|
1. Clear all TimerHandles (Invincibility, Regen, all DoT timers)
|
||||||
|
2. Clear ActiveDamageOverTime map
|
||||||
|
|
||||||
|
### Custom Event: `ApplyTemporaryInvincibility`
|
||||||
|
- **Parameters:** `float Duration`
|
||||||
|
- **Description:** Grants brief invincibility, often used after respawn or dodge.
|
||||||
|
- **Flow:**
|
||||||
|
1. Set bIsInvincible = true
|
||||||
|
2. Fire OnInvincibilityChanged(true)
|
||||||
|
3. Start timer for Duration
|
||||||
|
4. On timer end: set bIsInvincible = false, fire OnInvincibilityChanged(false)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blueprint Graph Logic Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[ApplyDamage called] --> B{DeathState == Dead?}
|
||||||
|
B -->|Yes| C[Return 0 damage]
|
||||||
|
B -->|No| D{bIsInvincible?}
|
||||||
|
D -->|Yes| C
|
||||||
|
D -->|No| E[Calculate resistance multiplier]
|
||||||
|
E --> F[EffectiveDamage = Base * Multiplier]
|
||||||
|
F --> G[CurrentHealth -= EffectiveDamage]
|
||||||
|
G --> H[Reset RegenDelay timer]
|
||||||
|
H --> I[Fire OnDamageTaken]
|
||||||
|
I --> J{CurrentHealth <= 0?}
|
||||||
|
J -->|Yes| K[HandleDeath]
|
||||||
|
J -->|No| L{CurrentHealth <= CriticalThreshold?}
|
||||||
|
L -->|Yes| M[Fire OnHealthCritical]
|
||||||
|
L -->|No| N[Fire OnHealthChanged]
|
||||||
|
K --> O[Fire OnDeath dispatcher]
|
||||||
|
K --> P[Notify GameMode: HandlePlayerDead]
|
||||||
|
K --> Q[Clear all timers]
|
||||||
|
M --> N
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Communication Matrix
|
||||||
|
|
||||||
|
| Who Talks | How | What Is Sent |
|
||||||
|
|-----------|-----|-------------|
|
||||||
|
| `BPC_HealthSystem` | `Dispatcher` | `OnHealthChanged` -> `WBP_HUD`, `BPC_StressSystem` |
|
||||||
|
| `BPC_HealthSystem` | `Dispatcher` | `OnDeath` -> `GM_CoreGameMode`, `SS_SaveSystem`, `BPC_PlayerMetricsTracker` |
|
||||||
|
| `BPC_HealthSystem` | `Dispatcher` | `OnDamageTaken` -> `BPC_StressSystem`, `BPC_AdaptiveEnvironment` |
|
||||||
|
| `BPC_HealthSystem` | `Interface` | `I_Damageable.Execute_ApplyDamage` route |
|
||||||
|
| `BPC_HealthSystem` | `Interface` | `I_Persistable` for save/load of CurrentHealth |
|
||||||
|
| `External (Weapons, Traps)` | `Interface` | `I_Damageable -> ApplyDamage` |
|
||||||
|
| `BPC_HealthSystem` | `Direct` | `GM_CoreGameMode.HandlePlayerDead` on death |
|
||||||
|
| `BPC_HealthSystem` | `Subsystem` | `GI_GameFramework.GetGamePhase` for phase-restricted actions |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Validation / Testing Checklist
|
||||||
|
|
||||||
|
- [ ] ApplyDamage with all E_DamageType values produces correct resistance-modified damage
|
||||||
|
- [ ] Health never drops below 0 or exceeds MaxHealth
|
||||||
|
- [ ] Regeneration does not start while RegenDelay timer is active
|
||||||
|
- [ ] Regeneration stops when health reaches MaxHealth
|
||||||
|
- [ ] Death sets DeathState properly and fires OnDeath exactly once
|
||||||
|
- [ ] Temporary invincibility blocks all damage for its duration
|
||||||
|
- [ ] Damage-over-time correctly applies ticks and cleans up timer on death
|
||||||
|
- [ ] Critical health event fires only when crossing below threshold from above
|
||||||
|
- [ ] Edge case: ApplyHealing on a dead character returns 0 with no dispatchers
|
||||||
|
- [ ] Edge case: ApplyDamage with negative BaseAmount is treated as 0
|
||||||
|
- [ ] Edge case: Multiple simultaneous DoT sources with same SourceName do not stack
|
||||||
|
- [ ] All dispatchers are cleaned up on component destroy
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Reuse Notes
|
||||||
|
|
||||||
|
- This component can be used on AI characters and enemy pawns by exposing the same configuration variables.
|
||||||
|
- For enemies, consider disabling regen (bRegenEnabled = false) unless a specific healing mechanic exists.
|
||||||
|
- The resistance system supports temporary buffs via AddResistance / RemoveResistance at runtime.
|
||||||
|
- If implementing a downed / bleeding state, extend E_DeathState with custom values and add a recovery timer check in HandleDeath.
|
||||||
|
- Damage numbers displayed as floating text should bind to OnDamageTaken.
|
||||||
|
- For multiplayer: all ApplyDamage logic runs on Server; clients receive repnotified CurrentHealth and fire local cosmetic dispatchers only.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Blueprint Spec: Health System. Conforms to TEMPLATE.md v1.0 — part of the UE5 Modular Game Framework.*
|
||||||
333
docs/blueprints/02-player/09_BPC_StaminaSystem.md
Normal file
333
docs/blueprints/02-player/09_BPC_StaminaSystem.md
Normal file
@@ -0,0 +1,333 @@
|
|||||||
|
# 09 — Stamina System (`BPC_StaminaSystem`)
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
Manages the player's stamina pool — drain during sprinting, jumping, and special actions; regeneration during idle/walking. Prevents action spamming by enforcing minimum stamina thresholds. Drives exhaustion VFX and audio cues.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **Requires:** `GI_GameFramework` (phase checks), `FL_GameUtilities` (tag queries)
|
||||||
|
- **Required By:** `BPC_MovementStateSystem` (stamina affects max speed), `WBP_HUD` (stamina bar), `BPC_PlayerMetricsTracker` (exhaustion tracking)
|
||||||
|
- **Engine/Plugin Requirements:** GameplayTags, Timers
|
||||||
|
|
||||||
|
## Class Info
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Parent Class** | `ActorComponent` |
|
||||||
|
| **Class Type** | Blueprint Component |
|
||||||
|
| **Asset Path** | `Content/Framework/Player/BPC_StaminaSystem` |
|
||||||
|
| **Implements Interfaces** | None |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Enums
|
||||||
|
|
||||||
|
### `E_ExhaustionState`
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Normal = 0` | Full stamina operation |
|
||||||
|
| `Low = 1` | Below LowThreshold, heavy breathing, slightly reduced regen delay |
|
||||||
|
| `Exhausted = 2` | Below ExhaustedThreshold, cannot sprint, slow regen |
|
||||||
|
|
||||||
|
### `E_StaminaActionType`
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Sprint = 0` | Continuous drain while sprinting |
|
||||||
|
| `Jump = 1` | One-time drain on jump |
|
||||||
|
| `Climb = 2` | Continuous drain while climbing |
|
||||||
|
| `SpecialAttack = 3` | One-time drain for strong melee |
|
||||||
|
| `Dodge = 4` | One-time drain for dodging |
|
||||||
|
| `Environment = 5` | Drain from external sources (cold, poison) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Structs
|
||||||
|
|
||||||
|
### `S_StaminaDrainRate`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `ActionType` | `E_StaminaActionType` | Which action this rate applies to |
|
||||||
|
| `DrainPerSecond` | `Float` | Stamina drained per second (continuous) |
|
||||||
|
| `DrainFlat` | `Float` | Stamina drained once per use (one-shot) |
|
||||||
|
| `MinStamina` | `Float` | Minimum stamina required to perform this action |
|
||||||
|
| `CooldownAfterUse` | `Float` | Seconds before this action can be used again |
|
||||||
|
|
||||||
|
### `S_StaminaRegenSettings`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `RegenRate` | `Float` | Stamina recovered per second |
|
||||||
|
| `RegenDelay` | `Float` | Seconds after last drain before regen begins |
|
||||||
|
| `ExhaustedRegenRate` | `Float` | Slower recovery while exhausted |
|
||||||
|
| `ExhaustedRegenDelay` | `Float` | Longer delay while exhausted |
|
||||||
|
| `RegenBlockedWhileMoving` | `Boolean` | If true, regen pauses while moving above walk speed |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
|
||||||
|
### Configuration (Instance Editable, Expose On Spawn)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `MaxStamina` | `Float` | `100.0` | `Stamina Config` | Maximum stamina pool |
|
||||||
|
| `DefaultDrainRates` | `Array<S_StaminaDrainRate>` | `[]` | `Stamina Config` | Base drain rates for each action type |
|
||||||
|
| `RegenSettings` | `S_StaminaRegenSettings` | `(15.0, 2.0, 5.0, 4.0, true)` | `Stamina Config` | Regeneration behaviour |
|
||||||
|
| `LowThreshold` | `Float` | `0.30` | `Stamina Config` | Fraction of MaxStamina for Low state |
|
||||||
|
| `ExhaustedThreshold` | `Float` | `0.10` | `Stamina Config` | Fraction of MaxStamina for Exhausted state |
|
||||||
|
| `bCanExhaust` | `Boolean` | `true` | `Stamina Config` | Allow exhaustion state |
|
||||||
|
|
||||||
|
### Internal (Private / Protected, No Expose)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `CurrentStamina` | `Float` | `100.0` | `Stamina State` | Current stamina pool |
|
||||||
|
| `ExhaustionState` | `E_ExhaustionState` | `Normal` | `Stamina State` | Current exhaustion tier |
|
||||||
|
| `ActiveDrains` | `Map<E_StaminaActionType, bool>` | `{}` | `Stamina State` | Which drains are currently active |
|
||||||
|
| `RegenTimerHandle` | `FTimerHandle` | `-` | `Stamina State` | Timer for regen ticks |
|
||||||
|
| `DrainTimerHandle` | `FTimerHandle` | `-` | `Stamina State` | Timer for continuous drain ticks |
|
||||||
|
| `LastDrainTime` | `Float` | `0.0` | `Stamina State` | World time of last stamina drain |
|
||||||
|
| `bRegenBlocked` | `Boolean` | `false` | `Stamina State` | True while external block is active |
|
||||||
|
| `ActionCooldownTimers` | `Map<E_StaminaActionType, FTimerHandle>` | `{}` | `Stamina State` | Per-action cooldown timers |
|
||||||
|
|
||||||
|
### Replicated (if multiplayer)
|
||||||
|
|
||||||
|
| Variable | Type | Condition | Description |
|
||||||
|
|----------|------|-----------|-------------|
|
||||||
|
| `CurrentStamina` | `Float` | `RepNotify` | Replicated with OnRep handler for UI updates |
|
||||||
|
| `ExhaustionState` | `E_ExhaustionState` | `RepNotify` | Replicated exhaustion tier |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
### Public Functions
|
||||||
|
|
||||||
|
#### `DrainStamina` → `Boolean (success)`
|
||||||
|
- **Description:** Attempts to drain stamina for a specific action. Returns false if insufficient stamina or cooldown active.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `ActionType` | `E_StaminaActionType` | Which action is being performed |
|
||||||
|
| `OverrideAmount` | `Float` | If > 0, use this instead of the DrainRate config value |
|
||||||
|
- **Blueprint Authority:** Server (if MP), Any (single-player)
|
||||||
|
- **Flow:**
|
||||||
|
1. If ActionCooldownTimers contains ActionType and timer active: return false
|
||||||
|
2. Look up S_StaminaDrainRate for ActionType
|
||||||
|
3. RequiredStamina = DrainRate.MinStamina
|
||||||
|
4. If CurrentStamina < RequiredStamina: return false
|
||||||
|
5. DrainAmount = OverrideAmount > 0 ? OverrideAmount : DrainRate.DrainFlat
|
||||||
|
6. CurrentStamina = FMath::Max(0, CurrentStamina - DrainAmount)
|
||||||
|
7. Start cooldown timer for DrainRate.CooldownAfterUse
|
||||||
|
8. Update ExhaustionState
|
||||||
|
9. Reset regen delay, stop regen if running
|
||||||
|
10. Fire OnStaminaDrained
|
||||||
|
11. If ActionType is continuous: add to ActiveDrains
|
||||||
|
12. Fire OnStaminaChanged
|
||||||
|
13. Return true
|
||||||
|
|
||||||
|
#### `StartContinuousDrain` → `void`
|
||||||
|
- **Description:** Begins draining stamina per second for a continuous action.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `ActionType` | `E_StaminaActionType` | Sprint, Climb, etc. |
|
||||||
|
- **Flow:**
|
||||||
|
1. Add ActionType to ActiveDrains = true
|
||||||
|
2. If DrainTimerHandle not active: start timer with interval 0.1 seconds
|
||||||
|
3. Timer callback: apply (DrainRate.DrainPerSecond * 0.1) to CurrentStamina each tick
|
||||||
|
4. If CurrentStamina <= 0: stop continuous drain, fire OnExhausted
|
||||||
|
|
||||||
|
#### `StopContinuousDrain` → `void`
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `ActionType` | `E_StaminaActionType` | Action to stop draining for |
|
||||||
|
- **Flow:**
|
||||||
|
1. Set ActiveDrains[ActionType] = false
|
||||||
|
2. If no active drains remain: clear DrainTimerHandle
|
||||||
|
3. Start regen delay timer
|
||||||
|
|
||||||
|
#### `RestoreStamina` → `Float (actual restored)`
|
||||||
|
- **Description:** Adds stamina up to MaxStamina.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Amount` | `Float` | Stamina to restore |
|
||||||
|
| `Tags` | `GameplayTagContainer` | Context tags (e.g. Potion, Rest) |
|
||||||
|
- **Flow:**
|
||||||
|
1. Previous = CurrentStamina
|
||||||
|
2. CurrentStamina = FMath::Min(MaxStamina, CurrentStamina + Amount)
|
||||||
|
3. Update ExhaustionState
|
||||||
|
4. Fire OnStaminaRestored
|
||||||
|
5. Fire OnStaminaChanged
|
||||||
|
6. Return CurrentStamina - Previous
|
||||||
|
|
||||||
|
#### `GetStaminaNormalised` → `Float [0.0 - 1.0]`
|
||||||
|
- **Flow:** Return CurrentStamina / MaxStamina
|
||||||
|
|
||||||
|
#### `CanAffordAction` → `Boolean`
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `ActionType` | `E_StaminaActionType` | Action to check |
|
||||||
|
- **Flow:**
|
||||||
|
1. Look up MinStamina for this ActionType
|
||||||
|
2. Return CurrentStamina >= MinStamina AND no active cooldown
|
||||||
|
|
||||||
|
#### `SetMaxStamina` → `void`
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `NewMax` | `Float` | New maximum value |
|
||||||
|
| `bMaintainRatio` | `Boolean` | Scale current stamina to maintain ratio |
|
||||||
|
- **Flow:**
|
||||||
|
1. If bMaintainRatio: CurrentStamina = (CurrentStamina / MaxStamina) * NewMax
|
||||||
|
2. Else: CurrentStamina = FMath::Min(CurrentStamina, NewMax)
|
||||||
|
3. MaxStamina = NewMax
|
||||||
|
4. Update ExhaustionState
|
||||||
|
5. Fire OnStaminaChanged
|
||||||
|
|
||||||
|
#### `BlockRegen` → `void`
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Duration` | `Float` | Seconds to block regen |
|
||||||
|
- **Flow:**
|
||||||
|
1. Set bRegenBlocked = true
|
||||||
|
2. Start timer for Duration
|
||||||
|
3. On timer end: bRegenBlocked = false
|
||||||
|
|
||||||
|
### Protected / Private Functions
|
||||||
|
|
||||||
|
#### `UpdateExhaustionState` → `void`
|
||||||
|
- **Flow:**
|
||||||
|
1. Normalised = CurrentStamina / MaxStamina
|
||||||
|
2. OldState = ExhaustionState
|
||||||
|
3. If Normalised <= ExhaustedThreshold: NewState = Exhausted
|
||||||
|
4. Else if Normalised <= LowThreshold: NewState = Low
|
||||||
|
5. Else: NewState = Normal
|
||||||
|
6. If NewState != OldState:
|
||||||
|
- ExhaustionState = NewState
|
||||||
|
- Fire OnExhaustionStateChanged
|
||||||
|
- If NewState == Exhausted: fire OnExhausted
|
||||||
|
|
||||||
|
#### `RegenTick` → `void`
|
||||||
|
- **Flow:**
|
||||||
|
1. If bRegenBlocked: return
|
||||||
|
2. Rate = ExhaustionState == Exhausted ? RegenSettings.ExhaustedRegenRate : RegenSettings.RegenRate
|
||||||
|
3. CurrentStamina = FMath::Min(MaxStamina, CurrentStamina + Rate * 0.1)
|
||||||
|
4. Fire OnStaminaChanged
|
||||||
|
5. If CurrentStamina >= MaxStamina: stop regen timer, fire OnStaminaFullyRestored
|
||||||
|
|
||||||
|
#### `OnMovementStateChanged (listener)` → `void`
|
||||||
|
- **Flow:**
|
||||||
|
1. Get movement mode from character
|
||||||
|
2. If RegenSettings.RegenBlockedWhileMoving and movement speed > walk:
|
||||||
|
- Pause regen timer
|
||||||
|
3. Else: resume regen timer if conditions met
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Parameters | Bind Access | Description |
|
||||||
|
|------------|-----------|-------------|-------------|
|
||||||
|
| `OnStaminaChanged` | `float OldStamina`, `float NewStamina`, `float Delta` | `Public` | Fired on any stamina change |
|
||||||
|
| `OnStaminaDrained` | `E_StaminaActionType ActionType`, `float Amount` | `Public` | Fired when stamina is consumed |
|
||||||
|
| `OnStaminaRestored` | `float Amount`, `GameplayTagContainer Tags` | `Public` | Fired when stamina is gained |
|
||||||
|
| `OnStaminaFullyRestored` | `none` | `Public` | Fired when stamina reaches MaxStamina |
|
||||||
|
| `OnExhaustionStateChanged` | `E_ExhaustionState OldState`, `E_ExhaustionState NewState` | `Public` | Fired on any exhaustion tier change |
|
||||||
|
| `OnExhausted` | `none` | `Public` | Fired when entering Exhausted state |
|
||||||
|
| `OnActionCooldownStarted` | `E_StaminaActionType ActionType`, `float Cooldown` | `Public` | Fired when a per-action cooldown begins |
|
||||||
|
| `OnActionCooldownEnded` | `E_StaminaActionType ActionType` | `Public` | Fired when a per-action cooldown expires |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Overridden Events / Custom Events
|
||||||
|
|
||||||
|
### Event: `BeginPlay`
|
||||||
|
- **Description:** Initialises stamina, starts regen if enabled, binds to movement state changes.
|
||||||
|
- **Flow:**
|
||||||
|
1. CurrentStamina = MaxStamina
|
||||||
|
2. Start regen timer with initial delay = 0
|
||||||
|
3. Find BPC_MovementStateSystem on owner, bind to its OnMovementStateChanged dispatcher
|
||||||
|
4. Initialise ExhaustionState = Normal
|
||||||
|
|
||||||
|
### Event: `OnComponentDestroyed`
|
||||||
|
- **Description:** Clean up all timers.
|
||||||
|
- **Flow:**
|
||||||
|
1. Clear RegenTimerHandle, DrainTimerHandle
|
||||||
|
2. Clear all ActionCooldownTimers
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blueprint Graph Logic Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[DrainStamina called] --> B{CanAffordAction?}
|
||||||
|
B -->|No| C[Return false]
|
||||||
|
B -->|Yes| D[Apply drain amount]
|
||||||
|
D --> E[Update CurrentStamina]
|
||||||
|
E --> F{ExhaustedThreshold crossed?}
|
||||||
|
F -->|Yes| G[Set Exhausted state]
|
||||||
|
F -->|No| H{LowThreshold crossed?}
|
||||||
|
H -->|Yes| I[Set Low state]
|
||||||
|
H -->|No| J[Keep Normal state]
|
||||||
|
G --> K[Fire OnExhausted]
|
||||||
|
K --> L[Stop continuous drains]
|
||||||
|
I --> M[Fire OnExhaustionStateChanged]
|
||||||
|
J --> M
|
||||||
|
L --> M
|
||||||
|
M --> N[Start regen delay]
|
||||||
|
N --> O[Fire OnStaminaChanged]
|
||||||
|
O --> P[Return true]
|
||||||
|
D --> Q{Continuous action?}
|
||||||
|
Q -->|Yes| R[Start DrainTimer loop]
|
||||||
|
Q -->|No| S[Start action cooldown]
|
||||||
|
R --> O
|
||||||
|
S --> O
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Communication Matrix
|
||||||
|
|
||||||
|
| Who Talks | How | What Is Sent |
|
||||||
|
|-----------|-----|-------------|
|
||||||
|
| `BPC_StaminaSystem` | `Dispatcher` | `OnStaminaChanged` -> `WBP_HUD` (stamina bar update) |
|
||||||
|
| `BPC_StaminaSystem` | `Dispatcher` | `OnExhausted` -> `BPC_PlayerController` (player feedback), `BPC_AdaptiveEnvironment` |
|
||||||
|
| `BPC_StaminaSystem` | `Dispatcher` | `OnExhaustionStateChanged` -> `ABP_GASP` (breathing animation) |
|
||||||
|
| `External (PC_PlayerController)` | `Direct` | Calls `DrainStamina(Sprint)` / `StartContinuousDrain` on input |
|
||||||
|
| `BPC_StaminaSystem` | `Dispatcher` | `OnStaminaFullyRestored` -> `WBP_HUD` (hide bar) |
|
||||||
|
| `BPC_StaminaSystem` | `Listener` | Binds to `BPC_MovementStateSystem.OnMovementStateChanged` for regen blocking |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Validation / Testing Checklist
|
||||||
|
|
||||||
|
- [ ] DrainStamina deducts correct amount and returns false when stamina is insufficient
|
||||||
|
- [ ] Continuous drain stops when stamina reaches 0
|
||||||
|
- [ ] Regen does not start until RegenDelay has passed
|
||||||
|
- [ ] Exhausted state correctly uses ExhaustedRegenRate and ExhaustedRegenDelay
|
||||||
|
- [ ] Exhaustion state transitions are one-way downward until full rest
|
||||||
|
- [ ] Per-action cooldowns prevent spamming dodge/jump
|
||||||
|
- [ ] BlockRegen pauses all regen for its duration
|
||||||
|
- [ ] RegenBlockedWhileMoving pauses regen at high speed, resumes when walking
|
||||||
|
- [ ] Edge case: MaxStamina change with bMaintainRatio=true preserves percentage
|
||||||
|
- [ ] Edge case: Multiple simultaneous continuous drains stack correctly
|
||||||
|
- [ ] Edge case: DrainStamina called during active cooldown returns false without state change
|
||||||
|
- [ ] All timers cleaned up on component destroy
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Reuse Notes
|
||||||
|
|
||||||
|
- Can be placed on AI characters with simplified config (e.g. only Sprint drain, no Exhaustion).
|
||||||
|
- For enemy stamina, consider removing the exhaustion mechanic and using only drain/restore.
|
||||||
|
- The drain rate map supports runtime modification for buffs/debuffs (e.g. Adrenaline reduces sprint cost).
|
||||||
|
- UI widgets should bind to OnStaminaChanged for smooth bar animation, not tick polling.
|
||||||
|
- For multiplayer: Server authorises all DrainStamina calls; client predicts and corrects on repnotify.
|
||||||
|
- Exhaustion state can drive conditional audio: heavy breathing, heart-beat, fatigue voice lines.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Blueprint Spec: Stamina System. Conforms to TEMPLATE.md v1.0 — part of the UE5 Modular Game Framework.*
|
||||||
345
docs/blueprints/02-player/10_BPC_StressSystem.md
Normal file
345
docs/blueprints/02-player/10_BPC_StressSystem.md
Normal file
@@ -0,0 +1,345 @@
|
|||||||
|
# 10 — Stress / Sanity System (`BPC_StressSystem`)
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
Tracks the player's psychological state via a stress meter that accumulates from environmental horror, enemy proximity, health deficits, and narrative events. Drives visual distortion, audio hallucinations, gameplay penalties, and narrative branching based on sanity thresholds.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **Requires:** `BPC_HealthSystem` (low health triggers stress), `GI_GameFramework` (phase checks)
|
||||||
|
- **Required By:** `BPC_PlayerController` (shader/audio effects), `WBP_HUD` (stress vignette), `NarrativeManager` (sanity-gated content), `BPC_AdaptiveEnvironment` (world distortion)
|
||||||
|
- **Engine/Plugin Requirements:** GameplayTags, Timers, Material Parameter Collections (for post-process effects)
|
||||||
|
|
||||||
|
## Class Info
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Parent Class** | `ActorComponent` |
|
||||||
|
| **Class Type** | Blueprint Component |
|
||||||
|
| **Asset Path** | `Content/Framework/Player/BPC_StressSystem` |
|
||||||
|
| **Implements Interfaces** | None |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Enums
|
||||||
|
|
||||||
|
### `E_StressTier`
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Calm = 0` | No stress effects |
|
||||||
|
| `Uneasy = 1` | Subtle audio cues, slight FOV shift |
|
||||||
|
| `Distressed = 2` | Visual noise, heartbeat audio, reduced stamina regen |
|
||||||
|
| `Panicked = 3` | Heavy distortion, screen shake, movement penalty |
|
||||||
|
| `Terrified = 4` | Hallucinations, audio distortion, random input inversion |
|
||||||
|
| `Catatonic = 5` | Brief freeze/stumble, forced slow walk |
|
||||||
|
|
||||||
|
### `E_StressSource`
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `EnemyProximity = 0` | Nearby hostile presence |
|
||||||
|
| `EnvironmentalHorror = 1` | Gore, darkness, unsettling areas |
|
||||||
|
| `HealthDeficit = 2` | Low health triggers fear |
|
||||||
|
| `NarrativeEvent = 3` | Scripted story beats |
|
||||||
|
| `Supernatural = 4` | Ghosts, paranormal activity |
|
||||||
|
| `Isolation = 5` | Long periods alone |
|
||||||
|
| `TraumaTrigger = 6` | PTSD flashback spots |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Structs
|
||||||
|
|
||||||
|
### `S_StressThresholds`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `UneasyThreshold` | `Float` | Stress value to enter Uneasy tier |
|
||||||
|
| `DistressedThreshold` | `Float` | Stress value to enter Distressed |
|
||||||
|
| `PanickedThreshold` | `Float` | Stress value to enter Panicked |
|
||||||
|
| `TerrifiedThreshold` | `Float` | Stress value to enter Terrified |
|
||||||
|
| `CatatonicThreshold` | `Float` | Stress value to enter Catatonic |
|
||||||
|
|
||||||
|
### `S_StressSourceData`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `SourceType` | `E_StressSource` | Category of source |
|
||||||
|
| `CurrentIntensity` | `Float` | Current contribution from this source [0..Max] |
|
||||||
|
| `MaxIntensity` | `Float` | Maximum contribution this source can apply |
|
||||||
|
| `DecayRate` | `Float` | How fast this source fades when no longer active |
|
||||||
|
| `SourceTags` | `GameplayTagContainer` | Contextual tags for filtering |
|
||||||
|
| `SourceIdentifier` | `FName` | Unique name for this specific source instance |
|
||||||
|
|
||||||
|
### `S_StressEvent`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Amount` | `Float` | Stress to add or remove |
|
||||||
|
| `SourceType` | `E_StressSource` | Category of source |
|
||||||
|
| `Instigator` | `Actor` | What caused the stress change |
|
||||||
|
| `bIsInstant` | `Boolean` | If true, bypasses buildup curve |
|
||||||
|
| `EventTags` | `GameplayTagContainer` | Additional context |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
|
||||||
|
### Configuration (Instance Editable, Expose On Spawn)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `MaxStress` | `Float` | `100.0` | `Stress Config` | Maximum stress value |
|
||||||
|
| `Thresholds` | `S_StressThresholds` | `(15, 30, 50, 75, 90)` | `Stress Config` | Threshold values for each tier |
|
||||||
|
| `PassiveDecayRate` | `Float` | `2.0` | `Stress Config` | Stress lost per second when in calm area |
|
||||||
|
| `PanicDecayDelay` | `Float` | `5.0` | `Stress Config` | Seconds at max tier before forced decay begins |
|
||||||
|
| `bEnableHallucinations` | `Boolean` | `true` | `Stress Config` | Allow visual/audio hallucinations at high stress |
|
||||||
|
| `bCanGoCatatonic` | `Boolean` | `true` | `Stress Config` | Allow catatonic freeze state |
|
||||||
|
| `bStressAffectsMovement` | `Boolean` | `true` | `Stress Config` | Apply movement penalties from stress |
|
||||||
|
| `HealthDeficitMultiplier` | `Float` | `1.5` | `Stress Config` | How much low health amplifies incoming stress |
|
||||||
|
|
||||||
|
### Internal (Private / Protected, No Expose)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `CurrentStress` | `Float` | `0.0` | `Stress State` | Current accumulated stress |
|
||||||
|
| `CurrentTier` | `E_StressTier` | `Calm` | `Stress State` | Current stress tier |
|
||||||
|
| `ActiveSources` | `Map<FName, S_StressSourceData>` | `{}` | `Stress State` | Currently contributing stress sources |
|
||||||
|
| `StressDecayTimer` | `FTimerHandle` | `-` | `Stress State` | Timer for passive decay tick |
|
||||||
|
| `bIsInSafeZone` | `Boolean` | `true` | `Stress State` | True when player is in a calm area |
|
||||||
|
| `LastStressTime` | `Float` | `0.0` | `Stress State` | World time of last stress change |
|
||||||
|
| `bHallucinationsActive` | `Boolean` | `false` | `Stress State` | Whether hallucinations are currently triggered |
|
||||||
|
| `TierEntryTimes` | `Map<E_StressTier, float>` | `{}` | `Stress State` | World time when each tier was entered |
|
||||||
|
|
||||||
|
### Replicated (if multiplayer)
|
||||||
|
|
||||||
|
| Variable | Type | Condition | Description |
|
||||||
|
|----------|------|-----------|-------------|
|
||||||
|
| `CurrentStress` | `Float` | `RepNotify` | Replicated for UI and effects |
|
||||||
|
| `CurrentTier` | `E_StressTier` | `RepNotify` | Replicated tier state |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
### Public Functions
|
||||||
|
|
||||||
|
#### `AddStress` → `Float (actual stress added)`
|
||||||
|
- **Description:** Adds stress from a source. Returns amount actually added.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Event` | `S_StressEvent` | Stress event to apply |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
- **Flow:**
|
||||||
|
1. If CurrentTier == Catatonic and !Event.bIsInstant: return 0 (immune at max)
|
||||||
|
2. If HealthDeficitMultiplier > 1.0 and health < 50%: scale amount
|
||||||
|
3. If Event.bIsInstant: CurrentStress += Event.Amount
|
||||||
|
4. Else: add/update ActiveSources with Event data, recalculate from sources
|
||||||
|
5. Clamp CurrentStress to [0, MaxStress]
|
||||||
|
6. Update CurrentTier
|
||||||
|
7. Fire OnStressChanged
|
||||||
|
8. If tier changed: fire OnStressTierChanged
|
||||||
|
9. If tier >= Panicked: start tick for hallucinations
|
||||||
|
10. Return delta
|
||||||
|
|
||||||
|
#### `RemoveStress` → `Float`
|
||||||
|
- **Description:** Reduces stress directly or removes a specific source.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Amount` | `Float` | Amount to reduce |
|
||||||
|
| `SourceIdentifier` | `FName` | Optional — if set, remove this source entirely |
|
||||||
|
- **Flow:**
|
||||||
|
1. If SourceIdentifier set: remove from ActiveSources, recalculate total
|
||||||
|
2. Else: CurrentStress = FMath::Max(0, CurrentStress - Amount)
|
||||||
|
3. Update CurrentTier
|
||||||
|
4. Fire OnStressChanged
|
||||||
|
5. If tier changed: fire OnStressTierChanged
|
||||||
|
|
||||||
|
#### `AddStressSource` → `void`
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `SourceData` | `S_StressSourceData` | Source configuration to add |
|
||||||
|
- **Flow:**
|
||||||
|
1. If SourceData.SourceIdentifier already in ActiveSources: update MaxIntensity and DecayRate
|
||||||
|
2. Else: add new entry
|
||||||
|
3. Recalculate total stress from all sources
|
||||||
|
|
||||||
|
#### `RemoveStressSource` → `void`
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `SourceIdentifier` | `FName` | Source to remove |
|
||||||
|
- **Flow:**
|
||||||
|
1. Remove from ActiveSources
|
||||||
|
2. Recalculate total stress
|
||||||
|
|
||||||
|
#### `SetSafeZone` → `void`
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `bSafe` | `Boolean` | Whether player is in a safe zone |
|
||||||
|
- **Flow:**
|
||||||
|
1. bIsInSafeZone = bSafe
|
||||||
|
2. If bSafe: begin passive decay
|
||||||
|
3. If !bSafe: stop passive decay
|
||||||
|
4. Fire OnSafeZoneChanged
|
||||||
|
|
||||||
|
#### `GetStressNormalised` → `Float [0.0 - 1.0]`
|
||||||
|
- **Flow:** Return CurrentStress / MaxStress
|
||||||
|
|
||||||
|
#### `GetTierDuration` → `Float`
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Tier` | `E_StressTier` | Which tier to query |
|
||||||
|
- **Description:** Returns how long the player has been in the given tier.
|
||||||
|
- **Flow:** If TierEntryTimes contains Tier: return CurrentWorldTime - TierEntryTimes[Tier]; else return 0
|
||||||
|
|
||||||
|
### Protected / Private Functions
|
||||||
|
|
||||||
|
#### `UpdateTier` → `void`
|
||||||
|
- **Flow:**
|
||||||
|
1. OldTier = CurrentTier
|
||||||
|
2. Check CurrentStress against Thresholds in descending order
|
||||||
|
3. NewTier = matching tier (highest threshold exceeded)
|
||||||
|
4. If NewTier != OldTier:
|
||||||
|
- CurrentTier = NewTier
|
||||||
|
- Record TierEntryTimes[NewTier] = CurrentWorldTime
|
||||||
|
- Fire OnStressTierChanged
|
||||||
|
|
||||||
|
#### `RecalculateFromSources` → `void`
|
||||||
|
- **Flow:**
|
||||||
|
1. Total = 0.0
|
||||||
|
2. For each ActiveSource: Total += Source.CurrentIntensity
|
||||||
|
3. CurrentStress = FMath::Clamp(Total, 0, MaxStress)
|
||||||
|
4. UpdateTier
|
||||||
|
5. Fire OnStressChanged
|
||||||
|
|
||||||
|
#### `PassiveDecayTick` → `void`
|
||||||
|
- **Flow:**
|
||||||
|
1. If bIsInSafeZone or CurrentTier < Distressed or CurrentStress <= 0: return
|
||||||
|
2. DecayAmount = PassiveDecayRate * 0.1
|
||||||
|
3. CurrentStress = FMath::Max(0, CurrentStress - DecayAmount)
|
||||||
|
4. Fire OnStressChanged
|
||||||
|
|
||||||
|
#### `HandleHallucinationCheck` → `void`
|
||||||
|
- **Flow:**
|
||||||
|
1. If CurrentTier < Terrified or !bEnableHallucinations:
|
||||||
|
- If bHallucinationsActive: StopHallucinations
|
||||||
|
- return
|
||||||
|
2. Random chance each tick (e.g., 5% per second at Terrified)
|
||||||
|
3. If roll succeeds: trigger random hallucination event
|
||||||
|
4. Fire OnHallucinationTriggered
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Parameters | Bind Access | Description |
|
||||||
|
|------------|-----------|-------------|-------------|
|
||||||
|
| `OnStressChanged` | `float OldStress`, `float NewStress` | `Public` | Fired on any stress change |
|
||||||
|
| `OnStressTierChanged` | `E_StressTier OldTier`, `E_StressTier NewTier` | `Public` | Fired when stress tier changes |
|
||||||
|
| `OnStressSourceAdded` | `FName SourceIdentifier`, `E_StressSource SourceType` | `Public` | Fired when a new stress source is added |
|
||||||
|
| `OnStressSourceRemoved` | `FName SourceIdentifier` | `Public` | Fired when a stress source is removed |
|
||||||
|
| `OnSafeZoneChanged` | `bool bIsSafe` | `Public` | Fired when entering/leaving safe zone |
|
||||||
|
| `OnHallucinationTriggered` | `E_HallucinationType Type` | `Public` | Fired when a hallucination event occurs |
|
||||||
|
| `OnCatatonicStateEntered` | `none` | `Public` | Fired when entering Catatonic tier |
|
||||||
|
| `OnStressRecovered` | `none` | `Public` | Fired when stress drops back to Calm |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Overridden Events / Custom Events
|
||||||
|
|
||||||
|
### Event: `BeginPlay`
|
||||||
|
- **Description:** Initialises stress system, binds to health component if available.
|
||||||
|
- **Flow:**
|
||||||
|
1. CurrentStress = 0.0
|
||||||
|
2. CurrentTier = Calm
|
||||||
|
3. Find BPC_HealthSystem on owner
|
||||||
|
4. If found: bind OnDamageTaken to a handler that adds stress for damage taken
|
||||||
|
5. Start passive decay timer (interval 0.1s)
|
||||||
|
|
||||||
|
### Custom Event: `OnDamageTakenHandler`
|
||||||
|
- **Parameters:** `S_DamageEvent DamageEvent`, `float EffectiveDamage`
|
||||||
|
- **Description:** Stress response to taking damage.
|
||||||
|
- **Flow:**
|
||||||
|
1. StressAmount = EffectiveDamage * 0.5
|
||||||
|
2. If DamageType == Fear: StressAmount *= 2.0
|
||||||
|
3. Build S_StressEvent with SourceType = HealthDeficit
|
||||||
|
4. Call AddStress
|
||||||
|
|
||||||
|
### Custom Event: `ForcePanic`
|
||||||
|
- **Parameters:** `float Duration`
|
||||||
|
- **Description:** Forcibly raises stress to Panicked tier for a duration, used by narrative events.
|
||||||
|
- **Flow:**
|
||||||
|
1. CurrentStress = Thresholds.PanickedThreshold
|
||||||
|
2. UpdateTier
|
||||||
|
3. Start timer for Duration
|
||||||
|
4. On timer end: gradually return stress to previous level
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blueprint Graph Logic Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[AddStress called] --> B{CurrentTier == Catatonic?}
|
||||||
|
B -->|Yes| C[Return 0]
|
||||||
|
B -->|No| D[Apply HealthDeficitMultiplier]
|
||||||
|
D --> E[Update ActiveSources map]
|
||||||
|
E --> F[Recalculate total stress]
|
||||||
|
F --> G[Clamp to 0..MaxStress]
|
||||||
|
G --> H{NewTier == OldTier?}
|
||||||
|
H -->|No| I[Update CurrentTier]
|
||||||
|
I --> J[Record tier entry time]
|
||||||
|
J --> K[Fire OnStressTierChanged]
|
||||||
|
H -->|Yes| L[Skip tier update]
|
||||||
|
K --> M{Tier >= Panicked?}
|
||||||
|
M -->|Yes| N[Start hallucination ticker]
|
||||||
|
M -->|No| O{Stress dropped below Distressed?}
|
||||||
|
O -->|Yes| P[Stop hallucinations]
|
||||||
|
O -->|No| Q[Continue passive decay]
|
||||||
|
N --> R[Fire OnStressChanged]
|
||||||
|
P --> R
|
||||||
|
Q --> R
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Communication Matrix
|
||||||
|
|
||||||
|
| Who Talks | How | What Is Sent |
|
||||||
|
|-----------|-----|-------------|
|
||||||
|
| `BPC_StressSystem` | `Dispatcher` | `OnStressTierChanged` -> `PC_PlayerController` (post-process), `ABP_GASP` (anim), `WBP_HUD` (vignette) |
|
||||||
|
| `BPC_StressSystem` | `Dispatcher` | `OnHallucinationTriggered` -> `BP_HallucinationManager`, `BP_AudioManager` |
|
||||||
|
| `BPC_StressSystem` | `Dispatcher` | `OnStressChanged` -> `WBP_HUD` (stress bar) |
|
||||||
|
| `BPC_StressSystem` | `Dispatcher` | `OnCatatonicStateEntered` -> `BPC_PlayerController` (input lockdown) |
|
||||||
|
| `External (Enemies)` | `Direct` | Calls `AddStressSource` on detection |
|
||||||
|
| `External (BP_DeathZone)` | `Direct` | Calls `AddStress` via interface |
|
||||||
|
| `BPC_HealthSystem` | `Listener` | Binds to `OnDamageTaken` for health-deficit stress |
|
||||||
|
| `BPC_StressSystem` | `Dispatcher` | `OnSafeZoneChanged` -> `BPC_AdaptiveAtmosphere` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Validation / Testing Checklist
|
||||||
|
|
||||||
|
- [ ] Stress accumulates from multiple sources and sums correctly
|
||||||
|
- [ ] Stress decays passively when in safe zone or above Distressed
|
||||||
|
- [ ] Tier transitions fire exactly once per threshold crossing
|
||||||
|
- [ ] Catatonic tier blocks further stress accumulation
|
||||||
|
- [ ] Hallucinations trigger randomly at Terrified tier and stop below it
|
||||||
|
- [ ] Stress sources with identifiers are properly tracked and removed
|
||||||
|
- [ ] Edge case: Stress added while at MaxStress clamps and does not overflow
|
||||||
|
- [ ] Edge case: RemoveStress with source identifier removes only that source
|
||||||
|
- [ ] Edge case: ForcePanic temporarily locks tier and restores after duration
|
||||||
|
- [ ] All timers clean up on component destroy
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Reuse Notes
|
||||||
|
|
||||||
|
- Can be used on NPCs for a simplified "morale" system (remove hallucination features).
|
||||||
|
- Stress tiers can gate narrative content — questgivers behave differently based on player stress.
|
||||||
|
- Hallucination types can be configured via Data Asset (DA_HallucinationConfig).
|
||||||
|
- For multiplayer: Stress is server-authoritative; clients receive tier changes for local effects.
|
||||||
|
- The HealthDeficitMultiplier creates a death spiral feel — use carefully for horror pacing.
|
||||||
|
- Safe zones double as narrative checkpoints and stress recovery areas.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Blueprint Spec: Stress/Sanity System. Conforms to TEMPLATE.md v1.0 — part of the UE5 Modular Game Framework.*
|
||||||
323
docs/blueprints/02-player/11_BPC_MovementStateSystem.md
Normal file
323
docs/blueprints/02-player/11_BPC_MovementStateSystem.md
Normal file
@@ -0,0 +1,323 @@
|
|||||||
|
# 11 — Movement State System (`BPC_MovementStateSystem`)
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
Tracks the player's current movement mode and posture state. Acts as a central query point for other systems (animations, stamina, audio, camera) to react to how the player is moving. Does not control movement input — only reports and reacts to state changes.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **Requires:** `FL_GameUtilities` (tag queries)
|
||||||
|
- **Required By:** `ABP_GASP` (animations), `BPC_StaminaSystem` (regen blocking), `BPC_CameraStateLayer` (FOV changes), `BPC_AudioManager` (footstep type), `BPC_InteractionDetector` (range scaling)
|
||||||
|
- **Engine/Plugin Requirements:** GameplayTags, GASP (ground/animation/strafing platform)
|
||||||
|
|
||||||
|
## Class Info
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Parent Class** | `ActorComponent` |
|
||||||
|
| **Class Type** | Blueprint Component |
|
||||||
|
| **Asset Path** | `Content/Framework/Player/BPC_MovementStateSystem` |
|
||||||
|
| **Implements Interfaces** | None |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Enums
|
||||||
|
|
||||||
|
### `E_PostureState`
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Standing = 0` | Full height, normal locomotion |
|
||||||
|
| `Crouching = 1` | Reduced height, slower speed |
|
||||||
|
| `Prone = 2` | Fully prone, crawling (horror immersion) |
|
||||||
|
| `Sliding = 3` | Transient — while sliding downhill or under obstacles |
|
||||||
|
| `Climbing = 4` | Ledge or ladder climbing |
|
||||||
|
| `Vaulting = 5` | Transient — while vaulting over obstacles |
|
||||||
|
|
||||||
|
### `E_MovementMode`
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Idle = 0` | Standing still |
|
||||||
|
| `Walking = 1` | Slow, careful movement |
|
||||||
|
| `Jogging = 2` | Normal movement speed |
|
||||||
|
| `Sprinting = 3` | Fast movement, stamina drain |
|
||||||
|
| `CrouchWalk = 4` | Slow crouch movement |
|
||||||
|
| `Sneaking = 5` | Ultra-slow, near-silent movement (special) |
|
||||||
|
|
||||||
|
### `E_StanceTransition`
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Instant = 0` | Immediate transition (no animation blend) |
|
||||||
|
| `Smooth = 1` | Smooth blend over TransitionBlendTime |
|
||||||
|
| `Forced = 2` | External force (knocked down, pushed) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Structs
|
||||||
|
|
||||||
|
### `S_MovementSettings`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `MovementMode` | `E_MovementMode` | Which mode this applies to |
|
||||||
|
| `MaxWalkSpeed` | `Float` | Max speed in this mode (cm/s) |
|
||||||
|
| `Acceleration` | `Float` | Acceleration rate |
|
||||||
|
| `Deceleration` | `Float` | Deceleration rate |
|
||||||
|
| `GroundFriction` | `Float` | Friction modifier |
|
||||||
|
| `bCanSprint` | `Boolean` | Can the player sprint from this mode? |
|
||||||
|
| `bCanCrouch` | `Boolean` | Can the player crouch from this mode? |
|
||||||
|
| `StaminaDrainMultiplier` | `Float` | Multiplier on stamina drain while in this mode |
|
||||||
|
|
||||||
|
### `S_FootstepProfile`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `SurfaceType` | `EPhysicalSurface` | Surface being stepped on |
|
||||||
|
| `MovementMode` | `E_MovementMode` | Player's current movement mode |
|
||||||
|
| `SoundSoft` | `SoundBase` | Footstep sound at low velocity |
|
||||||
|
| `SoundHard` | `SoundBase` | Footstep sound at high velocity |
|
||||||
|
| `VelocityThreshold` | `Float` | Speed at which to switch from Soft to Hard |
|
||||||
|
| `Decal` | `MaterialInterface` | Footprint decal (mud, snow, blood) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
|
||||||
|
### Configuration (Instance Editable, Expose On Spawn)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `MovementSettings` | `Map<E_MovementMode, S_MovementSettings>` | `all modes` | `Movement Config` | Speed/accel values per movement mode |
|
||||||
|
| `TransitionBlendTime` | `Float` | `0.2` | `Movement Config` | Blend duration for smooth stance transitions |
|
||||||
|
| `bApplyMovementPenalties` | `Boolean` | `true` | `Movement Config` | Allow stress/health to affect movement speed |
|
||||||
|
| `CrouchHeight` | `Float` | `60.0` | `Movement Config` | Capsule half-height while crouching |
|
||||||
|
| `ProneHeight` | `Float` | `30.0` | `Movement Config` | Capsule half-height while prone |
|
||||||
|
| `StandingHeight` | `Float` | `96.0` | `Movement Config` | Capsule half-height while standing |
|
||||||
|
| `bUseVelocityBasedFootsteps` | `Boolean` | `true` | `Movement Config` | Auto-select footstep sound based on velocity |
|
||||||
|
| `DefaultFootstepProfile` | `S_FootstepProfile` | `-` | `Movement Config` | Fallback footstep when no surface match |
|
||||||
|
|
||||||
|
### Internal (Private / Protected, No Expose)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `CurrentPosture` | `E_PostureState` | `Standing` | `Movement State` | Current posture |
|
||||||
|
| `PreviousPosture` | `E_PostureState` | `Standing` | `Movement State` | Previous posture (for transition detection) |
|
||||||
|
| `CurrentMovementMode` | `E_MovementMode` | `Idle` | `Movement State` | Current movement mode |
|
||||||
|
| `PreviousMovementMode` | `E_MovementMode` | `Idle` | `Movement State` | Previous movement mode |
|
||||||
|
| `bIsOnGround` | `Boolean` | `true` | `Movement State` | Whether the player is grounded |
|
||||||
|
| `bIsMoving` | `Boolean` | `false` | `Movement State` | Whether the player is currently moving |
|
||||||
|
| `CurrentSpeed` | `Float` | `0.0` | `Movement State` | Current character speed (cm/s) |
|
||||||
|
| `bIsClimbing` | `Boolean` | `false` | `Movement State` | Whether currently climbing |
|
||||||
|
| `LastValidFloorNormal` | `Vector` | `(0,0,1)` | `Movement State` | Last valid ground normal |
|
||||||
|
| `CapsuleRef` | `UCapsuleComponent` | `None` | `Movement State` | Cached reference to capsule component |
|
||||||
|
|
||||||
|
### Replicated (if multiplayer)
|
||||||
|
|
||||||
|
| Variable | Type | Condition | Description |
|
||||||
|
|----------|------|-----------|-------------|
|
||||||
|
| `CurrentPosture` | `E_PostureState` | `RepNotify` | Replicated posture state |
|
||||||
|
| `CurrentMovementMode` | `E_MovementMode` | `RepNotify` | Replicated movement mode |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
### Public Functions
|
||||||
|
|
||||||
|
#### `SetMovementMode` → `void`
|
||||||
|
- **Description:** Sets the current movement mode and applies corresponding movement settings to the CharacterMovementComponent.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `NewMode` | `E_MovementMode` | Desired movement mode |
|
||||||
|
| `Transition` | `E_StanceTransition` | How to handle the transition |
|
||||||
|
- **Blueprint Authority:** Server (if MP), Any (single-player)
|
||||||
|
- **Flow:**
|
||||||
|
1. If NewMode == CurrentMovementMode: return
|
||||||
|
2. PreviousMovementMode = CurrentMovementMode
|
||||||
|
3. CurrentMovementMode = NewMode
|
||||||
|
4. Apply MovementSettings[NewMode] to CharacterMovementComponent
|
||||||
|
5. Fire OnMovementModeChanged
|
||||||
|
6. If transition is Forced: apply impulse or knockback
|
||||||
|
7. Notify ABP_GASP via interface or direct cast
|
||||||
|
|
||||||
|
#### `SetPosture` → `void`
|
||||||
|
- **Description:** Sets the current posture and adjusts capsule size accordingly.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `NewPosture` | `E_PostureState` | Desired posture |
|
||||||
|
| `Transition` | `E_StanceTransition` | How to handle the transition |
|
||||||
|
- **Flow:**
|
||||||
|
1. If NewPosture == CurrentPosture: return
|
||||||
|
2. If NewPosture == Prone and CurrentPosture == Standing: must go through Crouching first
|
||||||
|
3. PreviousPosture = CurrentPosture
|
||||||
|
4. CurrentPosture = NewPosture
|
||||||
|
5. Update capsule height based on NewPosture
|
||||||
|
6. Fire OnPostureChanged
|
||||||
|
7. Notify ABP_GASP
|
||||||
|
|
||||||
|
#### `GetCurrentSpeedNormalised` → `Float [0.0 - 1.0]`
|
||||||
|
- **Description:** Returns speed as fraction of CurrentMovementMode's MaxWalkSpeed.
|
||||||
|
- **Flow:** Return CurrentSpeed / MovementSettings[CurrentMovementMode].MaxWalkSpeed
|
||||||
|
|
||||||
|
#### `CanTransitionToPosture` → `Boolean`
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `TargetPosture` | `E_PostureState` | Desired posture |
|
||||||
|
- **Flow:**
|
||||||
|
1. Check ceiling clearance for upright transitions
|
||||||
|
2. Check if MovementSettings allows this posture from current mode
|
||||||
|
3. Return clearance OK and setting allows
|
||||||
|
|
||||||
|
#### `SetSprinting` → `void`
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `bSprinting` | `Boolean` | Whether to sprint |
|
||||||
|
- **Flow:**
|
||||||
|
1. If bSprinting and CanSprint(): SetMovementMode(Sprinting)
|
||||||
|
2. If !bSprinting: restore previous movement mode
|
||||||
|
3. Fire OnSprintStateChanged
|
||||||
|
|
||||||
|
#### `SetCrouching` → `void`
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `bCrouching` | `Boolean` | Whether to crouch |
|
||||||
|
- **Flow:**
|
||||||
|
1. If bCrouching: SetPosture(Crouching), SetMovementMode(CrouchWalk)
|
||||||
|
2. If !bCrouching: SetPosture(Standing), restore previous walk mode
|
||||||
|
|
||||||
|
#### `ApplyMovementPenalty` → `void`
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Multiplier` | `Float` | Speed multiplier [0.0 - 1.0] |
|
||||||
|
| `Duration` | `Float` | How long the penalty lasts |
|
||||||
|
| `PenaltyTag` | `GameplayTag` | Identifier (e.g. Injury, Fear, SlowEffect) |
|
||||||
|
- **Flow:**
|
||||||
|
1. Modify current MovementSettings[CurrentMovementMode].MaxWalkSpeed *= Multiplier
|
||||||
|
2. Start timer for Duration
|
||||||
|
3. On timer end: restore original MaxWalkSpeed for this mode
|
||||||
|
|
||||||
|
### Protected / Private Functions
|
||||||
|
|
||||||
|
#### `OnMovementUpdated (Tick)` → `void`
|
||||||
|
- **Description:** Called each tick. Updates speed tracking, detects movement start/stop, checks ground state.
|
||||||
|
- **Flow:**
|
||||||
|
1. Get velocity from CharacterMovementComponent
|
||||||
|
2. CurrentSpeed = velocity.Size()
|
||||||
|
3. OldMoving = bIsMoving
|
||||||
|
4. bIsMoving = CurrentSpeed > 10.0
|
||||||
|
5. If bIsMoving != OldMoving: fire OnMovementStart or OnMovementStop
|
||||||
|
6. Check bIsOnGround from movement component
|
||||||
|
|
||||||
|
#### `CalculateFootstep` → `S_FootstepProfile`
|
||||||
|
- **Description:** Selects the correct footstep sound based on surface and movement mode.
|
||||||
|
- **Flow:**
|
||||||
|
1. Perform line trace from foot location downward
|
||||||
|
2. Get surface type from physical material
|
||||||
|
3. Look up S_FootstepProfile matching SurfaceType and CurrentMovementMode
|
||||||
|
4. If no match: return DefaultFootstepProfile
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Parameters | Bind Access | Description |
|
||||||
|
|------------|-----------|-------------|-------------|
|
||||||
|
| `OnMovementModeChanged` | `E_MovementMode OldMode`, `E_MovementMode NewMode` | `Public` | Fired when movement mode changes |
|
||||||
|
| `OnPostureChanged` | `E_PostureState OldPosture`, `E_PostureState NewPosture` | `Public` | Fired when posture changes |
|
||||||
|
| `OnMovementStart` | `none` | `Public` | Fired when the player starts moving from idle |
|
||||||
|
| `OnMovementStop` | `none` | `Public` | Fired when the player stops moving |
|
||||||
|
| `OnSprintStateChanged` | `bool bIsSprinting` | `Public` | Fired when sprint state toggles |
|
||||||
|
| `OnJumped` | `none` | `Public` | Fired when the player jumps |
|
||||||
|
| `OnLanded` | `float FallVelocity` | `Public` | Fired when the player lands after a fall |
|
||||||
|
| `OnClimbStarted` | `AActor ClimbableActor` | `Public` | Fired when climbing begins |
|
||||||
|
| `OnClimbEnded` | `none` | `Public` | Fired when climbing ends |
|
||||||
|
| `OnFootstep` | `E_MovementMode Mode`, `EPhysicalSurface Surface` | `Public` | Fired on each footstep for audio |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Overridden Events / Custom Events
|
||||||
|
|
||||||
|
### Event: `BeginPlay`
|
||||||
|
- **Description:** Initialises state, caches references, binds to owner's character movement events.
|
||||||
|
- **Flow:**
|
||||||
|
1. Get Owner as Character
|
||||||
|
2. Cache CharacterMovementComponent and CapsuleComponent
|
||||||
|
3. Bind to Character's OnJumped and OnLanded delegates
|
||||||
|
4. Set initial MovementMode = Idle, Posture = Standing
|
||||||
|
5. Apply default MovementSettings to movement component
|
||||||
|
|
||||||
|
### Event: `Tick (if enabled)`
|
||||||
|
- **Description:** Updates speed tracking and detects transitions.
|
||||||
|
- **Flow:**
|
||||||
|
1. Call OnMovementUpdated
|
||||||
|
2. Check for automatic posture transitions (e.g., falling -> standing on land)
|
||||||
|
|
||||||
|
### Custom Event: `OnTakeDamageAffectsMovement`
|
||||||
|
- **Description:** Listener bound to BPC_HealthSystem.OnDamageTaken.
|
||||||
|
- **Flow:**
|
||||||
|
1. If damage was significant (> 20% health): apply brief movement penalty
|
||||||
|
2. Penalty severity scales with damage amount
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blueprint Graph Logic Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[SetMovementMode called] --> B{Valid transition?}
|
||||||
|
B -->|No| C[Return]
|
||||||
|
B -->|Yes| D[Store PreviousMode]
|
||||||
|
D --> E[Set CurrentMode = NewMode]
|
||||||
|
E --> F[Apply MovementSettings to CharMoveComp]
|
||||||
|
F --> G[Fire OnMovementModeChanged]
|
||||||
|
G --> H{NewMode == Sprinting?}
|
||||||
|
H -->|Yes| I[Notify BPC_StaminaSystem: StartContinuousDrain]
|
||||||
|
H -->|No| J{PreviousMode == Sprinting?}
|
||||||
|
J -->|Yes| K[Notify BPC_StaminaSystem: StopContinuousDrain]
|
||||||
|
J -->|No| L[Update ABP_GASP blend values]
|
||||||
|
I --> L
|
||||||
|
K --> L
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Communication Matrix
|
||||||
|
|
||||||
|
| Who Talks | How | What Is Sent |
|
||||||
|
|-----------|-----|-------------|
|
||||||
|
| `BPC_MovementStateSystem` | `Dispatcher` | `OnMovementModeChanged` -> `ABP_GASP`, `BPC_StaminaSystem`, `BPC_CameraStateLayer` |
|
||||||
|
| `BPC_MovementStateSystem` | `Dispatcher` | `OnPostureChanged` -> `ABP_GASP`, `BPC_InteractionDetector`, `BP_AudioManager` |
|
||||||
|
| `BPC_MovementStateSystem` | `Dispatcher` | `OnFootstep` -> `BP_AudioManager` (footstep SFX) |
|
||||||
|
| `BPC_MovementStateSystem` | `Dispatcher` | `OnJumped` / `OnLanded` -> `BPC_StaminaSystem` (landing drain) |
|
||||||
|
| `BPC_MovementStateSystem` | `Dispatcher` | `OnSprintStateChanged` -> `BPC_StaminaSystem`, `BPC_CameraStateLayer` (FOV) |
|
||||||
|
| `External (Input)` | `Direct` | `PC_PlayerController` calls SetMovementMode / SetPosture |
|
||||||
|
| `BPC_MovementStateSystem` | `Listener` | Binds to `BPC_HealthSystem.OnDamageTaken` for injury penalties |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Validation / Testing Checklist
|
||||||
|
|
||||||
|
- [ ] Movement mode changes correctly apply speed/accel values to CharacterMovementComponent
|
||||||
|
- [ ] Posture changes correctly adjust capsule height and collision
|
||||||
|
- [ ] Prone requires crouch intermediate transition
|
||||||
|
- [ ] Sprinting auto-stops when stamina hits 0 (via stamina listener)
|
||||||
|
- [ ] Movement penalties correctly modify MaxWalkSpeed and restore after duration
|
||||||
|
- [ ] OnMovementStart/OnMovementStop fire correctly at speed thresholds
|
||||||
|
- [ ] Footstep profile selection works for all surface types
|
||||||
|
- [ ] Edge case: Forced transition (knockback) applies impulse regardless of state
|
||||||
|
- [ ] Edge case: CanTransitionToPosture returns false when ceiling is too low
|
||||||
|
- [ ] Edge case: Rapid mode switching does not cause animation glitches (blend time enforced)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Reuse Notes
|
||||||
|
|
||||||
|
- This component is the central "movement oracle" — other systems query posture/mode instead of polling the CharacterMovementComponent.
|
||||||
|
- Footstep profiles should be defined in a Data Asset (DA_FootstepProfileTable) for easy content team iteration.
|
||||||
|
- The component does NOT handle input — that remains in PC_PlayerController.
|
||||||
|
- For AI characters, a simplified version can expose only Walking and Sprinting modes with no posture system.
|
||||||
|
- GASP integration: set GASP-specific variables (bStrafing, bSprinting) in the AnimationBlueprint on each mode change.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Blueprint Spec: Movement State System. Conforms to TEMPLATE.md v1.0 — part of the UE5 Modular Game Framework.*
|
||||||
328
docs/blueprints/02-player/12_BPC_HidingSystem.md
Normal file
328
docs/blueprints/02-player/12_BPC_HidingSystem.md
Normal file
@@ -0,0 +1,328 @@
|
|||||||
|
# 12 — Hiding System (`BPC_HidingSystem`)
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
Manages the player's ability to hide inside, behind, or under environmental objects. Handles entering/exiting hiding spots, line-of-sight checks against enemies, peeking, and stress reduction while concealed. The central component for stealth gameplay.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **Requires:** `FL_GameUtilities` (interface casts), `I_HidingSpot` (interface on world actors)
|
||||||
|
- **Required By:** `AI_EnemyController` (query hiding state for awareness), `BPC_StressSystem` (stress decay while hidden), `BPC_PlayerController` (movement/input restriction)
|
||||||
|
- **Engine/Plugin Requirements:** GameplayTags, LineTraceByChannel (LOS checks)
|
||||||
|
|
||||||
|
## Class Info
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Parent Class** | `ActorComponent` |
|
||||||
|
| **Class Type** | Blueprint Component |
|
||||||
|
| **Asset Path** | `Content/Framework/Player/BPC_HidingSystem` |
|
||||||
|
| **Implements Interfaces** | None |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Enums
|
||||||
|
|
||||||
|
### `E_HideState`
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Exposed = 0` | Not hiding, fully visible |
|
||||||
|
| `Entering = 1` | Transient — animation playing to enter hide spot |
|
||||||
|
| `Hidden = 2` | Inside hide spot, concealed |
|
||||||
|
| `Peeking = 3` | Partially visible, can look around |
|
||||||
|
| `Exiting = 4` | Transient — animation playing to exit hide spot |
|
||||||
|
|
||||||
|
### `E_HideType`
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Locker = 0` | Fully enclosed (locker, wardrobe) |
|
||||||
|
| `BehindCover = 1` | Behind low wall, crate, counter |
|
||||||
|
| `Under = 2` | Under bed, table, porch |
|
||||||
|
| `InShadow = 3` | Standing in a shadow volume |
|
||||||
|
| `TallGrass = 4` | Crouch-moving through vegetation |
|
||||||
|
|
||||||
|
### `E_PeekDirection`
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Left = 0` | Peek left from behind cover |
|
||||||
|
| `Right = 1` | Peek right from behind cover |
|
||||||
|
| `Over = 2` | Peek over low cover |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Structs
|
||||||
|
|
||||||
|
### `S_HideSpotInfo`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `HidingActor` | `AActor` | The hide spot actor |
|
||||||
|
| `HideType` | `E_HideType` | Type of hiding |
|
||||||
|
| `SlotCount` | `Integer` | How many can hide here (-1 = infinite) |
|
||||||
|
| `bHasPeekAbility` | `Boolean` | Can player peek from this spot? |
|
||||||
|
| `PeekSocketLeft` | `FName` | Socket name for left peek camera |
|
||||||
|
| `PeekSocketRight` | `FName` | Socket name for right peek camera |
|
||||||
|
| `PeekSocketOver` | `FName` | Socket name for over peek camera |
|
||||||
|
| `ExitLocation` | `Vector` | World location to place player on exit |
|
||||||
|
| `ExitRotation` | `Rotator` | Rotation to face on exit |
|
||||||
|
| `bBlocksStress` | `Boolean` | Whether this spot reduces stress gain |
|
||||||
|
| `HideTags` | `GameplayTagContainer` | Tags for filtering |
|
||||||
|
| `LOSTraceChannel` | `TEnumAsByte<ETraceTypeQuery>` | Trace channel for LOS checks from this spot |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
|
||||||
|
### Configuration (Instance Editable, Expose On Spawn)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `bCanHide` | `Boolean` | `true` | `Hiding Config` | Can this player hide at all? |
|
||||||
|
| `StressDecayWhileHidden` | `Float` | `5.0` | `Hiding Config` | Stress lost per second while hidden |
|
||||||
|
| `bEnemyCanFindHidingSpot` | `Boolean` | `true` | `Hiding Config` | Can enemies discover this spot |
|
||||||
|
| `MaxPeekDuration` | `Float` | `3.0` | `Hiding Config` | Max seconds player can peek before forced back |
|
||||||
|
| `PeekCooldown` | `Float` | `1.5` | `Hiding Config` | Seconds before player can peek again |
|
||||||
|
| `CapsuleCheckRadius` | `Float` | `50.0` | `Hiding Config` | Radius for capsule trace when finding hide spots |
|
||||||
|
|
||||||
|
### Internal (Private / Protected, No Expose)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `CurrentHideState` | `E_HideState` | `Exposed` | `Hiding State` | Current hiding state |
|
||||||
|
| `PreviousHideState` | `E_HideState` | `Exposed` | `Hiding State` | Previous state for transition detection |
|
||||||
|
| `CurrentHideSpot` | `S_HideSpotInfo` | `-` | `Hiding State` | Active hide spot data |
|
||||||
|
| `PendingHideSpot` | `AActor` | `None` | `Hiding State` | Hide spot being entered |
|
||||||
|
| `PeekTimerHandle` | `FTimerHandle` | `-` | `Hiding State` | Timer for max peek duration |
|
||||||
|
| `PeekCooldownTimerHandle` | `FTimerHandle` | `-` | `Hiding State` | Timer for peek cooldown |
|
||||||
|
| `bCanPeek` | `Boolean` | `true` | `Hiding State` | Whether peeking is allowed |
|
||||||
|
| `LastLOSCheckTime` | `Float` | `0.0` | `Hiding State` | World time of last LOS check |
|
||||||
|
| `LOSTimerHandle` | `FTimerHandle` | `-` | `Hiding State` | Timer for periodic LOS checks |
|
||||||
|
| `BreathHeldTimer` | `FTimerHandle` | `-` | `Hiding State` | Timer for breath-hold mechanic |
|
||||||
|
| `bIsHoldingBreath` | `Boolean` | `false` | `Hiding State` | Whether player is holding breath |
|
||||||
|
|
||||||
|
### Replicated (if multiplayer)
|
||||||
|
|
||||||
|
| Variable | Type | Condition | Description |
|
||||||
|
|----------|------|-----------|-------------|
|
||||||
|
| `CurrentHideState` | `E_HideState` | `RepNotify` | Replicated hide state |
|
||||||
|
| `CurrentHideSpot` | `S_HideSpotInfo` | `Replicated` | Replicated spot reference |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
### Public Functions
|
||||||
|
|
||||||
|
#### `EnterHideSpot` → `Boolean`
|
||||||
|
- **Description:** Attempts to enter a hiding spot. Returns true if successful.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `HideSpotActor` | `AActor` | The hide spot to enter |
|
||||||
|
- **Blueprint Authority:** Server (if MP), Any (single-player)
|
||||||
|
- **Flow:**
|
||||||
|
1. Early-out if CurrentHideState != Exposed or !bCanHide
|
||||||
|
2. Validate that HideSpotActor implements I_HidingSpot
|
||||||
|
3. Check slot availability (I_HidingSpot.Execute_HasAvailableSlots)
|
||||||
|
4. If available:
|
||||||
|
- Set CurrentHideState = Entering
|
||||||
|
- Store HideSpotActor info in CurrentHideSpot
|
||||||
|
- Disable player movement input
|
||||||
|
- Play entering animation (via ABP_GASP)
|
||||||
|
- On animation complete: Set CurrentHideState = Hidden
|
||||||
|
- Fire OnHideStateChanged
|
||||||
|
- Start stress decay timer
|
||||||
|
- Start periodic LOS check timer
|
||||||
|
- Return true
|
||||||
|
5. If full: return false
|
||||||
|
|
||||||
|
#### `ExitHideSpot` → `void`
|
||||||
|
- **Description:** Exits the current hiding spot.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `bForceExit` | `Boolean` | If true, skip exit animation |
|
||||||
|
- **Flow:**
|
||||||
|
1. If CurrentHideState != Hidden and CurrentHideState != Peeking: return
|
||||||
|
2. Set CurrentHideState = Exiting
|
||||||
|
3. If bForceExit: skip animation, immediately set Exposed
|
||||||
|
4. Else: play exiting animation
|
||||||
|
5. On animation complete or bForceExit:
|
||||||
|
- Teleport player to CurrentHideSpot.ExitLocation with ExitRotation
|
||||||
|
- Re-enable movement input
|
||||||
|
- Set CurrentHideState = Exposed
|
||||||
|
- Clear CurrentHideSpot
|
||||||
|
- Stop stress decay and LOS timers
|
||||||
|
- Fire OnHideStateChanged
|
||||||
|
- Fire OnExitedHidingSpot
|
||||||
|
|
||||||
|
#### `StartPeek` → `void`
|
||||||
|
- **Description:** Begins peeking from behind cover.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Direction` | `E_PeekDirection` | Which direction to peek |
|
||||||
|
- **Flow:**
|
||||||
|
1. Early-out if CurrentHideState != Hidden or !bCanPeek or !CurrentHideSpot.bHasPeekAbility
|
||||||
|
2. Set CurrentHideState = Peeking
|
||||||
|
3. Move camera to peek socket location
|
||||||
|
4. Start MaxPeekDuration timer
|
||||||
|
5. Start PeekCooldown timer (bCanPeek = false during cooldown)
|
||||||
|
6. Fire OnPeekStarted
|
||||||
|
|
||||||
|
#### `StopPeek` → `void`
|
||||||
|
- **Description:** Ends the peek and returns to Hidden state.
|
||||||
|
- **Flow:**
|
||||||
|
1. If CurrentHideState != Peeking: return
|
||||||
|
2. Set CurrentHideState = Hidden
|
||||||
|
3. Return camera to default hide position
|
||||||
|
4. Fire OnPeekEnded
|
||||||
|
|
||||||
|
#### `IsPlayerDetectable` → `Boolean`
|
||||||
|
- **Description:** Checks if the player is detectable from a given enemy location, considering hide type and edges.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `EnemyLocation` | `Vector` | Location of the enemy |
|
||||||
|
| `DetectionRange` | `Float` | Max range for detection check |
|
||||||
|
- **Flow:**
|
||||||
|
1. If CurrentHideState != Hidden: return true (fully detectable)
|
||||||
|
2. If EnemyLocation distance > DetectionRange: return false
|
||||||
|
3. Perform line trace from enemy to player's approximate position
|
||||||
|
4. If trace hits CurrentHideSpot.HidingActor before player: return false (blocked by cover)
|
||||||
|
5. If trace hits player directly: return true (cover not fully effective)
|
||||||
|
6. Edge case: peek state increases detection radius
|
||||||
|
|
||||||
|
#### `TryBreathHold` → `void`
|
||||||
|
- **Description:** Player holds breath to reduce noise/detectability for a short time.
|
||||||
|
- **Flow:**
|
||||||
|
1. bIsHoldingBreath = true
|
||||||
|
2. Start breath-hold timer (e.g., 8 seconds)
|
||||||
|
3. On timer end: forced exhale, brief noise event, bIsHoldingBreath = false
|
||||||
|
4. Fire OnBreathHoldChanged
|
||||||
|
|
||||||
|
### Protected / Private Functions
|
||||||
|
|
||||||
|
#### `PerformLOSCheck` → `void`
|
||||||
|
- **Description:** Timer callback — checks if any enemy has line of sight to this hide spot.
|
||||||
|
- **Flow:**
|
||||||
|
1. Get all AI_EnemyController instances within detection range
|
||||||
|
2. For each enemy: call IsPlayerDetectable(EnemyLocation)
|
||||||
|
3. If any enemy has LOS:
|
||||||
|
- Fire OnHideSpotCompromised
|
||||||
|
- If CurrentHideSpot.bEnemyCanFindHidingSpot: fire OnForcedExitWarning
|
||||||
|
|
||||||
|
#### `OnAnimationHideEnterComplete` → `void`
|
||||||
|
- **Description:** Animation notify callback when enter animation finishes.
|
||||||
|
- **Flow:**
|
||||||
|
1. Set CurrentHideState = Hidden
|
||||||
|
2. Attach player to hide spot socket if applicable
|
||||||
|
3. Fire OnHideStateChanged
|
||||||
|
4. Start stress decay loop
|
||||||
|
|
||||||
|
#### `OnAnimationHideExitComplete` → `void`
|
||||||
|
- **Description:** Animation notify callback when exit animation finishes.
|
||||||
|
- **Flow:**
|
||||||
|
1. Perform final exit steps already queued in ExitHideSpot
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Parameters | Bind Access | Description |
|
||||||
|
|------------|-----------|-------------|-------------|
|
||||||
|
| `OnHideStateChanged` | `E_HideState OldState`, `E_HideState NewState` | `Public` | Fired on any hide state change |
|
||||||
|
| `OnEnteredHidingSpot` | `AActor HideSpot`, `E_HideType HideType` | `Public` | Fired when player successfully hides |
|
||||||
|
| `OnExitedHidingSpot` | `AActor HideSpot` | `Public` | Fired when player leaves hiding |
|
||||||
|
| `OnPeekStarted` | `E_PeekDirection Direction` | `Public` | Fired when player starts peeking |
|
||||||
|
| `OnPeekEnded` | `none` | `Public` | Fired when player stops peeking |
|
||||||
|
| `OnHideSpotCompromised` | `AActor EnemyActor` | `Public` | Fired when an enemy sees the player's spot |
|
||||||
|
| `OnForcedExitWarning` | `float TimeUntilDiscovered` | `Public` | Fired when enemy is approaching known spot |
|
||||||
|
| `OnBreathHoldChanged` | `bool bIsHoldingBreath` | `Public` | Fired when breath hold toggles |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Overridden Events / Custom Events
|
||||||
|
|
||||||
|
### Event: `BeginPlay`
|
||||||
|
- **Description:** Initialises state, binds to input actions.
|
||||||
|
- **Flow:**
|
||||||
|
1. Set CurrentHideState = Exposed
|
||||||
|
2. Register with GI_GameTagRegistry if needed
|
||||||
|
3. Cache reference to CharacterMovementComponent for disabling during hide
|
||||||
|
|
||||||
|
### Custom Event: `ForceKickFromHide`
|
||||||
|
- **Description:** Called externally (e.g., by enemy discovery) to force player out of hiding.
|
||||||
|
- **Flow:**
|
||||||
|
1. Call ExitHideSpot(bForceExit = true)
|
||||||
|
2. Apply brief stun or camera shake
|
||||||
|
3. Fire OnForcedExitWarning with 0 seconds
|
||||||
|
|
||||||
|
### Custom Event: `OnDamageWhileHiding`
|
||||||
|
- **Description:** Listener for when damage is taken while hidden.
|
||||||
|
- **Flow:**
|
||||||
|
1. If damage source penetrates cover: force exit
|
||||||
|
2. Else: reduce stress instead of health (environmental damage)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blueprint Graph Logic Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[EnterHideSpot called] --> B{Is Exposed?}
|
||||||
|
B -->|No| C[Return false]
|
||||||
|
B -->|Yes| D[Validate I_HidingSpot]
|
||||||
|
D --> E{Has available slots?}
|
||||||
|
E -->|No| F[Return false]
|
||||||
|
E -->|Yes| G[Set Entering state]
|
||||||
|
G --> H[Disable movement input]
|
||||||
|
H --> I[Play Enter animation]
|
||||||
|
I --> J[Animation notify: OnComplete]
|
||||||
|
J --> K[Set Hidden state]
|
||||||
|
K --> L[Start stress decay loop]
|
||||||
|
L --> M[Start periodic LOS check]
|
||||||
|
M --> N[Fire OnEnteredHidingSpot]
|
||||||
|
N --> O[Return true]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Communication Matrix
|
||||||
|
|
||||||
|
| Who Talks | How | What Is Sent |
|
||||||
|
|-----------|-----|-------------|
|
||||||
|
| `BPC_HidingSystem` | `Dispatcher` | `OnHideStateChanged` -> `BPC_PlayerController` (input), `ABP_GASP` (anim), `BPC_StressSystem` (decay), `AI_EnemyController` (awareness reset) |
|
||||||
|
| `BPC_HidingSystem` | `Dispatcher` | `OnHideSpotCompromised` -> `BPC_StressSystem` (stress spike), `BP_AudioManager` (tension music) |
|
||||||
|
| `BPC_HidingSystem` | `Dispatcher` | `OnForcedExitWarning` -> `WBP_HUD` (hide icon flash), `BP_AudioManager` (heartbeat) |
|
||||||
|
| `External (AI)` | `Query` | `IsPlayerDetectable` called by `AI_EnemyController` for LOS checks |
|
||||||
|
| `External (World)` | `Interface` | `I_HidingSpot` on `BP_Locker`, `BP_Wardrobe`, `BP_Cover` |
|
||||||
|
| `BPC_HidingSystem` | `Dispatcher` | `OnPeekStarted/Ended` -> `BPC_CameraStateLayer` (FOV/position) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Validation / Testing Checklist
|
||||||
|
|
||||||
|
- [ ] EnterHideSpot correctly transitions through Entering -> Hidden
|
||||||
|
- [ ] ExitHideSpot correctly transitions through Exiting -> Exposed
|
||||||
|
- [ ] Forced exit with bForceExit = true skips animation, immediately exits
|
||||||
|
- [ ] Peek timer forces player back to Hidden after MaxPeekDuration
|
||||||
|
- [ ] PeekCooldown prevents rapid peek toggling
|
||||||
|
- [ ] IsPlayerDetectable returns correct values for all hide types and peek states
|
||||||
|
- [ ] Multiple hide spots with limited slots: full slots reject entry
|
||||||
|
- [ ] Stress decays while hidden at configured rate
|
||||||
|
- [ ] LOS check detects enemies within range and fires compromise dispatcher
|
||||||
|
- [ ] Edge case: EnterHideSpot called while already hiding returns false
|
||||||
|
- [ ] Edge case: ExitHideSpot called while Exposed does nothing
|
||||||
|
- [ ] Edge case: Destroying hide spot while inside force-exits player
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Reuse Notes
|
||||||
|
|
||||||
|
- Enemy AI characters can use a simplified version to find and occupy hide spots.
|
||||||
|
- Hide spots can be pre-placed (lockers, beds) or dynamic (shadow volumes, tall grass).
|
||||||
|
- The LOS trace channel should be custom (e.g., DetectionChannel) to ignore small props.
|
||||||
|
- Breath-hold mechanic adds depth to stealth gameplay near enemies.
|
||||||
|
- For multiplayer: only the hiding player knows their state; other players see a generic "occupado" on the spot.
|
||||||
|
- Hide spots with bBlocksStress = true can double as narrative safe rooms.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Blueprint Spec: Hiding System. Conforms to TEMPLATE.md v1.0 — part of the UE5 Modular Game Framework.*
|
||||||
258
docs/blueprints/02-player/13_BPC_EmbodimentSystem.md
Normal file
258
docs/blueprints/02-player/13_BPC_EmbodimentSystem.md
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
# 13 — Embodiment System (`BPC_EmbodimentSystem`)
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
Creates a sense of first-person body awareness — the player can see their own body, shadow, arms, and legs in appropriate contexts. Manages body visibility, shadow casting, limb IK, and environmental body interactions (body brushing against walls, water drip on arms, blood spatter on hands).
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **Requires:** `BPC_MovementStateSystem` (posture affects body visibility), `BPC_HealthSystem` (blood spatter based on damage)
|
||||||
|
- **Required By:** `BPC_CameraStateLayer` (head height offsets), `BP_PlayerCharacter` (component composition)
|
||||||
|
- **Engine/Plugin Requirements:** GASP (arms animation), Material Parameter Collections (blood/water overlays)
|
||||||
|
|
||||||
|
## Class Info
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Parent Class** | `ActorComponent` |
|
||||||
|
| **Class Type** | Blueprint Component |
|
||||||
|
| **Asset Path** | `Content/Framework/Player/BPC_EmbodimentSystem` |
|
||||||
|
| **Implements Interfaces** | None |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Enums
|
||||||
|
|
||||||
|
### `E_BodyVisibilityMode`
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `FullBody = 0` | Full body visible (third person, mirrors, death) |
|
||||||
|
| `ArmsOnly = 1` | Only arms/hands visible (default first person) |
|
||||||
|
| `ArmsAndShadow = 2` | Arms + body shadow visible on ground |
|
||||||
|
| `Minimal = 3` | Only hands visible (high stress, underwater) |
|
||||||
|
| `Hidden = 4` | Body fully hidden (cutscene, UI menu) |
|
||||||
|
|
||||||
|
### `E_BodyOverlayState`
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Clean = 0` | No overlays |
|
||||||
|
| `BloodSpatter = 1` | Blood on hands/arms |
|
||||||
|
| `WaterDroplets = 2` | Water on arms/camera |
|
||||||
|
| `Mud = 3` | Mud/dirt on body |
|
||||||
|
| `Toxic = 4` | Green/glowing contamination |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Structs
|
||||||
|
|
||||||
|
### `S_BodyOverlay`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `OverlayType` | `E_BodyOverlayState` | Which overlay to apply |
|
||||||
|
| `MaterialParameter` | `FName` | Parameter name on the material |
|
||||||
|
| `TargetValue` | `Float` | Target intensity [0..1] |
|
||||||
|
| `BlendSpeed` | `Float` | How fast to blend to target |
|
||||||
|
| `Duration` | `Float` | How long the overlay lasts before fading |
|
||||||
|
|
||||||
|
### `S_BodyPartVisibility`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `bShowArms` | `Boolean` | Whether arms are visible |
|
||||||
|
| `bShowLegs` | `Boolean` | Whether legs are visible |
|
||||||
|
| `bShowShadow` | `Boolean` | Whether body shadow is visible |
|
||||||
|
| `bShowTorso` | `Boolean` | Whether torso is visible (mirrors) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
|
||||||
|
### Configuration (Instance Editable, Expose On Spawn)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `DefaultVisibility` | `E_BodyVisibilityMode` | `ArmsOnly` | `Embodiment Config` | Default visibility for normal gameplay |
|
||||||
|
| `ArmsMesh` | `SkeletalMeshComponent` | `None` | `Embodiment Config` | First-person arms mesh reference |
|
||||||
|
| `BodyShadowComponent` | `UShadowComponent` | `None` | `Embodiment Config` | Shadow-only body component |
|
||||||
|
| `bEnableBodyBrushing` | `Boolean` | `true` | `Embodiment Config` | Arms react to nearby walls |
|
||||||
|
| `BrushTraceDistance` | `Float` | `30.0` | `Embodiment Config` | Distance to trace for wall detection |
|
||||||
|
| `OverlayBlendGlobalSpeed` | `Float` | `2.0` | `Embodiment Config` | Default blend speed for all overlays |
|
||||||
|
|
||||||
|
### Internal (Private / Protected, No Expose)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `CurrentVisibility` | `E_BodyVisibilityMode` | `ArmsOnly` | `Embodiment State` | Current visibility mode |
|
||||||
|
| `CurrentOverlay` | `E_BodyOverlayState` | `Clean` | `Embodiment State` | Current active overlay |
|
||||||
|
| `OverlayIntensity` | `Float` | `0.0` | `Embodiment State` | Current overlay intensity [0..1] |
|
||||||
|
| `bIsNearWall` | `Boolean` | `false` | `Embodiment State` | Whether wall is near for arm brushing |
|
||||||
|
| `LastOverLayTime` | `Float` | `0.0` | `Embodiment State` | When last overlay was applied |
|
||||||
|
| `ActiveOverlays` | `Map<E_BodyOverlayState, S_BodyOverlay>` | `{}` | `Embodiment State` | Currently active overlays with blend info |
|
||||||
|
| `OverlayTimerHandle` | `FTimerHandle` | `-` | `Embodiment State` | Timer for overlay fade-out |
|
||||||
|
|
||||||
|
### Replicated (if multiplayer)
|
||||||
|
|
||||||
|
| Variable | Type | Condition | Description |
|
||||||
|
|----------|------|-----------|-------------|
|
||||||
|
| `CurrentVisibility` | `E_BodyVisibilityMode` | `Replicated` | Synced visibility mode |
|
||||||
|
| `CurrentOverlay` | `E_BodyOverlayState` | `Replicated` | Synced overlay state |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
### Public Functions
|
||||||
|
|
||||||
|
#### `SetVisibilityMode` → `void`
|
||||||
|
- **Description:** Sets body visibility mode and toggles mesh components.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `NewMode` | `E_BodyVisibilityMode` | Desired visibility mode |
|
||||||
|
| `bInstant` | `Boolean` | If true, skip fade transition |
|
||||||
|
- **Flow:**
|
||||||
|
1. CurrentVisibility = NewMode
|
||||||
|
2. Apply S_BodyPartVisibility based on NewMode
|
||||||
|
3. Set ArmsMesh visibility based on bShowArms
|
||||||
|
4. Set BodyShadowComponent visibility based on bShowShadow
|
||||||
|
5. Fire OnVisibilityModeChanged
|
||||||
|
|
||||||
|
#### `ApplyOverlay` → `void`
|
||||||
|
- **Description:** Applies a body overlay (blood, water, mud) with blend.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Overlay` | `S_BodyOverlay` | Overlay to apply |
|
||||||
|
- **Flow:**
|
||||||
|
1. Add or update ActiveOverlays[Overlay.OverlayType]
|
||||||
|
2. Start blend toward TargetValue at Overlay.BlendSpeed
|
||||||
|
3. If Overlay.Duration > 0: start timer to fade out after Duration
|
||||||
|
4. Update material parameter on ArmsMesh
|
||||||
|
5. Fire OnOverlayChanged
|
||||||
|
|
||||||
|
#### `ClearOverlay` → `void`
|
||||||
|
- **Description:** Clears a specific overlay or all overlays.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `OverlayType` | `E_BodyOverlayState` | Type to clear; None = clear all |
|
||||||
|
- **Flow:**
|
||||||
|
1. If OverlayType specified: remove single overlay, blend intensity to 0
|
||||||
|
2. If OverlayType == None: clear all overlays, reset to Clean
|
||||||
|
3. Update material parameters
|
||||||
|
4. Fire OnOverlayChanged
|
||||||
|
|
||||||
|
#### `CheckWallProximity` → `void`
|
||||||
|
- **Description:** Traces forward from camera to detect nearby walls for arm brushing.
|
||||||
|
- **Flow:**
|
||||||
|
1. Perform line trace from camera forward by BrushTraceDistance
|
||||||
|
2. If hit: bIsNearWall = true, apply arm IK offset
|
||||||
|
3. If no hit: bIsNearWall = false, relax arm IK
|
||||||
|
4. Fire OnWallProximityChanged
|
||||||
|
|
||||||
|
#### `GetBodyPartVisibility` → `S_BodyPartVisibility`
|
||||||
|
- **Description:** Returns current visibility state for all body parts.
|
||||||
|
- **Flow:** Return struct derived from CurrentVisibility
|
||||||
|
|
||||||
|
### Protected / Private Functions
|
||||||
|
|
||||||
|
#### `ApplyOverlayBlendTick` → `void`
|
||||||
|
- **Description:** Called each tick — blends all active overlay material parameters.
|
||||||
|
- **Flow:**
|
||||||
|
1. For each ActiveOverlay: blend material parameter toward TargetValue
|
||||||
|
2. If blend complete and Duration expired: begin fade to 0
|
||||||
|
3. Set material parameters on ArmsMesh
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Parameters | Bind Access | Description |
|
||||||
|
|------------|-----------|-------------|-------------|
|
||||||
|
| `OnVisibilityModeChanged` | `E_BodyVisibilityMode OldMode`, `E_BodyVisibilityMode NewMode` | `Public` | Fired when body visibility changes |
|
||||||
|
| `OnOverlayChanged` | `E_BodyOverlayState CurrentOverlay`, `float Intensity` | `Public` | Fired when overlay state changes |
|
||||||
|
| `OnWallProximityChanged` | `bool bIsNearWall`, `float Distance` | `Public` | Fired when wall proximity changes |
|
||||||
|
| `OnBodyPartHit` | `FName BoneName`, `S_DamageEvent DamageEvent` | `Public` | Fired when a visible body part is hit |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Overridden Events / Custom Events
|
||||||
|
|
||||||
|
### Event: `BeginPlay`
|
||||||
|
- **Description:** Caches mesh references, sets initial visibility.
|
||||||
|
- **Flow:**
|
||||||
|
1. Find ArmsMesh on owner (first-person skeletal mesh)
|
||||||
|
2. Find BodyShadowComponent
|
||||||
|
3. Set initial visibility to DefaultVisibility
|
||||||
|
4. Bind to BPC_HealthSystem.OnDamageTaken for blood overlay
|
||||||
|
5. Start wall proximity tick if bEnableBodyBrushing
|
||||||
|
|
||||||
|
### Custom Event: `OnDamageTakenBloodHandler`
|
||||||
|
- **Description:** Applies blood spatter overlay when player takes damage.
|
||||||
|
- **Flow:**
|
||||||
|
1. Intensity = FMath::Min(DamageEvent.BaseAmount / 50.0, 1.0)
|
||||||
|
2. Build S_BodyOverlay with BloodSpatter type
|
||||||
|
3. Call ApplyOverlay
|
||||||
|
|
||||||
|
### Custom Event: `OnEnterWaterHandler`
|
||||||
|
- **Description:** Fired when player enters water volume.
|
||||||
|
- **Flow:**
|
||||||
|
1. Apply WaterDroplets overlay with fast blend and short duration
|
||||||
|
2. Start drying timer for overlay fade
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blueprint Graph Logic Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[SetVisibilityMode called] --> B{NewMode == Current?}
|
||||||
|
B -->|Yes| C[Return]
|
||||||
|
B -->|No| D[Determine BodyPartVisibility]
|
||||||
|
D --> E{NewMode == ArmsOnly?}
|
||||||
|
E -->|Yes| F[Enable Arms, disable shadow/torso]
|
||||||
|
E -->|No| G{NewMode == FullBody?}
|
||||||
|
G -->|Yes| H[Enable all body parts]
|
||||||
|
G -->|No| I{NewMode == Hidden?}
|
||||||
|
I -->|Yes| J[Disable all meshes]
|
||||||
|
I -->|No| K[Apply partial visibility]
|
||||||
|
F --> L[Fire OnVisibilityModeChanged]
|
||||||
|
H --> L
|
||||||
|
J --> L
|
||||||
|
K --> L
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Communication Matrix
|
||||||
|
|
||||||
|
| Who Talks | How | What Is Sent |
|
||||||
|
|-----------|-----|-------------|
|
||||||
|
| `BPC_EmbodimentSystem` | `Dispatcher` | `OnVisibilityModeChanged` -> `BPC_CameraStateLayer` (FOV adjust) |
|
||||||
|
| `BPC_EmbodimentSystem` | `Dispatcher` | `OnOverlayChanged` -> `WBP_HUD` (screen effects), `BP_AudioManager` |
|
||||||
|
| `BPC_EmbodimentSystem` | `Dispatcher` | `OnWallProximityChanged` -> `ABP_Arms` (IK offset) |
|
||||||
|
| `BPC_HealthSystem` | `Listener` | `OnDamageTaken` triggers blood overlay |
|
||||||
|
| `External (BP_WaterVolume)` | `Direct` | Calls `ApplyOverlay` for water effects |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Validation / Testing Checklist
|
||||||
|
|
||||||
|
- [ ] Visibility modes correctly toggle mesh component visibility
|
||||||
|
- [ ] Blood overlay appears on damage and fades over time
|
||||||
|
- [ ] Water overlay appears on water entry and dries gradually
|
||||||
|
- [ ] Wall proximity trace correctly detects nearby walls
|
||||||
|
- [ ] Arm IK offsets apply when wall is detected
|
||||||
|
- [ ] Edge case: Rapid visibility mode changes do not cause rendering glitches
|
||||||
|
- [ ] Edge case: Multiple overlays stack correctly (blood + water = combined)
|
||||||
|
- [ ] Edge case: Hidden mode hides all body components regardless of other settings
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Reuse Notes
|
||||||
|
|
||||||
|
- The overlay system uses Material Parameter Collections — the implementation should expose scalar parameters for blood/water/mud blend amounts.
|
||||||
|
- Wall proximity uses a simple line trace; more advanced IK requires the arms animation blueprint.
|
||||||
|
- For third-person modes, use a separate full-body skeletal mesh instead of the arms mesh.
|
||||||
|
- Overlay intensity can be driven by gameplay values: more damage = more blood, longer swim = more water.
|
||||||
|
- The body shadow component is a separate capsule with a shadow-only material — not a full character mesh.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Blueprint Spec: Embodiment System. Conforms to TEMPLATE.md v1.0 — part of the UE5 Modular Game Framework.*
|
||||||
329
docs/blueprints/02-player/14_BPC_CameraStateLayer.md
Normal file
329
docs/blueprints/02-player/14_BPC_CameraStateLayer.md
Normal file
@@ -0,0 +1,329 @@
|
|||||||
|
# 14 — Camera State Layer (`BPC_CameraStateLayer`)
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
Manages the first-person camera's dynamic behaviour: field-of-view changes based on movement speed, camera shake from impacts/events, head bob intensity, and location offsets for crouch/prone/hide. Centralises all camera-modifying logic so other systems can request camera changes without directly manipulating the camera.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **Requires:** `BPC_MovementStateSystem` (speed-based FOV), `BPC_StressSystem` (stress-based distortion)
|
||||||
|
- **Required By:** `PC_PlayerController` (camera management), `BPC_HidingSystem` (peek camera offsets), `WBP_HUD` (post-process materials)
|
||||||
|
- **Engine/Plugin Requirements:** Camera Shake patterns, Post-Process Materials, Material Parameter Collections
|
||||||
|
|
||||||
|
## Class Info
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Parent Class** | `ActorComponent` |
|
||||||
|
| **Class Type** | Blueprint Component |
|
||||||
|
| **Asset Path** | `Content/Framework/Player/BPC_CameraStateLayer` |
|
||||||
|
| **Implements Interfaces** | None |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Enums
|
||||||
|
|
||||||
|
### `E_CameraState`
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Default = 0` | Normal gameplay FOV |
|
||||||
|
| `Aiming = 1` | Zoomed in (weapon aim, inspection) |
|
||||||
|
| `Sprinting = 2` | FOV widened for speed |
|
||||||
|
| `Crouching = 3` | Slight downward offset |
|
||||||
|
| `Peeking = 4` | Offset sideways from cover |
|
||||||
|
| `Hiding = 5` | Constricted FOV, darkness from inside |
|
||||||
|
| `Stressed = 6` | FOV pulsing, chromatic aberration |
|
||||||
|
| `Injured = 7` | FOV wobble, blood tint |
|
||||||
|
| `Death = 8` | Fade to black, camera drop |
|
||||||
|
| `Cutscene = 9` | Fixed camera, no player control |
|
||||||
|
|
||||||
|
### `E_CameraShakePriority`
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Low = 0` | Ambient shake (footstep, wind) |
|
||||||
|
| `Medium = 1` | Impact shake (damage, explosion) |
|
||||||
|
| `High = 2` | Critical shake (near-death, supernatural event) |
|
||||||
|
| `Cinematic = 3` | Scripted shake, overrides everything |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Structs
|
||||||
|
|
||||||
|
### `S_CameraShakeRequest`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `ShakeClass` | `TSubclassOf<UCameraShakeBase>` | Camera shake blueprint class |
|
||||||
|
| `Scale` | `Float` | Intensity scale [0..1] |
|
||||||
|
| `Priority` | `E_CameraShakePriority` | Priority level |
|
||||||
|
| `Duration` | `Float` | How long the shake lasts |
|
||||||
|
| `bIsLooping` | `Boolean` | Whether the shake loops |
|
||||||
|
| `Tag` | `GameplayTag` | Unique identifier to prevent stacking same shake |
|
||||||
|
|
||||||
|
### `S_CameraStateConfig`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `TargetFOV` | `Float` | FOV value for this state |
|
||||||
|
| `BlendSpeed` | `Float` | How fast to blend to this FOV |
|
||||||
|
| `HeadOffset` | `Vector` | Location offset for head/camera |
|
||||||
|
| `HeadRotation` | `Rotator` | Rotation offset |
|
||||||
|
| `bConstrainPitch` | `Boolean` | Limit vertical look in this state |
|
||||||
|
| `MinPitch` | `Float` | Minimum pitch angle |
|
||||||
|
| `MaxPitch` | `Float` | Maximum pitch angle |
|
||||||
|
| `bEnableHeadBob` | `Boolean` | Enable head bob in this state |
|
||||||
|
| `HeadBobAmplitude` | `Float` | Head bob strength |
|
||||||
|
| `HeadBobFrequency` | `Float` | Head bob speed |
|
||||||
|
|
||||||
|
### `S_PostProcessOverride`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `bOverrideVignette` | `Boolean` | Override vignette intensity |
|
||||||
|
| `VignetteIntensity` | `Float` | Vignette strength [0..1] |
|
||||||
|
| `bOverrideChromaticAberration` | `Boolean` | Override CA intensity |
|
||||||
|
| `ChromaticAberrationIntensity` | `Float` | CA strength [0..1] |
|
||||||
|
| `bOverrideColorGrading` | `Boolean` | Override color grading |
|
||||||
|
| `ColorGradingLUT` | `Texture` | LUT texture for color grading |
|
||||||
|
| `BlendTime` | `Float` | Time to blend to new post-process settings |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
|
||||||
|
### Configuration (Instance Editable, Expose On Spawn)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `DefaultFOV` | `Float` | `90.0` | `Camera Config` | Default first-person FOV |
|
||||||
|
| `StateConfigs` | `Map<E_CameraState, S_CameraStateConfig>` | `all states` | `Camera Config` | FOV/offset values per state |
|
||||||
|
| `DefaultPostProcess` | `S_PostProcessOverride` | `-` | `Camera Config` | Default post-process settings |
|
||||||
|
| `BaseHeadBobAmplitude` | `Float` | `0.1` | `Camera Config` | Default head bob strength |
|
||||||
|
| `BaseHeadBobFrequency` | `Float` | `1.0` | `Camera Config` | Default head bob speed |
|
||||||
|
| `SprintFOVMultiplier` | `Float` | `1.1` | `Camera Config` | FOV multiplied by this when sprinting |
|
||||||
|
| `StressFOVPulseAmount` | `Float` | `5.0` | `Camera Config` | FOV pulse range when stressed (degrees) |
|
||||||
|
| `StressFOVPulseSpeed` | `Float` | `0.5` | `Camera Config` | Speed of stress FOV pulse |
|
||||||
|
|
||||||
|
### Internal (Private / Protected, No Expose)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `CurrentCameraState` | `E_CameraState` | `Default` | `Camera State` | Active camera state |
|
||||||
|
| `CurrentTargetFOV` | `Float` | `90.0` | `Camera State` | Current FOV target being blended to |
|
||||||
|
| `CurrentFOV` | `Float` | `90.0` | `Camera State` | Actual current FOV |
|
||||||
|
| `ActiveShakeRequests` | `Map<FName, S_CameraShakeRequest>` | `{}` | `Camera State` | Active shakes by tag |
|
||||||
|
| `HighestShakePriority` | `E_CameraShakePriority` | `Low` | `Camera State` | Current highest priority shake |
|
||||||
|
| `CameraOffsetTarget` | `Vector` | `(0,0,0)` | `Camera State` | Target head offset being blended to |
|
||||||
|
| `CameraRotationTarget` | `Rotator` | `(0,0,0)` | `Camera State` | Target rotation offset |
|
||||||
|
| `bIsPitchConstrained` | `Boolean` | `false` | `Camera State` | Whether pitch is currently constrained |
|
||||||
|
| `PostProcessBlendAlpha` | `Float` | `0.0` | `Camera State` | Current post-process blend |
|
||||||
|
| `FOVTimerHandle` | `FTimerHandle` | `-` | `Camera State` | Timer for FOV pulse when stressed |
|
||||||
|
| `PlayerCameraManager` | `APlayerCameraManager` | `None` | `Camera State` | Cached camera manager reference |
|
||||||
|
|
||||||
|
### Replicated (if multiplayer)
|
||||||
|
|
||||||
|
| Variable | Type | Condition | Description |
|
||||||
|
|----------|------|-----------|-------------|
|
||||||
|
| `CurrentCameraState` | `E_CameraState` | `Replicated` | Synced camera state |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
### Public Functions
|
||||||
|
|
||||||
|
#### `RequestCameraState` → `void`
|
||||||
|
- **Description:** Sets the camera to a state config. Multiple states can stack; highest priority wins.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `NewState` | `E_CameraState` | Desired camera state |
|
||||||
|
| `bImmediate` | `Boolean` | Skip blend |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
- **Flow:**
|
||||||
|
1. Store CurrentCameraState as PreviousState
|
||||||
|
2. CurrentCameraState = NewState
|
||||||
|
3. Get S_CameraStateConfig for NewState
|
||||||
|
4. Set CurrentTargetFOV to config.TargetFOV
|
||||||
|
5. Set CameraOffsetTarget to config.HeadOffset
|
||||||
|
6. Set CameraRotationTarget to config.HeadRotation
|
||||||
|
7. Set pitch constraints from config
|
||||||
|
8. Begin blending toward targets over BlendSpeed
|
||||||
|
9. Fire OnCameraStateChanged
|
||||||
|
|
||||||
|
#### `PlayCameraShake` → `void`
|
||||||
|
- **Description:** Plays a camera shake, respecting priority to prevent shake stacking.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `ShakeRequest` | `S_CameraShakeRequest` | Shake parameters |
|
||||||
|
- **Flow:**
|
||||||
|
1. If ShakeRequest.Tag already in ActiveShakeRequests:
|
||||||
|
- If new priority >= existing: replace existing shake
|
||||||
|
- If new priority < existing: ignore
|
||||||
|
2. Ask PlayerCameraManager to play shake
|
||||||
|
3. Store in ActiveShakeRequests
|
||||||
|
4. If Duration > 0: start timer to auto-stop shake after Duration
|
||||||
|
|
||||||
|
#### `StopCameraShake` → `void`
|
||||||
|
- **Description:** Stops a specific camera shake.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `ShakeTag` | `GameplayTag` | Tag identifying the shake |
|
||||||
|
- **Flow:**
|
||||||
|
1. If ShakeTag exists in ActiveShakeRequests:
|
||||||
|
- Stop the shake in PlayerCameraManager
|
||||||
|
- Remove from ActiveShakeRequests
|
||||||
|
|
||||||
|
#### `ApplyPostProcessOverride` → `void`
|
||||||
|
- **Description:** Applies temporary post-process settings (vignette, CA, color grading).
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Override` | `S_PostProcessOverride` | Post-process settings |
|
||||||
|
| `bInstant` | `Boolean` | If true, blend instantly |
|
||||||
|
- **Flow:**
|
||||||
|
1. If bInstant: apply immediately
|
||||||
|
2. Else: start blend over Override.BlendTime
|
||||||
|
3. Update Material Parameter Collection values
|
||||||
|
|
||||||
|
#### `ClearPostProcessOverride` → `void`
|
||||||
|
- **Description:** Returns post-process to default settings.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `BlendTime` | `Float` | Seconds to blend back to default |
|
||||||
|
- **Flow:**
|
||||||
|
1. Blend all post-process parameters to 0 / neutral over BlendTime
|
||||||
|
|
||||||
|
#### `GetCameraStateConfig` → `S_CameraStateConfig`
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `State` | `E_CameraState` | Which state to query |
|
||||||
|
- **Flow:** Return StateConfigs[State]
|
||||||
|
|
||||||
|
### Protected / Private Functions
|
||||||
|
|
||||||
|
#### `BlendToTargetFOV (Tick)` → `void`
|
||||||
|
- **Description:** Each tick, blend CurrentFOV toward CurrentTargetFOV.
|
||||||
|
- **Flow:**
|
||||||
|
1. Delta = CurrentTargetFOV - CurrentFOV
|
||||||
|
2. If Abs(Delta) < 0.1: CurrentFOV = CurrentTargetFOV
|
||||||
|
3. Else: CurrentFOV += Delta * BlendSpeed * DeltaTime
|
||||||
|
4. Apply CurrentFOV to PlayerCameraManager
|
||||||
|
|
||||||
|
#### `BlendCameraOffset (Tick)` → `void`
|
||||||
|
- **Description:** Each tick, blend camera location/rotation toward target.
|
||||||
|
- **Flow:**
|
||||||
|
1. InterpVTo(CameraOffsetTarget) for location
|
||||||
|
2. InterpRTo(CameraRotationTarget) for rotation
|
||||||
|
3. Apply offset to camera boom or view target
|
||||||
|
|
||||||
|
#### `UpdateStressPulse` → `void`
|
||||||
|
- **Description:** If stressed, pulse FOV for uneasy effect.
|
||||||
|
- **Flow:**
|
||||||
|
1. If CurrentCameraState != Stressed: return
|
||||||
|
2. PulseValue = Sin(Time * StressFOVPulseSpeed * PI * 2) * StressFOVPulseAmount
|
||||||
|
3. CurrentTargetFOV = StateConfigs[Stressed].TargetFOV + PulseValue
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Parameters | Bind Access | Description |
|
||||||
|
|------------|-----------|-------------|-------------|
|
||||||
|
| `OnCameraStateChanged` | `E_CameraState OldState`, `E_CameraState NewState` | `Public` | Fired when camera state changes |
|
||||||
|
| `OnFOVChanged` | `float OldFOV`, `float NewFOV` | `Public` | Fired when FOV changes |
|
||||||
|
| `OnShakeStarted` | `GameplayTag ShakeTag`, `float Scale` | `Public` | Fired when a camera shake begins |
|
||||||
|
| `OnShakeEnded` | `GameplayTag ShakeTag` | `Public` | Fired when a camera shake ends |
|
||||||
|
| `OnPostProcessChanged` | `float Vignette`, `float ChromaticAb` | `Public` | Fired on post-process change |
|
||||||
|
| `OnCameraConstrained` | `bool bIsConstrained`, `float MinPitch`, `float MaxPitch` | `Public` | Fired when pitch constraints change |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Overridden Events / Custom Events
|
||||||
|
|
||||||
|
### Event: `BeginPlay`
|
||||||
|
- **Description:** Cache camera manager, set initial FOV, bind to state systems.
|
||||||
|
- **Flow:**
|
||||||
|
1. Get PlayerCameraManager from owner's controller
|
||||||
|
2. Set CurrentFOV = DefaultFOV
|
||||||
|
3. Apply to camera
|
||||||
|
4. Bind to BPC_MovementStateSystem.OnMovementModeChanged
|
||||||
|
5. Bind to BPC_StressSystem.OnStressTierChanged
|
||||||
|
|
||||||
|
### Custom Event: `OnMovementModeChangedHandler`
|
||||||
|
- **Description:** Reacts to movement changes for FOV adjustments.
|
||||||
|
- **Flow:**
|
||||||
|
1. If Sprinting: RequestCameraState(Sprinting)
|
||||||
|
2. If Crouching: RequestCameraState(Crouching)
|
||||||
|
3. If Idle/Walking: RequestCameraState(Default)
|
||||||
|
|
||||||
|
### Custom Event: `OnStressTierChangedHandler`
|
||||||
|
- **Description:** Reacts to stress tier changes for camera effects.
|
||||||
|
- **Flow:**
|
||||||
|
1. If tier >= Panicked: RequestCameraState(Stressed), apply CA post-process
|
||||||
|
2. If tier < Distressed: return to Default camera, clear post-process
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blueprint Graph Logic Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[RequestCameraState called] --> B{NewState == Current?}
|
||||||
|
B -->|Yes| C[Return]
|
||||||
|
B -->|No| D[Look up S_CameraStateConfig]
|
||||||
|
D --> E[Set FOV target]
|
||||||
|
E --> F[Set head offset/rotation targets]
|
||||||
|
F --> G[Set pitch constraints]
|
||||||
|
G --> H[Fire OnCameraStateChanged]
|
||||||
|
H --> I[BlendTick: each frame]
|
||||||
|
I --> J[Interp FOV toward target]
|
||||||
|
J --> K[Interp offset toward target]
|
||||||
|
K --> L[Apply to PlayerCameraManager]
|
||||||
|
L --> M{Is Stressed state?}
|
||||||
|
M -->|Yes| N[Apply FOV pulse via sine wave]
|
||||||
|
M -->|No| O[Clear pulse effect]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Communication Matrix
|
||||||
|
|
||||||
|
| Who Talks | How | What Is Sent |
|
||||||
|
|-----------|-----|-------------|
|
||||||
|
| `BPC_CameraStateLayer` | `Dispatcher` | `OnCameraStateChanged` -> `ABP_GASP` (anim offset), `WBP_HUD` (post-process) |
|
||||||
|
| `BPC_CameraStateLayer` | `Dispatcher` | `OnShakeStarted` / `OnShakeEnded` -> `BP_AudioManager` |
|
||||||
|
| `BPC_CameraStateLayer` | `Listener` | Binds to `BPC_MovementStateSystem.OnMovementModeChanged` for FOV |
|
||||||
|
| `BPC_CameraStateLayer` | `Listener` | Binds to `BPC_StressSystem.OnStressTierChanged` for distortion |
|
||||||
|
| `BPC_CameraStateLayer` | `Listener` | Binds to `BPC_HealthSystem.OnDamageTaken` for injury shake |
|
||||||
|
| `External (Any)` | `Direct` | Calls `PlayCameraShake` for any gameplay event |
|
||||||
|
| `BPC_HidingSystem` | `Direct` | Calls `RequestCameraState(Peeking)` with peek offset |
|
||||||
|
| `BPC_HidingSystem` | `Direct` | Calls `RequestCameraState(Hiding)` with hide FOV |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Validation / Testing Checklist
|
||||||
|
|
||||||
|
- [ ] FOV blends smoothly between states at configured speed
|
||||||
|
- [ ] Sprinting FOV wider than Default, returns on stop
|
||||||
|
- [ ] Camera shake priority prevents lower-priority shakes from overriding high-priority ones
|
||||||
|
- [ ] Stress pulsing creates smooth FOV oscillation without jitter
|
||||||
|
- [ ] Post-process overrides blend in/out correctly
|
||||||
|
- [ ] Pitch constraints apply correctly in hiding/peeking states
|
||||||
|
- [ ] Multiple state changes are queued — last request wins
|
||||||
|
- [ ] Edge case: Rapid state switching between sprint and crouch transitions smoothly
|
||||||
|
- [ ] Edge case: Camera shake with same tag replaces shake without stacking
|
||||||
|
- [ ] Edge case: ClearPostProcessOverride returns to default with correct blend
|
||||||
|
- [ ] All active shakes stop on component destroy or pawn unpossess
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Reuse Notes
|
||||||
|
|
||||||
|
- Camera shake patterns should be authored as Blueprint Camera Shake classes in Content/Framework/Player/Shakes/.
|
||||||
|
- Post-process settings use a Material Parameter Collection (MPC_CameraEffects) for real-time parameter updates.
|
||||||
|
- The stress FOV pulse creates a subtle "unease" effect — pair with audio breathing changes for best results.
|
||||||
|
- For multiplayer: camera shakes are local-only; state changes replicate for shared visual consistency.
|
||||||
|
- Custom camera states can be added by extending E_CameraState and adding a config entry.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Blueprint Spec: Camera State Layer. Conforms to TEMPLATE.md v1.0 — part of the UE5 Modular Game Framework.*
|
||||||
335
docs/blueprints/02-player/15_BPC_PlayerMetricsTracker.md
Normal file
335
docs/blueprints/02-player/15_BPC_PlayerMetricsTracker.md
Normal file
@@ -0,0 +1,335 @@
|
|||||||
|
# 15 — Player Metrics Tracker (`BPC_PlayerMetricsTracker`)
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
Records and exposes player behaviour telemetry throughout a play session. Metrics feed into the adaptive difficulty system, achievement tracking, end-game stats screen, and narrative pacing adjustments. This is a pure data-gathering component — it never blocks or modifies gameplay directly.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **Requires:** `GI_GameFramework` (session start/end events), `DA_GameDifficulty` (adaptive thresholds)
|
||||||
|
- **Required By:** `BPC_AdaptiveDifficulty` (consumes metrics), `WBP_StatsScreen` (end-game display), `GM_CoreGameMode` (death/checkpoint metrics)
|
||||||
|
- **Engine/Plugin Requirements:** `FTimerHandle` for play time tracking
|
||||||
|
|
||||||
|
## Class Info
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Parent Class** | `ActorComponent` |
|
||||||
|
| **Class Type** | Blueprint Component |
|
||||||
|
| **Asset Path** | `Content/Framework/Player/BPC_PlayerMetricsTracker` |
|
||||||
|
| **Implements Interfaces** | `I_Persistable` (save/load metrics) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Enums
|
||||||
|
|
||||||
|
### `E_MetricEventType`
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Death = 0` | Player died |
|
||||||
|
| `HideStarted = 1` | Player entered a hide spot |
|
||||||
|
| `HideEnded = 2` | Player exited a hide spot |
|
||||||
|
| `ItemCollected = 3` | Player picked up an item |
|
||||||
|
| `EnemyEncountered = 4` | Player entered an enemy detection range |
|
||||||
|
| `EnemyEvaded = 5` | Player escaped enemy detection |
|
||||||
|
| `StressPeakReached = 6` | Stress tier exceeded Distressed |
|
||||||
|
| `AreaExplored = 7` | Player entered a new area/chapter |
|
||||||
|
| `NarrativeEvent = 8` | Story beat triggered |
|
||||||
|
| `CombatEvent = 9` | Player used a weapon or was attacked |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Structs
|
||||||
|
|
||||||
|
### `S_BehaviourEvent`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Timestamp` | `Float` | Game time in seconds when event occurred |
|
||||||
|
| `EventType` | `E_MetricEventType` | What happened |
|
||||||
|
| `ContextTag` | `GameplayTag` | Tagged context for filtering |
|
||||||
|
| `FloatValue` | `Float` | Numeric payload (e.g. distance, intensity) |
|
||||||
|
| `StringValue` | `String` | Text payload (e.g. location name, enemy ID) |
|
||||||
|
| `Location` | `Vector` | World location where event occurred |
|
||||||
|
|
||||||
|
### `S_MetricsSnapshot`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `TotalPlayTime` | `Float` | Total seconds played |
|
||||||
|
| `TimesDied` | `Integer` | Total deaths this session |
|
||||||
|
| `TimesHid` | `Integer` | Total hide events |
|
||||||
|
| `TotalDistanceWalked` | `Float` | Cumulative walking distance (cm) |
|
||||||
|
| `TotalDistanceSprinted` | `Float` | Cumulative sprinting distance (cm) |
|
||||||
|
| `ItemsCollected` | `Integer` | Total items picked up |
|
||||||
|
| `UniqueItemsCollected` | `Set<FName>` | Names of unique items collected |
|
||||||
|
| `EnemiesEncountered` | `Integer` | Total unique enemy encounters |
|
||||||
|
| `EnemiesEvaded` | `Integer` | Total successful evasions |
|
||||||
|
| `PeakStressTier` | `Float` | Highest stress tier reached (as float) |
|
||||||
|
| `StressPeakCount` | `Integer` | Times stress exceeded Distressed threshold |
|
||||||
|
| `NarrativeBeatsCompleted` | `Integer` | Story triggers activated |
|
||||||
|
| `AreasExplored` | `Set<FName>` | Unique areas/chapters visited |
|
||||||
|
| `CombatEvents` | `Integer` | Total combat interactions |
|
||||||
|
|
||||||
|
### `S_TimeSegment`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `SegmentLabel` | `String` | Label for this time segment |
|
||||||
|
| `StartTime` | `Float` | Game time at segment start |
|
||||||
|
| `EndTime` | `Float` | Game time at segment end |
|
||||||
|
| `StressAverage` | `Float` | Average stress during segment |
|
||||||
|
| `MetricsDelta` | `S_MetricsSnapshot` | Metrics accumulated in this segment |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
|
||||||
|
### Configuration (Instance Editable, Expose On Spawn)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `bTrackDetailedEvents` | `Boolean` | `true` | `Metrics Config` | If true, store full `S_BehaviourEvent` history |
|
||||||
|
| `MaxEventHistory` | `Integer` | `5000` | `Metrics Config` | Cap on stored events to prevent memory bloat |
|
||||||
|
| `bAutoSaveMetrics` | `Boolean` | `true` | `Metrics Config` | Auto-save metrics via I_Persistable on quit |
|
||||||
|
| `SnapshotInterval` | `Float` | `60.0` | `Metrics Config` | Seconds between automatic snapshot writes |
|
||||||
|
|
||||||
|
### Computed / Runtime (Public, Blueprint Read Only)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `CurrentSnapshot` | `S_MetricsSnapshot` | `-` | `Runtime Metrics` | Live accumulated metrics this session |
|
||||||
|
| `bIsTrackingSession` | `Boolean` | `false` | `Runtime Metrics` | Whether currently in a tracked session |
|
||||||
|
| `TimeSegmentHistory` | `Array<S_TimeSegment>` | `[]` | `Runtime Metrics` | Historical segments for analysis |
|
||||||
|
| `CurrentSegment` | `S_TimeSegment` | `-` | `Runtime Metrics` | Active time segment |
|
||||||
|
|
||||||
|
### Internal (Private / Protected, No Expose)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `SessionStartTime` | `Float` | `0.0` | `Timing` | Game time when session started |
|
||||||
|
| `CachedEventHistory` | `Array<S_BehaviourEvent>` | `[]` | `History` | Full event history (if tracking enabled) |
|
||||||
|
| `LastPosition` | `Vector` | `(0,0,0)` | `Tracking` | Previous frame position for distance calc |
|
||||||
|
| `SnapshotTimerHandle` | `FTimerHandle` | `-` | `Timing` | Timer for automatic snapshots |
|
||||||
|
|
||||||
|
### Replicated (if multiplayer)
|
||||||
|
|
||||||
|
| Variable | Type | Condition | Description |
|
||||||
|
|----------|------|-----------|-------------|
|
||||||
|
| `CurrentSnapshot` | `S_MetricsSnapshot` | `Replicated` | Synced session metrics |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
### Public Functions
|
||||||
|
|
||||||
|
#### `StartSession` → `void`
|
||||||
|
- **Description:** Begins a new metrics tracking session. Resets snapshot.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `SegmentLabel` | `String` | Label for initial segment (e.g. "Chapter 1") |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
- **Flow:**
|
||||||
|
1. Set SessionStartTime = Current Game Time
|
||||||
|
2. Reset CurrentSnapshot to zero values
|
||||||
|
3. Initialize CurrentSegment with label and start time
|
||||||
|
4. Set bIsTrackingSession = true
|
||||||
|
5. Start snapshot timer
|
||||||
|
6. Fire OnSessionStarted
|
||||||
|
|
||||||
|
#### `EndSession` → `S_MetricsSnapshot`
|
||||||
|
- **Description:** Ends the tracking session and returns final snapshot.
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Flow:**
|
||||||
|
1. Finalize CurrentSegment end time
|
||||||
|
2. Finalize CurrentSnapshot.PlayTime
|
||||||
|
3. Add CurrentSegment to TimeSegmentHistory
|
||||||
|
4. Set bIsTrackingSession = false
|
||||||
|
5. Clear snapshot timer
|
||||||
|
6. Fire OnSessionEnded
|
||||||
|
7. Return CurrentSnapshot
|
||||||
|
|
||||||
|
#### `RecordEvent` → `void`
|
||||||
|
- **Description:** Records a single behaviour event into history and updates snapshot.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `EventType` | `E_MetricEventType` | Type of event |
|
||||||
|
| `ContextTag` | `GameplayTag` | Filtering tag |
|
||||||
|
| `FloatValue` | `Float` | Numeric payload |
|
||||||
|
| `StringValue` | `String` | Text payload |
|
||||||
|
- **Flow:**
|
||||||
|
1. If not bIsTrackingSession: return
|
||||||
|
2. Create S_BehaviourEvent with current timestamp
|
||||||
|
3. If bTrackDetailedEvents and event count < MaxEventHistory:
|
||||||
|
- Add event to CachedEventHistory
|
||||||
|
4. Update CurrentSnapshot based on EventType:
|
||||||
|
- Death: TimesDied++
|
||||||
|
- HideStarted: TimesHid++
|
||||||
|
- ItemCollected: ItemsCollected++, add name to UniqueItemsCollected
|
||||||
|
- EnemyEncountered: EnemiesEncountered++
|
||||||
|
- EnemyEvaded: EnemiesEvaded++
|
||||||
|
- StressPeakReached: StressPeakCount++, update PeakStressTier if higher
|
||||||
|
- NarrativeEvent: NarrativeBeatsCompleted++
|
||||||
|
- AreaExplored: add to AreasExplored
|
||||||
|
- CombatEvent: CombatEvents++
|
||||||
|
5. Fire OnEventRecorded
|
||||||
|
|
||||||
|
#### `RecordDistanceTraveled` → `void`
|
||||||
|
- **Description:** Called by movement system or tick to log distance.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `DistanceDelta` | `Float` | Distance moved this frame |
|
||||||
|
| `bWasSprinting` | `Boolean` | Whether player was sprinting |
|
||||||
|
- **Flow:**
|
||||||
|
1. If bWasSprinting: CurrentSnapshot.TotalDistanceSprinted += Delta
|
||||||
|
2. Else: CurrentSnapshot.TotalDistanceWalked += Delta
|
||||||
|
|
||||||
|
#### `GetMetricsSnapshot` → `S_MetricsSnapshot`
|
||||||
|
- **Description:** Returns a copy of the current live snapshot.
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Flow:** Return CurrentSnapshot
|
||||||
|
|
||||||
|
#### `GetEventHistory` → `Array<S_BehaviourEvent>`
|
||||||
|
- **Description:** Returns recorded event history.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `bFiltered` | `Boolean` | If true, apply filter |
|
||||||
|
| `FilterType` | `E_MetricEventType` | Only return events of this type |
|
||||||
|
- **Flow:**
|
||||||
|
1. If bFiltered: return filtered CachedEventHistory by FilterType
|
||||||
|
2. Else: return CachedEventHistory
|
||||||
|
|
||||||
|
#### `GetPlayTimeFormatted` → `String`
|
||||||
|
- **Description:** Returns total play time as formatted string (HH:MM:SS).
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Flow:** Format SecondsToTimeString(SessionDuration)
|
||||||
|
|
||||||
|
### Protected / Private Functions
|
||||||
|
|
||||||
|
#### `OnTickDistance (Tick)` → `void`
|
||||||
|
- **Description:** Each tick, compute distance from LastPosition. Only active when tracking.
|
||||||
|
- **Flow:**
|
||||||
|
1. If not bIsTrackingSession: return
|
||||||
|
2. Get current actor location
|
||||||
|
3. Delta = Distance(LastPosition, CurrentLocation)
|
||||||
|
4. If Delta < 10.0: skip (noise threshold)
|
||||||
|
5. Check movement mode from BPC_MovementStateSystem
|
||||||
|
6. Call RecordDistanceTraveled(Delta, bWasSprinting)
|
||||||
|
7. Update LastPosition = CurrentLocation
|
||||||
|
|
||||||
|
#### `CreateSnapshot` → `void`
|
||||||
|
- **Description:** Timer callback to create periodic snapshots.
|
||||||
|
- **Flow:**
|
||||||
|
1. Copy CurrentSnapshot as snapshot
|
||||||
|
2. Finalize CurrentSegment end time
|
||||||
|
3. Store snapshot delta in CurrentSegment.MetricsDelta
|
||||||
|
4. Add CurrentSegment to TimeSegmentHistory
|
||||||
|
5. Start new CurrentSegment
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Parameters | Bind Access | Description |
|
||||||
|
|------------|-----------|-------------|-------------|
|
||||||
|
| `OnSessionStarted` | `String SegmentLabel` | `Public` | Fired when a tracking session begins |
|
||||||
|
| `OnSessionEnded` | `S_MetricsSnapshot FinalSnapshot` | `Public` | Fired when a tracking session ends |
|
||||||
|
| `OnEventRecorded` | `S_BehaviourEvent Event` | `Public` | Fired each time an event is recorded |
|
||||||
|
| `OnMetricsThresholdReached` | `E_MetricEventType EventType`, `int CurrentValue`, `int Threshold` | `Public` | Fired when a metric crosses a threshold |
|
||||||
|
| `OnSnapshotCreated` | `S_TimeSegment Segment` | `Public` | Fired when a periodic snapshot is written |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Overridden Events / Custom Events
|
||||||
|
|
||||||
|
### Event: `BeginPlay`
|
||||||
|
- **Description:** Cache owner reference, start session if game has started.
|
||||||
|
- **Flow:**
|
||||||
|
1. Get owning pawn
|
||||||
|
2. Bind to GI_GameFramework.OnGamePhaseChanged
|
||||||
|
3. If game phase is Playing: StartSession("Initial")
|
||||||
|
|
||||||
|
### Custom Event: `OnGamePhaseChangedHandler`
|
||||||
|
- **Description:** Auto-start/stop session based on game phase.
|
||||||
|
- **Flow:**
|
||||||
|
1. If NewPhase == Playing: StartSession("Resume")
|
||||||
|
2. If NewPhase == Menu / Transition / Ended: EndSession()
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blueprint Graph Logic Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[RecordEvent called] --> B{Is session active?}
|
||||||
|
B -->|No| C[Return]
|
||||||
|
B -->|Yes| D[Build S_BehaviourEvent]
|
||||||
|
D --> E{Has history space?}
|
||||||
|
E -->|Yes| F[Append to CachedEventHistory]
|
||||||
|
E -->|No| G[Skip history store]
|
||||||
|
G --> H[Update CurrentSnapshot counters]
|
||||||
|
F --> H
|
||||||
|
H --> I{Threshold crossed?}
|
||||||
|
I -->|Yes| J[Fire OnMetricsThresholdReached]
|
||||||
|
I -->|No| K[Fire OnEventRecorded]
|
||||||
|
J --> K
|
||||||
|
|
||||||
|
L[Tick: RecordDistanceTraveled] --> M[Get current location]
|
||||||
|
M --> N[Distance from LastPosition]
|
||||||
|
N --> O{Delta > 10 cm?}
|
||||||
|
O -->|No| P[Skip]
|
||||||
|
O -->|Yes| Q[Update snapshot distance]
|
||||||
|
Q --> R[Set LastPosition]
|
||||||
|
|
||||||
|
S[SnapshotTimer fires] --> T[Copy CurrentSnapshot]
|
||||||
|
T --> U[Write segment to history]
|
||||||
|
U --> V[Fire OnSnapshotCreated]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Communication Matrix
|
||||||
|
|
||||||
|
| Who Talks | How | What Is Sent |
|
||||||
|
|-----------|-----|-------------|
|
||||||
|
| `BPC_PlayerMetricsTracker` | `Direct call (other)` | `GetMetricsSnapshot` -> `BPC_AdaptiveDifficulty` |
|
||||||
|
| `BPC_PlayerMetricsTracker` | `Dispatcher` | `OnEventRecorded` -> `BPC_AdaptiveDifficulty` (real-time feed) |
|
||||||
|
| `BPC_PlayerMetricsTracker` | `Dispatcher` | `OnSessionEnded` -> `WBP_StatsScreen` (end-game display) |
|
||||||
|
| `BPC_PlayerMetricsTracker` | `Dispatcher` | `OnMetricsThresholdReached` -> `GM_CoreGameMode` (achievements) |
|
||||||
|
| `BPC_HealthSystem` | `Direct` | Calls `RecordEvent(Death)` on player death |
|
||||||
|
| `BPC_HidingSystem` | `Direct` | Calls `RecordEvent(HideStarted/HideEnded)` |
|
||||||
|
| `BPC_StressSystem` | `Direct` | Calls `RecordEvent(StressPeakReached)` |
|
||||||
|
| `GI_GameFramework` | `Dispatcher` | `OnGamePhaseChanged` -> `BPC_PlayerMetricsTracker` (start/stop) |
|
||||||
|
| `BPC_NarrativeManager` | `Direct` | Calls `RecordEvent(NarrativeEvent)` on story beat |
|
||||||
|
| `BPC_WeaponSystem` | `Direct` | Calls `RecordEvent(CombatEvent)` on attack |
|
||||||
|
| `BPC_InventoryManager` | `Direct` | Calls `RecordEvent(ItemCollected)` on pickup |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Validation / Testing Checklist
|
||||||
|
|
||||||
|
- [ ] StartSession resets all counters to zero
|
||||||
|
- [ ] Distance tracking correctly accumulates walking vs sprinting
|
||||||
|
- [ ] Events are capped at MaxEventHistory and do not overflow
|
||||||
|
- [ ] Snapshot timer creates periodic snapshots at correct interval
|
||||||
|
- [ ] EndSession returns accurate final snapshot
|
||||||
|
- [ ] Filtered event history returns only matching event types
|
||||||
|
- [ ] Threshold dispatchers fire at correct metric values
|
||||||
|
- [ ] I_Persistable save/load preserves snapshot across sessions
|
||||||
|
- [ ] Edge case: Rapid events within one frame all recorded
|
||||||
|
- [ ] Edge case: Session ends with zero events — snapshot is valid zero-state
|
||||||
|
- [ ] Edge case: Distance tracking ignores teleportation (delta > 5000 cm)
|
||||||
|
- [ ] Formatted play time displays correctly for all durations
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Reuse Notes
|
||||||
|
|
||||||
|
- The metrics snapshot is designed to be consumed by `BPC_AdaptiveDifficulty` for dynamic difficulty adjustment.
|
||||||
|
- Event history can be serialised to disk via `I_Persistable` for cross-session analytics.
|
||||||
|
- The same `BPC_PlayerMetricsTracker` can be attached to any character blueprint — not just the player.
|
||||||
|
- Threshold values are not defined here; they live in `DA_GameDifficulty` or `DA_AdaptiveSettings`.
|
||||||
|
- For debugging, call `GetEventHistory(false)` to dump all events to log.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Blueprint Spec: Player Metrics Tracker. Conforms to TEMPLATE.md v1.0 — part of the UE5 Modular Game Framework.*
|
||||||
382
docs/blueprints/03-interaction/16_BPC_InteractionDetector.md
Normal file
382
docs/blueprints/03-interaction/16_BPC_InteractionDetector.md
Normal file
@@ -0,0 +1,382 @@
|
|||||||
|
# 16 — Interaction Detector (`BPC_InteractionDetector`)
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
The player's "interaction eye" — performs trace-based detection of interactable objects in the world, sorts them by priority/distance, tracks the current best target, and manages the interaction prompt widget. Every interactive system in the game funnels through this component.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **Requires:** `I_Interactable` (interface on all interactable actors), `PlayerCameraManager` (for trace origin), `BPC_MovementStateSystem` (block interaction during specific states)
|
||||||
|
- **Required By:** `WBP_InteractionPrompt` (HUD element), `BPC_PickupComponent`, `BPC_InteractableDoorComponent`, `BPC_LeverPuzzleComponent`
|
||||||
|
- **Engine/Plugin Requirements:** `LineTraceByChannel` (ECC_GameTraceChannel1 = Interaction), `TimerHandle` for scan interval
|
||||||
|
|
||||||
|
## Class Info
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Parent Class** | `ActorComponent` |
|
||||||
|
| **Class Type** | Blueprint Component |
|
||||||
|
| **Asset Path** | `Content/Framework/Interaction/BPC_InteractionDetector` |
|
||||||
|
| **Implements Interfaces** | None |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Enums
|
||||||
|
|
||||||
|
### `E_InteractionInputMode`
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Press = 0` | Single press to interact |
|
||||||
|
| `Hold = 1` | Hold over duration to confirm |
|
||||||
|
| `DoubleTap = 2` | Two rapid presses |
|
||||||
|
| `Auto = 3` | Automatic on proximity (no input) |
|
||||||
|
|
||||||
|
### `E_InteractionPriority`
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Low = 0` | Ambient / cosmetic items |
|
||||||
|
| `Normal = 1` | Standard pickups, doors |
|
||||||
|
| `High = 2` | Story-critical, puzzles |
|
||||||
|
| `Emergency = 3` | Immediate threat (hide spot, weapon) |
|
||||||
|
|
||||||
|
### `E_DetectionState`
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `NoTarget = 0` | Nothing in range |
|
||||||
|
| `TargetInRange = 1` | Potential target found |
|
||||||
|
| `TargetConfirmed = 2` | Best target selected |
|
||||||
|
| `Interacting = 3` | Currently performing interaction |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Structs
|
||||||
|
|
||||||
|
### `S_InteractableTarget`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `TargetActor` | `AActor` | The interactable actor |
|
||||||
|
| `InterfaceRef` | `I_Interactable` | Cast to interface |
|
||||||
|
| `Priority` | `E_InteractionPriority` | Priority level |
|
||||||
|
| `Distance` | `Float` | Distance from player to target |
|
||||||
|
| `InteractionLabel` | `FText` | Display name (e.g. "Open Door", "Pick Up Key") |
|
||||||
|
| `InputMode` | `E_InteractionInputMode` | How interaction is triggered |
|
||||||
|
| `HoldDuration` | `Float` | Seconds required if InputMode is Hold |
|
||||||
|
| `bIsHighlighted` | `Boolean` | Whether target is visually highlighted |
|
||||||
|
| `TargetLocation` | `Vector` | World location for UI widget |
|
||||||
|
|
||||||
|
### `S_InteractionResult`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `bSuccess` | `Boolean` | Whether interaction was successful |
|
||||||
|
| `FailureReason` | `FText` | Human-readable failure reason |
|
||||||
|
| `TargetActor` | `AActor` | The interacted actor |
|
||||||
|
| `ContextTag` | `GameplayTag` | Tag for event recording |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
|
||||||
|
### Configuration (Instance Editable, Expose On Spawn)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `InteractionRange` | `Float` | `250.0` | `Detection Config` | Max trace distance (cm) |
|
||||||
|
| `DetectionAngle` | `Float` | `45.0` | `Detection Config` | Cone half-angle for detection (degrees) |
|
||||||
|
| `TraceRadius` | `Float` | `10.0` | `Detection Config` | Sphere trace radius |
|
||||||
|
| `ScanInterval` | `Float` | `0.1` | `Detection Config` | Seconds between detection scans |
|
||||||
|
| `MaxTargetsInRange` | `Integer` | `16` | `Detection Config` | Max detected targets per scan |
|
||||||
|
| `bShowDebugTrace` | `Boolean` | `false` | `Detection Config` | Visualise trace in editor |
|
||||||
|
| `TraceChannel` | `TEnumAsByte<ECollisionChannel>` | `ECC_GameTraceChannel1` | `Detection Config` | Collision channel for interaction |
|
||||||
|
| `bBlockDuringDeath` | `Boolean` | `true` | `Detection Config` | Block interaction while dead |
|
||||||
|
| `bBlockDuringHiding` | `Boolean` | `true` | `Detection Config` | Block interaction while hidden |
|
||||||
|
| `bBlockDuringCombat` | `Boolean` | `false` | `Detection Config` | Block interaction during combat |
|
||||||
|
|
||||||
|
### Internal (Private / Protected, No Expose)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `BestTargetIndex` | `Integer` | `-1` | `Detection State` | Index of currently selected target |
|
||||||
|
| `DetectedTargets` | `Array<S_InteractableTarget>` | `[]` | `Detection State` | All detected targets this scan |
|
||||||
|
| `CurrentTarget` | `S_InteractableTarget` | `-` | `Detection State` | Currently selected best target |
|
||||||
|
| `DetectionState` | `E_DetectionState` | `NoTarget` | `Detection State` | Current state of the detector |
|
||||||
|
| `bIsPerformingInteraction` | `Boolean` | `false` | `Detection State` | Whether interaction is in progress |
|
||||||
|
| `HoldInteractionProgress` | `Float` | `0.0` | `Detection State` | Progress for hold-type interactions |
|
||||||
|
| `OwnerPlayerController` | `APlayerController` | `None` | `Cache` | Cached player controller |
|
||||||
|
| `OwnerCameraManager` | `APlayerCameraManager` | `None` | `Cache` | Cached camera manager |
|
||||||
|
| `ScanTimerHandle` | `FTimerHandle` | `-` | `Timing` | Timer for scan interval |
|
||||||
|
| `DetectedActors_LastFrame` | `Set<AActor>` | `{}` | `Tracking` | Previous frame targets for enter/exit detection |
|
||||||
|
|
||||||
|
### Replicated (if multiplayer)
|
||||||
|
|
||||||
|
| Variable | Type | Condition | Description |
|
||||||
|
|----------|------|-----------|-------------|
|
||||||
|
| `CurrentTarget` | `S_InteractableTarget` | `Replicated` | Synced selected target |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
### Public Functions
|
||||||
|
|
||||||
|
#### `StartDetection` → `void`
|
||||||
|
- **Description:** Begins the scan loop for interactable targets.
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Flow:**
|
||||||
|
1. Get owner Player Controller
|
||||||
|
2. Get Player Camera Manager
|
||||||
|
3. Start scan timer (ScanInterval)
|
||||||
|
4. If bShowDebugTrace: enable debug line visualisation
|
||||||
|
|
||||||
|
#### `StopDetection` → `void`
|
||||||
|
- **Description:** Stops the scan loop and clears detected targets.
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Flow:**
|
||||||
|
1. Clear scan timer
|
||||||
|
2. Clear DetectedTargets array
|
||||||
|
3. Set DetectionState = NoTarget
|
||||||
|
4. Fire OnTargetLost
|
||||||
|
5. Clear any highlight effects
|
||||||
|
|
||||||
|
#### `PerformInteraction` → `S_InteractionResult`
|
||||||
|
- **Description:** Executes interaction on the current best target.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `InputMode` | `E_InteractionInputMode` | How the input was triggered |
|
||||||
|
- **Flow:**
|
||||||
|
1. If not CurrentTarget valid or CurrentTarget actor is None: return failure
|
||||||
|
2. If bIsPerformingInteraction: return failure (already interacting)
|
||||||
|
3. If InputMode != CurrentTarget.InputMode: return failure (input mismatch)
|
||||||
|
4. Set bIsPerformingInteraction = true
|
||||||
|
5. Set DetectionState = Interacting
|
||||||
|
6. Fire OnInteractionStarted
|
||||||
|
7. Call CurrentTarget.InterfaceRef.ExecuteInteraction (owner)
|
||||||
|
8. Wait for I_Interactable.OnInteractionCompleted or timeout
|
||||||
|
9. On completion: set bIsPerformingInteraction = false
|
||||||
|
10. Resume scanning
|
||||||
|
11. Return S_InteractionResult with success/failure
|
||||||
|
|
||||||
|
#### `CancelInteraction` → `void`
|
||||||
|
- **Description:** Aborts the current interaction if possible.
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Flow:**
|
||||||
|
1. If not bIsPerformingInteraction: return
|
||||||
|
2. Call CurrentTarget.InterfaceRef.CancelInteraction
|
||||||
|
3. Set bIsPerformingInteraction = false
|
||||||
|
4. HoldInteractionProgress = 0.0
|
||||||
|
5. Set DetectionState = TargetConfirmed
|
||||||
|
6. Fire OnInteractionCancelled
|
||||||
|
|
||||||
|
#### `GetBestTarget` → `S_InteractableTarget`
|
||||||
|
- **Description:** Returns the current best target.
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Flow:** Return CurrentTarget
|
||||||
|
|
||||||
|
#### `HasTarget` → `Boolean`
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Flow:** Return DetectionState >= TargetInRange
|
||||||
|
|
||||||
|
#### `ForceSetTarget` → `void`
|
||||||
|
- **Description:** Forcefully sets a specific target (for scripted interactions).
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `TargetActor` | `AActor` | The specific actor to target |
|
||||||
|
- **Flow:**
|
||||||
|
1. If TargetActor implements I_Interactable:
|
||||||
|
- Build S_InteractableTarget
|
||||||
|
- Set CurrentTarget
|
||||||
|
- Set DetectionState = TargetConfirmed
|
||||||
|
- Fire OnTargetFound
|
||||||
|
2. Else: fire OnInteractionError(not interactable)
|
||||||
|
|
||||||
|
### Private Functions
|
||||||
|
|
||||||
|
#### `ScanForInteractables (Timer)` → `void`
|
||||||
|
- **Description:** Timer callback — performs trace and sorts targets.
|
||||||
|
- **Flow:**
|
||||||
|
1. If DetectionState == Interacting: return (skip scan while interacting)
|
||||||
|
2. Get camera forward vector and world location
|
||||||
|
3. Sphere trace forward (TraceRadius, InteractionRange, TraceChannel)
|
||||||
|
4. For each hit actor:
|
||||||
|
- If implements I_Interactable: add to DetectedTargets
|
||||||
|
- If not: skip
|
||||||
|
5. Remove duplicates by actor reference
|
||||||
|
6. Sort by Priority (descending), then Distance (ascending)
|
||||||
|
7. Clamp to MaxTargetsInRange
|
||||||
|
8. Track enter/exit for each actor — fire OnTargetEntered / OnTargetExited
|
||||||
|
9. Select BestTarget from sorted list
|
||||||
|
10. Update DetectionState accordingly
|
||||||
|
11. Fire OnTargetFound or OnTargetLost
|
||||||
|
|
||||||
|
#### `CalculateInteractionScore` → `Float`
|
||||||
|
- **Description:** Computes a score for a target based on priority, distance, and facing angle.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Target` | `S_InteractableTarget` | The target to score |
|
||||||
|
- **Flow:**
|
||||||
|
1. Score = Priority * 100 - Distance * 0.5
|
||||||
|
2. Facing bonus: if angle < 15 deg, add 50
|
||||||
|
3. Return Max(0, Score)
|
||||||
|
|
||||||
|
#### `HighlightTarget` → `void`
|
||||||
|
- **Description:** Applies highlight effect to target actor.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Target` | `S_InteractableTarget` | Target to highlight |
|
||||||
|
| `bHighlighted` | `Boolean` | Whether to enable or disable |
|
||||||
|
- **Flow:**
|
||||||
|
1. Call Target.InterfaceRef.SetHighlighted(bHighlighted)
|
||||||
|
|
||||||
|
#### `GetViewTraceOrigin` → `Vector, Vector`
|
||||||
|
- **Description:** Returns camera location and forward direction for trace.
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Flow:**
|
||||||
|
1. If OwnerCameraManager valid:
|
||||||
|
- Return cam location and forward vector
|
||||||
|
2. Else: fall back to owner actor location and forward
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Parameters | Bind Access | Description |
|
||||||
|
|------------|-----------|-------------|-------------|
|
||||||
|
| `OnTargetFound` | `S_InteractableTarget NewTarget` | `Public` | New best target selected |
|
||||||
|
| `OnTargetLost` | `S_InteractableTarget LostTarget` | `Public` | Current target lost from view / range |
|
||||||
|
| `OnTargetEntered` | `AActor TargetActor` | `Public` | Actor entered detectable range |
|
||||||
|
| `OnTargetExited` | `AActor TargetActor` | `Public` | Actor left detectable range |
|
||||||
|
| `OnInteractionStarted` | `S_InteractableTarget Target` | `Public` | Interaction began |
|
||||||
|
| `OnInteractionCompleted` | `S_InteractionResult Result` | `Public` | Interaction finished |
|
||||||
|
| `OnInteractionCancelled` | `S_InteractableTarget Target` | `Public` | Interaction was cancelled |
|
||||||
|
| `OnInteractionError` | `FText ErrorMessage` | `Public` | Interaction blocked or failed |
|
||||||
|
| `OnHoldProgressUpdated` | `Float Progress`, `S_InteractableTarget Target` | `Public` | Hold interaction progress [0..1] |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Overridden Events / Custom Events
|
||||||
|
|
||||||
|
### Event: `BeginPlay`
|
||||||
|
- **Description:** Cache references, bind to relevant systems, start detection.
|
||||||
|
- **Flow:**
|
||||||
|
1. Get owning actor → ensure it is a Pawn
|
||||||
|
2. Get Player Controller from owner
|
||||||
|
3. Get Player Camera Manager from controller
|
||||||
|
4. Bind to BPC_MovementStateSystem.OnPostureChanged — block when hidden
|
||||||
|
5. Bind to BPC_HealthSystem.OnDeathStateChanged — block when dead
|
||||||
|
6. StartDetection()
|
||||||
|
|
||||||
|
### Custom Event: `OnInputInteractPressed`
|
||||||
|
- **Description:** Handles press input for interaction.
|
||||||
|
- **Flow:**
|
||||||
|
1. If DetectionState < TargetConfirmed: return
|
||||||
|
2. If CurrentTarget.InputMode == Press:
|
||||||
|
- PerformInteraction(Press)
|
||||||
|
3. If CurrentTarget.InputMode == Hold:
|
||||||
|
- Start hold timer
|
||||||
|
4. If CurrentTarget.InputMode == DoubleTap:
|
||||||
|
- Wait for second press within threshold
|
||||||
|
|
||||||
|
### Custom Event: `OnHoldProgressTick (Timer)` → `void`
|
||||||
|
- **Description:** Ticks hold interaction progress and fires progress dispatcher.
|
||||||
|
- **Flow:**
|
||||||
|
1. HoldInteractionProgress += DeltaTime / CurrentTarget.HoldDuration
|
||||||
|
2. Clamp to 1.0
|
||||||
|
3. Fire OnHoldProgressUpdated
|
||||||
|
4. If HoldInteractionProgress >= 1.0: PerformInteraction(Hold)
|
||||||
|
|
||||||
|
### Custom Event: `OnInputInteractReleased`
|
||||||
|
- **Description:** Cancels hold interaction if button released early.
|
||||||
|
- **Flow:**
|
||||||
|
1. If CurrentTarget.InputMode == Hold and not completed:
|
||||||
|
- CancelInteraction()
|
||||||
|
- HoldInteractionProgress = 0.0
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blueprint Graph Logic Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[Scan Timer Fires] --> B[Get Camera Location / Forward]
|
||||||
|
B --> C[SphereTraceForObjects]
|
||||||
|
C --> D[Hit actors found?]
|
||||||
|
D -->|No| E[Clear targets]
|
||||||
|
E --> F[DetectionState = NoTarget]
|
||||||
|
F --> G[Fire OnTargetLost]
|
||||||
|
|
||||||
|
D -->|Yes| H[Filter by I_Interactable]
|
||||||
|
H --> I[Sort by Priority then Distance]
|
||||||
|
I --> J[Select BestTarget]
|
||||||
|
J --> K{BestTarget changed?}
|
||||||
|
K -->|Yes| L[Unhighlight old target]
|
||||||
|
L --> M[Highlight new target]
|
||||||
|
M --> N[Fire OnTargetFound]
|
||||||
|
K -->|No| O[Keep current target]
|
||||||
|
|
||||||
|
P[OnInputInteractPressed] --> Q{DetectionState >= Confirmed?}
|
||||||
|
Q -->|No| R[Return]
|
||||||
|
Q -->|Yes| S{InputMode match?}
|
||||||
|
S -->|Press| T[PerformInteraction]
|
||||||
|
S -->|Hold| U[Start hold timer]
|
||||||
|
S -->|DoubleTap| V[Start double-tap timer]
|
||||||
|
T --> W[Call ExecuteInteraction on target]
|
||||||
|
W --> X[Fire OnInteractionStarted]
|
||||||
|
X --> Y{Interaction succeeded?}
|
||||||
|
Y -->|Yes| Z[Fire OnInteractionCompleted]
|
||||||
|
Y -->|No| AA[Fire OnInteractionError]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Communication Matrix
|
||||||
|
|
||||||
|
| Who Talks | How | What Is Sent |
|
||||||
|
|-----------|-----|-------------|
|
||||||
|
| `BPC_InteractionDetector` | `Dispatcher` | `OnTargetFound` -> `WBP_InteractionPrompt` (show prompt) |
|
||||||
|
| `BPC_InteractionDetector` | `Dispatcher` | `OnTargetLost` -> `WBP_InteractionPrompt` (hide prompt) |
|
||||||
|
| `BPC_InteractionDetector` | `Dispatcher` | `OnInteractionStarted` -> `BPC_PlayerMetricsTracker` (log event) |
|
||||||
|
| `BPC_InteractionDetector` | `Dispatcher` | `OnInteractionCompleted` -> `BPC_PlayerMetricsTracker` (log result) |
|
||||||
|
| `BPC_InteractionDetector` | `Dispatcher` | `OnHoldProgressUpdated` -> `WBP_InteractionPrompt` (progress bar) |
|
||||||
|
| `BPC_InteractionDetector` | `Dispatcher` | `OnInteractionCancelled` -> `WBP_InteractionPrompt` (hide) |
|
||||||
|
| `BPC_InteractionDetector` | `Listener` | Binds to `BPC_MovementStateSystem.OnPostureChanged` |
|
||||||
|
| `BPC_InteractionDetector` | `Listener` | Binds to `BPC_HealthSystem.OnDeathStateChanged` |
|
||||||
|
| `BPC_InteractionDetector` | `Direct` | Calls `I_Interactable.ExecuteInteraction` on target |
|
||||||
|
| `BPC_InteractionDetector` | `Direct` | Calls `I_Interactable.CancelInteraction` on target |
|
||||||
|
| `BPC_InteractionDetector` | `Direct` | Calls `I_Interactable.SetHighlighted` on target |
|
||||||
|
| `BPC_InteractionDetector` | `Direct call (other)` | `GetBestTarget` called by `WBP_InteractionPrompt` for name |
|
||||||
|
| `PC_PlayerController` | `Input Event` | Binds press / release / hold to `OnInputInteractPressed` etc. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Validation / Testing Checklist
|
||||||
|
|
||||||
|
- [ ] Trace detects actors implementing I_Interactable at correct range
|
||||||
|
- [ ] Priority sorting: Emergency targets always selected over Low
|
||||||
|
- [ ] Same priority: closest target selected
|
||||||
|
- [ ] Detection respects blocking states (dead, hiding)
|
||||||
|
- [ ] Hold interaction progress updates and fires progress dispatcher
|
||||||
|
- [ ] Double-tap interaction waits for second press within window
|
||||||
|
- [ ] Cancelling hold interaction resets progress and fires cancelled
|
||||||
|
- [ ] Interaction blocked during combat if bBlockDuringCombat is true
|
||||||
|
- [ ] Targeting auto-switches if closer target enters range
|
||||||
|
- [ ] Target exit detection fires when actor moves beyond max range
|
||||||
|
- [ ] Edge case: No targets in range — DetectionState stays NoTarget
|
||||||
|
- [ ] Edge case: Target destroyed mid-interaction — cancels gracefully
|
||||||
|
- [ ] Edge case: Multiple interactables in trace — selects correctly
|
||||||
|
- [ ] Debug trace visualisation draws correct sphere/line
|
||||||
|
- [ ] Scan timer pauses while interacting, resumes after completion
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Reuse Notes
|
||||||
|
|
||||||
|
- TraceChannel should be set to a custom object channel (ECC_GameTraceChannel1) with only interactable actors responding.
|
||||||
|
- Priority values can be overridden per actor via the `I_Interactable` interface's `GetInteractionPriority` function.
|
||||||
|
- The hold interaction progress is designed to drive a radial progress widget in the HUD.
|
||||||
|
- For controller/console: `E_InteractionInputMode.Hold` is preferred to prevent accidental interactions.
|
||||||
|
- The same component can be attached to NPCs if they need interaction detection.
|
||||||
|
- To support networked games: replicate CurrentTarget and have each client run their own trace locally.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Blueprint Spec: Interaction Detector. Conforms to TEMPLATE.md v1.0 — part of the UE5 Modular Game Framework.*
|
||||||
286
docs/blueprints/03-interaction/17_I_HidingSpot.md
Normal file
286
docs/blueprints/03-interaction/17_I_HidingSpot.md
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
# 18 — Hiding Spot Interface & Actor (`I_HidingSpot`, `BP_HidingSpotBase`)
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
Defines the contract for any world actor that can serve as a hiding spot. `I_HidingSpot` is the interface; `BP_HidingSpotBase` is the concrete base actor that designers place in levels. Together they provide the entry/exit points, peek positions, and detection rating that `BPC_HidingSystem` queries during gameplay.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **Requires:** `I_Interactable` (so hiding spots appear in interaction detection), `BPC_HidingSystem` (the consumer), `BPC_InteractionDetector` (triggers interaction)
|
||||||
|
- **Required By:** All custom hiding spot blueprints (e.g., `BP_LockerHideSpot`, `BP_DeskHideSpot`, `BP_CabinetHideSpot`)
|
||||||
|
- **Engine/Plugin Requirements:** `AI Perception System` (enemy detection vs hide spot quality), `UNavLinkComponent` for AI pathfinding around hides
|
||||||
|
|
||||||
|
## Class Info
|
||||||
|
|
||||||
|
### Interface
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Parent Class** | `Interface` |
|
||||||
|
| **Asset Path** | `Content/Framework/Interaction/I_HidingSpot` |
|
||||||
|
|
||||||
|
### Base Actor
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Parent Class** | `Actor` |
|
||||||
|
| **Class Type** | Blueprint Actor |
|
||||||
|
| **Asset Path** | `Content/Framework/Interaction/BP_HidingSpotBase` |
|
||||||
|
| **Implements Interfaces** | `I_HidingSpot`, `I_Interactable`, `I_Persistable` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Enums
|
||||||
|
|
||||||
|
### `E_HideSpotQuality`
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Poor = 0` | Exposed, easily spotted (e.g. behind a thin curtain) |
|
||||||
|
| `Average = 1` | Standard cover (e.g. behind a desk) |
|
||||||
|
| `Good = 2` | Well-concealed (e.g. inside a locker) |
|
||||||
|
| `Excellent = 3` | Nearly invisible (e.g. secret compartment, vent) |
|
||||||
|
|
||||||
|
### `E_HideSpotType`
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Locker = 0` | Enclosed, can be locked from inside |
|
||||||
|
| `UnderObject = 1` | Crawl space (bed, desk) |
|
||||||
|
| `BehindObject = 2` | Lean against wall/object |
|
||||||
|
| `Closet = 3` | Walk-in, can close door |
|
||||||
|
| `Vent = 4` | Crawl space with movement to another location |
|
||||||
|
| `Shadow = 5` | Stand still in darkness; no physical cover |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Structs
|
||||||
|
|
||||||
|
### `S_HideSpotData`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `SpotType` | `E_HideSpotType` | Type of hiding spot |
|
||||||
|
| `Quality` | `E_HideSpotQuality` | Concealment quality |
|
||||||
|
| `bHasDoor` | `Boolean` | Whether the spot has an openable/closable door |
|
||||||
|
| `bCanBeLocked` | `Boolean` | Whether the door can be locked from inside |
|
||||||
|
| `bAllowPeeking` | `Boolean` | Whether player can peek out |
|
||||||
|
| `MaxPeekAngle` | `Float` | Maximum peek angle left/right (degrees) |
|
||||||
|
| `bAllowBreathing` | `Boolean` | Whether player can hold breath here |
|
||||||
|
| `bAllowMovement` | `Boolean` | Whether player can move while inside |
|
||||||
|
| `DetectionRangeModifier` | `Float` | Multiplier on enemy detection range [0..1] |
|
||||||
|
| `DetectionSpeedModifier` | `Float` | Multiplier on detection speed [0..1] |
|
||||||
|
| `bIsOneTimeUse` | `Boolean` | Spot breaks after first use |
|
||||||
|
| `bIsSavePoint` | `Boolean` | Trigger auto-save on enter |
|
||||||
|
|
||||||
|
### `S_HideSpotTransform`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `PlayerEnterLocation` | `Transform` | Where player stands to enter |
|
||||||
|
| `PlayerExitLocation` | `Transform` | Where player appears on exit |
|
||||||
|
| `PlayerHidingLocation` | `Transform` | Player's transform while hiding |
|
||||||
|
| `PeekLeftLocation` | `Transform` | Camera location when peeking left |
|
||||||
|
| `PeekRightLocation` | `Transform` | Camera location when peeking right |
|
||||||
|
| `EnemyDetectionOrigin` | `Vector` | World location used for line-of-sight checks |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables (BP_HidingSpotBase)
|
||||||
|
|
||||||
|
### Configuration (Instance Editable, Expose On Spawn)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `HideSpotData` | `S_HideSpotData` | `-` | `Hide Spot Config` | Core hiding spot properties |
|
||||||
|
| `HideSpotTransforms` | `S_HideSpotTransform` | `-` | `Hide Spot Config` | All transform locations |
|
||||||
|
| `bOccupied` | `Boolean` | `false` | `Hide Spot State` | Whether currently occupied |
|
||||||
|
| `LockedByPlayer` | `Boolean` | `false` | `Hide Spot State` | Whether player locked door from inside |
|
||||||
|
| `OccupantPawn` | `APawn` | `None` | `Hide Spot State` | Reference to the occupying pawn |
|
||||||
|
| `CooldownDuration` | `Float` | `2.0` | `Hide Spot Config` | Seconds before re-usable |
|
||||||
|
| `DetectionRadiusOverride` | `Float` | `0.0` | `Hide Spot Config` | If > 0, overrides default detection check radius |
|
||||||
|
| `bRequiresCrouch` | `Boolean` | `false` | `Hide Spot Config` | Player must be crouching to enter |
|
||||||
|
| `bOnlyAllowSneakEnter` | `Boolean` | `true` | `Hide Spot Config` | Must be walking (not sprinting) to enter |
|
||||||
|
| `EnterMontage` | `UAnimMontage` | `None` | `Animations` | Player animation for entering |
|
||||||
|
| `ExitMontage` | `UAnimMontage` | `None` | `Animations` | Player animation for exiting |
|
||||||
|
| `IdleMontage` | `UAnimMontage` | `None` | `Animations` | Looping idle animation while hiding |
|
||||||
|
| `bIsPersistent` | `Boolean` | `false` | `Save System` | Whether to save state (door open/closed, locked) |
|
||||||
|
| `SaveID` | `FName` | `None` | `Save System` | Unique ID for save system |
|
||||||
|
|
||||||
|
### Internal (Private / Protected, No Expose)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `SceneComponents` | `Array<USceneComponent>` | `[]` | `Components` | Array of scene comps for editor visualization |
|
||||||
|
| `bCanUseTimerHandle` | `FTimerHandle` | `-` | `Timing` | Cooldown timer handle |
|
||||||
|
| `bIsAvailable` | `Boolean` | `true` | `State` | Whether the spot can be entered |
|
||||||
|
|
||||||
|
### Replicated (if multiplayer)
|
||||||
|
|
||||||
|
| Variable | Type | Condition | Description |
|
||||||
|
|----------|------|-----------|-------------|
|
||||||
|
| `bOccupied` | `Boolean` | `Replicated` | Synced occupation state |
|
||||||
|
| `LockedByPlayer` | `Boolean` | `Replicated` | Synced lock state |
|
||||||
|
| `OccupantPawn` | `APawn` | `Replicated` | Who is hiding here |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions (I_HidingSpot Interface)
|
||||||
|
|
||||||
|
#### `CanEnterHideSpot` → `Boolean`
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `RequestingPawn` | `APawn` | The pawn wanting to enter |
|
||||||
|
- **Flow:**
|
||||||
|
1. If bOccupied: return false (someone is already hiding)
|
||||||
|
2. If not bIsAvailable: return false (cooldown or one-time use expired)
|
||||||
|
3. Validate movement state of requesting pawn
|
||||||
|
4. Return true
|
||||||
|
|
||||||
|
#### `OnEnterHideSpot` → `S_HideSpotData`
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `EnteringPawn` | `APawn` | The pawn entering |
|
||||||
|
- **Flow:**
|
||||||
|
1. Set bOccupied = true
|
||||||
|
2. Set OccupantPawn = EnteringPawn
|
||||||
|
3. Play EnterMontage on EnteringPawn mesh
|
||||||
|
4. Move pawn to PlayerHidingLocation
|
||||||
|
5. Set pawn collision to NoCollision
|
||||||
|
6. Set pawn visibility to Hidden
|
||||||
|
7. If bCanBeLocked: enable lock input
|
||||||
|
8. If bIsSavePoint: trigger auto-save via GM_CoreGameMode
|
||||||
|
9. Return HideSpotData to BPC_HidingSystem
|
||||||
|
|
||||||
|
#### `OnExitHideSpot` → `void`
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `ExitingPawn` | `APawn` | The pawn exiting |
|
||||||
|
- **Flow:**
|
||||||
|
1. Move pawn to PlayerExitLocation
|
||||||
|
2. Restore pawn collision and visibility
|
||||||
|
3. Set bOccupied = false
|
||||||
|
4. Set OccupantPawn = None
|
||||||
|
5. Set LockedByPlayer = false
|
||||||
|
6. Play ExitMontage
|
||||||
|
7. If bIsOneTimeUse: set bIsAvailable = false, destroy actor
|
||||||
|
8. Else: start cooldown timer
|
||||||
|
|
||||||
|
#### `GetPeekTransform` → `Transform`
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Direction` | `E_PeekDirection` | Which direction to peek |
|
||||||
|
- **Flow:**
|
||||||
|
1. If Direction == Left: return PeekLeftLocation
|
||||||
|
2. If Direction == Right: return PeekRightLocation
|
||||||
|
|
||||||
|
#### `GetDetectionModifier` → `Float, Float`
|
||||||
|
- **Description:** Returns (range modifier, speed modifier) for enemy AI detection checks.
|
||||||
|
- **Flow:**
|
||||||
|
1. Return (DetectionRangeModifier, DetectionSpeedModifier)
|
||||||
|
|
||||||
|
#### `IsOccupied` → `Boolean`
|
||||||
|
- **Flow:** Return bOccupied
|
||||||
|
|
||||||
|
### BP_HidingSpotBase — I_Interactable Implementation
|
||||||
|
|
||||||
|
#### `ExecuteInteraction` → `void`
|
||||||
|
- **Flow:**
|
||||||
|
1. Get interactor pawn
|
||||||
|
2. Call BPC_HidingSystem.EnterHideSpot on the pawn, passing self reference
|
||||||
|
|
||||||
|
#### `GetInteractionLabel` → `FText`
|
||||||
|
- **Flow:**
|
||||||
|
1. If bOccupied: return "Occupied"
|
||||||
|
2. Return "Hide" or "Enter"
|
||||||
|
|
||||||
|
#### `CancelInteraction` → `void`
|
||||||
|
- **Flow:** (no-op for hiding spots)
|
||||||
|
|
||||||
|
#### `SetHighlighted` → `void`
|
||||||
|
- **Flow:**
|
||||||
|
1. Toggle highlight on door/mesh if bHighlighted
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers (BP_HidingSpotBase)
|
||||||
|
|
||||||
|
| Dispatcher | Parameters | Bind Access | Description |
|
||||||
|
|------------|-----------|-------------|-------------|
|
||||||
|
| `OnHideSpotEntered` | `APawn Occupant` | `Public` | A pawn entered this spot |
|
||||||
|
| `OnHideSpotExited` | `APawn PreviousOccupant` | `Public` | A pawn left this spot |
|
||||||
|
| `OnDoorLocked` | `APawn Occupant` | `Public` | Player locked door from inside |
|
||||||
|
| `OnDoorUnlocked` | `APawn Occupant` | `Public` | Player unlocked door |
|
||||||
|
| `OnSpotDestroyed` | `-` | `Public` | Spot used up (one-time) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Blueprint Graph Logic Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant P as BPC_HidingSystem
|
||||||
|
participant S as BP_HidingSpotBase
|
||||||
|
participant I as Interface I_HidingSpot
|
||||||
|
|
||||||
|
P->>S: CanEnterHideSpot
|
||||||
|
S-->>P: Boolean
|
||||||
|
alt Spot Available
|
||||||
|
P->>P: RequestCameraState(Hiding)
|
||||||
|
P->>S: OnEnterHideSpot
|
||||||
|
S->>S: Set bOccupied = true
|
||||||
|
S->>S: Move pawn to HideSpotTransform
|
||||||
|
S->>S: Set pawn collision/visibility
|
||||||
|
S->>S: Play EnterMontage
|
||||||
|
S-->>P: Return S_HideSpotData
|
||||||
|
P->>P: Start Hiding state machine
|
||||||
|
S->>S: Fire OnHideSpotEntered
|
||||||
|
else Spot Occupied
|
||||||
|
P->>P: Show "Occupied" message
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Communication Matrix
|
||||||
|
|
||||||
|
| Who Talks | How | What Is Sent |
|
||||||
|
|-----------|-----|-------------|
|
||||||
|
| `I_HidingSpot` / `BP_HidingSpotBase` | `Interface` | `CanEnterHideSpot`, `OnEnterHideSpot`, `OnExitHideSpot` called by `BPC_HidingSystem` |
|
||||||
|
| `BP_HidingSpotBase` | `I_Interactable` | Implements interface — detected by `BPC_InteractionDetector` |
|
||||||
|
| `BP_HidingSpotBase` | `Dispatcher` | `OnHideSpotEntered` / `OnHideSpotExited` -> `BPC_PlayerMetricsTracker` |
|
||||||
|
| `BP_HidingSpotBase` | `Dispatcher` | `OnDoorLocked` -> `BP_AudioManager` (play lock sound) |
|
||||||
|
| `BP_HidingSpotBase` | `Direct` | Auto-save via `GM_CoreGameMode.RequestAutoSave` |
|
||||||
|
| `BP_HidingSpotBase` | `I_Persistable` | Save/load `bOccupied`, `LockedByPlayer`, `bIsAvailable` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Validation / Testing Checklist
|
||||||
|
|
||||||
|
- [ ] Player can enter an unoccupied hiding spot
|
||||||
|
- [ ] Player cannot enter an occupied hiding spot
|
||||||
|
- [ ] Entering moves pawn to correct transform location
|
||||||
|
- [ ] Exiting returns pawn to exit transform
|
||||||
|
- [ ] Collision disabled while hidden, restored on exit
|
||||||
|
- [ ] Peek transforms position camera correctly
|
||||||
|
- [ ] Lock/unlock door works and blocks AI detection access
|
||||||
|
- [ ] One-time use spots are destroyed after exit
|
||||||
|
- [ ] Cooldown prevents immediate re-entry
|
||||||
|
- [ ] Auto-save triggers when bIsSavePoint is true
|
||||||
|
- [ ] AI detection modifiers correctly reduce detection range/speed
|
||||||
|
- [ ] Player must be crouching to enter if bRequiresCrouch = true
|
||||||
|
- [ ] Player cannot sprint into hide if bOnlyAllowSneakEnter = true
|
||||||
|
- [ ] Edge case: Spot destroyed while player inside — player forced out safely
|
||||||
|
- [ ] Edge case: Player exit blocked by another actor — exit location adjusted
|
||||||
|
- [ ] Edge case: Re-entering immediately after exit respects cooldown
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Reuse Notes
|
||||||
|
|
||||||
|
- Create child blueprints of `BP_HidingSpotBase` for specific spot types (locker, desk, vent, closet).
|
||||||
|
- Override `OnEnterHideSpot` and `OnExitHideSpot` in child blueprints for custom animations and VFX.
|
||||||
|
- Place `PeekLeftLocation` and `PeekRightLocation` scene components in the child blueprint to visualise peek positions in the editor.
|
||||||
|
- For open-world spots (shadow hiding): set `SpotType = Shadow`, leave `HideSpotTransforms.PlayerHidingLocation` at the standing position, and do not disable collision.
|
||||||
|
- The detection modifier system allows designers to create "risky" hiding spots vs completely safe ones.
|
||||||
|
- Editor visualization: draw debug spheres at each transform location for quick placement validation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Blueprint Spec: Hiding Spot Interface & Actor. Conforms to TEMPLATE.md v1.0 — part of the UE5 Modular Game Framework.*
|
||||||
370
docs/blueprints/03-interaction/18_BPC_DiegeticDisplay.md
Normal file
370
docs/blueprints/03-interaction/18_BPC_DiegeticDisplay.md
Normal file
@@ -0,0 +1,370 @@
|
|||||||
|
# `BPC_DiegeticDisplay` — Diegetic Display System
|
||||||
|
|
||||||
|
**Parent Class:** `ActorComponent`
|
||||||
|
**Category:** Interaction / UI
|
||||||
|
**Target UE Version:** 5.5–5.7
|
||||||
|
**Build Phase:** 2 — Interaction
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Overview
|
||||||
|
|
||||||
|
`BPC_DiegeticDisplay` manages any in-world screen that renders game UI directly inside the 3D scene — watches, PDAs, tablets, computer monitors, cockpit screens. Instead of traditional HUD overlays, the player looks down at their character's wrist (watch) or picks up a tablet to see inventory/map/messages rendered as a world-space widget on a static mesh.
|
||||||
|
|
||||||
|
The component provides a 6-mode state machine (`Off → Splash → Idle → Menu → Messaging → Mapping/Scanning`), handles input routing to the active widget, manages screen transitions (flicker, boot sequence, static interference), and communicates with other systems via Event Dispatchers.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Mermaid — Display Mode State Machine
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[Off] --> B{Power On?}
|
||||||
|
B -->|Yes| C[Splash]
|
||||||
|
C --> D[Idle]
|
||||||
|
D --> E{User Input?}
|
||||||
|
E -->|Open Menu| F[Menu]
|
||||||
|
E -->|Open Map| G[Mapping]
|
||||||
|
E -->|Receive Message| H[Messaging]
|
||||||
|
E -->|Activate Scan| I[Scanning]
|
||||||
|
F --> J{Close Menu?}
|
||||||
|
J -->|Yes| D
|
||||||
|
G --> K{Close Map?}
|
||||||
|
K -->|Yes| D
|
||||||
|
H --> L{Message Dismissed?}
|
||||||
|
L -->|Yes| D
|
||||||
|
I --> M{Scan Complete?}
|
||||||
|
M -->|Yes| D
|
||||||
|
D --> N{Idle Timeout?}
|
||||||
|
N -->|Yes| A
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Enums
|
||||||
|
|
||||||
|
### `E_DiegeticDisplayMode`
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Off` | Screen completely off (black/no widget). |
|
||||||
|
| `Splash` | Boot-up animation playing (logo, startup sequence). |
|
||||||
|
| `Idle` | Default active state. May show time, low-priority info. |
|
||||||
|
| `Menu` | Full menu — inventory, settings, journal. |
|
||||||
|
| `Messaging` | Incoming/outgoing message display. |
|
||||||
|
| `Mapping` | Map or floorplan overlay. |
|
||||||
|
| `Scanning` | Active scanning mode (environment analysis, entity detection). |
|
||||||
|
|
||||||
|
### `E_ScreenEffect`
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `None` | No effect — clean image. |
|
||||||
|
| `Static` | Analog TV static / interference. |
|
||||||
|
| `Flicker` | Rapid on/off flicker (power fluctuation). |
|
||||||
|
| `Glitch` | Digital artifact corruption. |
|
||||||
|
| `ScanLine` | CRT scan lines overlay. |
|
||||||
|
| `BootSequence` | OS-style boot animation (loading bars, text crawl). |
|
||||||
|
| `LowBattery` | Dim + pulsing warning overlay. |
|
||||||
|
|
||||||
|
### `E_DisplayInputMode`
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Locked` | Display ignores player input. |
|
||||||
|
| `Passthrough` | Input forwarded to active widget. |
|
||||||
|
| `Modal` | Input consumed entirely by display (menu open). |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Structs
|
||||||
|
|
||||||
|
### `S_DiegeticDisplayConfig`
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `DisplayName` | `FText` | Human-readable name (e.g., "Wrist Link", "PDA-7"). |
|
||||||
|
| `DefaultMode` | `E_DiegeticDisplayMode` | Mode to start in at BeginPlay. |
|
||||||
|
| `bStartPoweredOn` | `bool` | If false, starts in Off mode. |
|
||||||
|
| `WidgetClass` | `TSubclassOf<UUserWidget>` | World-space widget class to render on the screen. |
|
||||||
|
| `ScreenMeshComponent` | `FName` | Name of the static mesh component the widget renders on. |
|
||||||
|
| `ScreenSize` | `FVector2D` | Widget resolution in pixels (e.g., 256x144). |
|
||||||
|
| `InteractionDistance` | `float` | Max distance to interact with the display (0 = attached to player). |
|
||||||
|
| `bIsPlayerOwned` | `bool` | If true, attached to player (watch) vs. world object (terminal). |
|
||||||
|
| `IdleTimeoutSeconds` | `float` | Seconds of inactivity before powering off. 0 = never timeout. |
|
||||||
|
| `BootDuration` | `float` | Seconds the Splash animation plays. |
|
||||||
|
| `bHasMapMode` | `bool` | If true, Mapping mode is available. |
|
||||||
|
| `bHasScanMode` | `bool` | If true, Scanning mode is available. |
|
||||||
|
| `bReplicates` | `bool` | Whether display state is replicated. |
|
||||||
|
|
||||||
|
### `S_ScreenState`
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `CurrentMode` | `E_DiegeticDisplayMode` | Active display mode. |
|
||||||
|
| `ActiveEffect` | `E_ScreenEffect` | Currently playing visual effect. |
|
||||||
|
| `EffectIntensity` | `float` | 0–1 intensity of the active effect. |
|
||||||
|
| `InputMode` | `E_DisplayInputMode` | Current input routing mode. |
|
||||||
|
| `BatteryLevel` | `float` | 0–1 remaining battery (if applicable). |
|
||||||
|
| `bIsPoweredOn` | `bool` | Quick check for power state. |
|
||||||
|
|
||||||
|
### `S_MessagePayload`
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `SenderName` | `FText` | Name of message sender. |
|
||||||
|
| `MessageBody` | `FText` | Message text content. |
|
||||||
|
| `bIsUrgent` | `bool` | If true, forces display to Messaging mode with priority. |
|
||||||
|
| `AssociatedQuestTag` | `FGameplayTag` | Quest/objective this message relates to. |
|
||||||
|
| `AudioAttachment` | `USoundBase*` | Optional audio clip attached to message. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Variables
|
||||||
|
|
||||||
|
### Configuration (Instance Editable, Expose On Spawn)
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `DisplayConfig` | `S_DiegeticDisplayConfig` | Primary configuration struct. |
|
||||||
|
| `IdleWidget` | `TSubclassOf<UUserWidget>` | Widget to display in Idle mode. |
|
||||||
|
| `MenuWidget` | `TSubclassOf<UUserWidget>` | Widget to display in Menu mode. |
|
||||||
|
| `MessagingWidget` | `TSubclassOf<UUserWidget>` | Widget for incoming/outgoing messages. |
|
||||||
|
| `MapWidget` | `TSubclassOf<UUserWidget>` | Widget for mapping overlay. |
|
||||||
|
| `ScanWidget` | `TSubclassOf<UUserWidget>` | Widget for scanning mode. |
|
||||||
|
|
||||||
|
### State (Blueprint Read Only)
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `CurrentState` | `S_ScreenState` | Struct holding all current screen state. |
|
||||||
|
| `ActiveWidget` | `UUserWidget*` | Reference to the currently displayed widget instance. |
|
||||||
|
| `ScreenMesh` | `UStaticMeshComponent*` | Cached reference to the screen mesh. |
|
||||||
|
| `IdleTimerHandle` | `FTimerHandle` | Handle for idle power-off timer. |
|
||||||
|
| `BootTimerHandle` | `FTimerHandle` | Handle for boot-up duration timer. |
|
||||||
|
| `PendingMessages` | `TArray<S_MessagePayload>` | Queue of messages to display. |
|
||||||
|
| `CurrentMessageIndex` | `int32` | Index into PendingMessages for cycling. |
|
||||||
|
|
||||||
|
### Input Handling (Blueprint Read Only)
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `bInputConsumed` | `bool` | Whether the display is currently consuming input. |
|
||||||
|
| `InputActionBindings` | `TArray<FInputActionBinding>` | Active input bindings for display mode. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Functions & Events
|
||||||
|
|
||||||
|
### Public Functions
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `PowerOn` | Boots the display. Plays Splash sequence, then transitions to Idle. |
|
||||||
|
| `PowerOff` | Immediately turns off display, clears widget, kills idle timer. |
|
||||||
|
| `SetDisplayMode` | Transitions to any `E_DiegeticDisplayMode`. Plays transition effect. |
|
||||||
|
| `ApplyScreenEffect` | Applies a temporary `E_ScreenEffect` for a given duration. |
|
||||||
|
| `ClearScreenEffect` | Immediately clears active effect, returns to clean image. |
|
||||||
|
| `ReceiveMessage` | Adds a `S_MessagePayload` to the queue. If current mode is Idle, auto-switches to Messaging. |
|
||||||
|
| `DismissMessage` | Closes current message. If queue has more, shows next; else returns to Idle. |
|
||||||
|
| `OpenMenu` | Transitions to Menu mode. Locks input to display. |
|
||||||
|
| `CloseMenu` | Returns to Idle mode. Releases input. |
|
||||||
|
| `OpenMap` | Transitions to Mapping mode (if `bHasMapMode`). |
|
||||||
|
| `CloseMap` | Returns to Idle mode. |
|
||||||
|
| `StartScan` | Begins Scanning mode (if `bHasScanMode`). |
|
||||||
|
| `StopScan` | Ends Scanning mode, returns to Idle. |
|
||||||
|
| `ToggleDisplay` | Toggles between current mode and Off/Idle based on power state. |
|
||||||
|
| `IsDisplayActive` | Returns true if powered on and not Off/Splash/Boot. |
|
||||||
|
| `GetScreenState` | Returns the `S_ScreenState` struct. |
|
||||||
|
|
||||||
|
### Protected Functions
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `BeginPlay` | Caches references, creates widget instance if auto-start. |
|
||||||
|
| `Tick` | If `ActiveEffect != None`, applies effect material parameter animation. |
|
||||||
|
| `CreateWidgetInstance` | Creates the world-space widget for the given mode class. Attaches to `ScreenMesh`. |
|
||||||
|
| `DestroyWidgetInstance` | Removes and destroys current widget. |
|
||||||
|
| `PlayBootSequence` | Timelines splash animation. On complete, calls `OnBootComplete`. |
|
||||||
|
| `OnBootComplete` | Transitions to Idle, starts idle timeout timer. |
|
||||||
|
| `StartIdleTimer` | Resets and starts the idle timeout. |
|
||||||
|
| `OnIdleTimeout` | Powers off the display. |
|
||||||
|
| `SetupInputForMode` | Binds/unbinds input actions based on `CurrentState.InputMode`. |
|
||||||
|
| `HandleMenuInput` | Processes navigation/select inputs in Menu mode. |
|
||||||
|
| `HandleMessageInput` | Processes dismiss/respond inputs in Messaging mode. |
|
||||||
|
| `HandleMapInput` | Processes pan/zoom inputs in Mapping mode. |
|
||||||
|
| `HandleScanInput` | Processes scan activation input. |
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Payload | Description |
|
||||||
|
|------------|---------|-------------|
|
||||||
|
| `OnDisplayModeChanged` | `E_DiegeticDisplayMode NewMode`, `E_DiegeticDisplayMode PreviousMode` | Fired on any mode transition. |
|
||||||
|
| `OnDisplayPoweredOn` | — | Fired after boot sequence completes. |
|
||||||
|
| `OnDisplayPoweredOff` | — | Fired when display is turned off. |
|
||||||
|
| `OnMessageReceived` | `S_MessagePayload Message` | Fired when a new message enters the queue. |
|
||||||
|
| `OnMessageDismissed` | `S_MessagePayload Message` | Fired when a message is dismissed. |
|
||||||
|
| `OnScreenEffectStarted` | `E_ScreenEffect Effect`, `float Duration` | Fired when a screen effect begins. |
|
||||||
|
| `OnScreenEffectEnded` | `E_ScreenEffect Effect` | Fired when a screen effect ends. |
|
||||||
|
| `OnDisplayInputLocked` | `bool bIsLocked` | Fired when input mode changes to Locked or unlocked. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blueprint Graph Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
Event BeginPlay
|
||||||
|
→ GetOwner → FindComponentByTag (ScreenMeshComponent name) → Cache ScreenMesh
|
||||||
|
→ If DisplayConfig.bStartPoweredOn → Call PowerOn
|
||||||
|
→ Else → Set CurrentState.CurrentMode = Off
|
||||||
|
|
||||||
|
PowerOn
|
||||||
|
→ Set CurrentState.bIsPoweredOn = true
|
||||||
|
→ Set CurrentState.InputMode = Locked
|
||||||
|
→ Broadcast OnDisplayModeChanged (Off → Splash)
|
||||||
|
→ PlayBootSequence
|
||||||
|
|
||||||
|
PlayBootSequence
|
||||||
|
→ ApplyScreenEffect (BootSequence)
|
||||||
|
→ Set Timer by Event (DisplayConfig.BootDuration, Call OnBootComplete)
|
||||||
|
|
||||||
|
OnBootComplete
|
||||||
|
→ ClearScreenEffect
|
||||||
|
→ Set CurrentState.CurrentMode = Idle
|
||||||
|
→ CreateWidgetInstance (IdleWidget)
|
||||||
|
→ Set CurrentState.InputMode = Passthrough
|
||||||
|
→ Broadcast OnDisplayPoweredOn
|
||||||
|
→ Broadcast OnDisplayModeChanged (Splash → Idle)
|
||||||
|
→ StartIdleTimer
|
||||||
|
|
||||||
|
SetDisplayMode
|
||||||
|
→ Store PreviousMode = CurrentState.CurrentMode
|
||||||
|
→ If PreviousMode == NewMode → return
|
||||||
|
→ DestroyWidgetInstance
|
||||||
|
→ Broadcast OnDisplayModeChanged (PreviousMode → NewMode)
|
||||||
|
→ Set CurrentState.CurrentMode = NewMode
|
||||||
|
→ Branch on NewMode:
|
||||||
|
— Menu → CreateWidgetInstance (MenuWidget), SetupInputForMode (Modal)
|
||||||
|
— Messaging → CreateWidgetInstance (MessagingWidget), SetupInputForMode (Modal)
|
||||||
|
— Mapping → CreateWidgetInstance (MapWidget), SetupInputForMode (Modal)
|
||||||
|
— Scanning → CreateWidgetInstance (ScanWidget), SetupInputForMode (Modal)
|
||||||
|
— Idle → CreateWidgetInstance (IdleWidget), SetupInputForMode (Passthrough), StartIdleTimer
|
||||||
|
→ If NewMode != Idle → Clear idle timer
|
||||||
|
|
||||||
|
ReceiveMessage
|
||||||
|
→ PendingMessages.Add(Message)
|
||||||
|
→ If CurrentState.CurrentMode == Idle:
|
||||||
|
→ SetDisplayMode (Messaging)
|
||||||
|
→ If CurrentState.CurrentMode == Messaging:
|
||||||
|
→ Update widget to show new message count
|
||||||
|
→ Broadcast OnMessageReceived
|
||||||
|
|
||||||
|
DismissMessage
|
||||||
|
→ Remove CurrentMessage from PendingMessages
|
||||||
|
→ If PendingMessages.Num() > 0:
|
||||||
|
→ Show next message
|
||||||
|
→ Else:
|
||||||
|
→ SetDisplayMode (Idle)
|
||||||
|
→ Broadcast OnMessageDismissed
|
||||||
|
|
||||||
|
PowerOff
|
||||||
|
→ ClearScreenEffect
|
||||||
|
→ DestroyWidgetInstance
|
||||||
|
→ Clear Input Bindings
|
||||||
|
→ Set CurrentState.bIsPoweredOn = false
|
||||||
|
→ Set CurrentState.CurrentMode = Off
|
||||||
|
→ Clear IdleTimerHandle
|
||||||
|
→ Broadcast OnDisplayPoweredOff
|
||||||
|
→ Broadcast OnDisplayModeChanged (previous → Off)
|
||||||
|
|
||||||
|
OpenMenu
|
||||||
|
→ SetDisplayMode (Menu)
|
||||||
|
|
||||||
|
CloseMenu
|
||||||
|
→ SetDisplayMode (Idle)
|
||||||
|
|
||||||
|
Event Tick (DeltaTime)
|
||||||
|
→ If ActiveEffect != None and ActiveEffectDuration > 0:
|
||||||
|
→ Update material parameter for effect animation (flicker intensity, static noise)
|
||||||
|
→ Decrement remaining duration
|
||||||
|
→ If duration expired → ClearScreenEffect
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Replication
|
||||||
|
|
||||||
|
| Variable | Replication | Callback |
|
||||||
|
|----------|-------------|----------|
|
||||||
|
| `CurrentState.CurrentMode` | `RepNotify` | `OnRep_DisplayMode` — recreates widget on remote clients |
|
||||||
|
| `CurrentState.ActiveEffect` | `Replicated` | — |
|
||||||
|
| `CurrentState.bIsPoweredOn` | `Replicated` | — |
|
||||||
|
|
||||||
|
**Authority:** Server controls power/mode transitions. Client predicts input for Menu/Message dismissal, but server validates.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Widget Setup (Per-Mode)
|
||||||
|
|
||||||
|
| Mode | Widget Content |
|
||||||
|
|------|----------------|
|
||||||
|
| `Idle` | Time display, low-priority notifications, battery indicator. |
|
||||||
|
| `Menu` | Radial or list menu: Inventory, Journal, Settings, Map, Messages. |
|
||||||
|
| `Messaging` | Scrolling text log with sender avatar, reply options, audio playback. |
|
||||||
|
| `Mapping` | 2D floorplan with player position marker, discovered rooms, objective markers. |
|
||||||
|
| `Scanning` | Overlaid data readouts: entity heat signatures, material composition, structural integrity. |
|
||||||
|
|
||||||
|
All widgets are added as `UUserWidget` instances attached to the screen mesh via `AddToViewport`/`SetWidget` on a `UWidgetComponent`. The component handles creation/destruction; individual widgets handle their own internal state.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Dependencies & Communication
|
||||||
|
|
||||||
|
| System | Relationship |
|
||||||
|
|--------|--------------|
|
||||||
|
| `BPC_InteractionDetector` | Detects world terminals; calls `Interact_Implementation` on the host actor. |
|
||||||
|
| `BPC_InventoryComponent` | Menu mode reads inventory contents to display in widget. |
|
||||||
|
| `BPC_MappingSubsystem` | Supplies floorplan data to Map widget. |
|
||||||
|
| `BPC_ScanningSubsystem` | Supplies scan results to Scan widget. |
|
||||||
|
| `GI_GameFramework` | Provides game time, date, and phase info for idle widget display. |
|
||||||
|
| `BPC_StressSystem` | High stress may trigger screen flicker/glitch effects. |
|
||||||
|
| `BPC_HealthSystem` | Low health may apply low battery / dimming effect. |
|
||||||
|
| `BPC_PlayerMetricsTracker` | Logs display usage events for analytics. |
|
||||||
|
| `BPC_AdaptiveDifficulty` | May adjust idle timeout or boot duration based on player performance. |
|
||||||
|
| `Save/Load System` | Saves `CurrentState` and `PendingMessages` array for restore. |
|
||||||
|
| `Narrative Subsystem` | Pushes messages via `ReceiveMessage` for story-driven communication. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Success Criteria
|
||||||
|
|
||||||
|
1. Display boots with splash sequence and transitions to idle.
|
||||||
|
2. Player can open/close menu from idle.
|
||||||
|
3. Receiving a message from narrative subsystem forces display to Messaging mode.
|
||||||
|
4. Player dismisses message and display returns to idle.
|
||||||
|
5. Display powers off after idle timeout; interact to power back on.
|
||||||
|
6. Screen effects (static, flicker, glitch) play on demand and clear correctly.
|
||||||
|
7. Stress/health systems trigger appropriate visual feedback on the display.
|
||||||
|
8. Multiplayer: display mode syncs across clients.
|
||||||
|
9. Save/load restores display mode, pending messages, and effect state.
|
||||||
|
10. Multiple displays (watch + terminal) operate independently with separate state.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Data Flow Summary
|
||||||
|
|
||||||
|
```
|
||||||
|
External System (e.g. Narrative)
|
||||||
|
→ calls ReceiveMessage (S_MessagePayload)
|
||||||
|
→ BPC_DiegeticDisplay.PendingMessages.Add
|
||||||
|
→ Auto-switches to Messaging mode if currently Idle
|
||||||
|
→ Broadcast OnMessageReceived
|
||||||
|
→ Widget shows message content
|
||||||
|
→ Player dismisses → DismissMessage
|
||||||
|
→ Broadcast OnMessageDismissed
|
||||||
|
→ If queue empty → return to Idle
|
||||||
|
|
||||||
|
Player Input (Menu)
|
||||||
|
→ Input routed via SetupInputForMode (Modal)
|
||||||
|
→ Widget handles navigation
|
||||||
|
→ On close → CloseMenu → SetDisplayMode (Idle)
|
||||||
339
docs/blueprints/03-interaction/19_BP_DoorActor.md
Normal file
339
docs/blueprints/03-interaction/19_BP_DoorActor.md
Normal file
@@ -0,0 +1,339 @@
|
|||||||
|
# `BP_DoorActor` — Door Actor System
|
||||||
|
|
||||||
|
**Parent Class:** `Actor`
|
||||||
|
**Category:** Interaction
|
||||||
|
**Implements:** `I_Interactable`
|
||||||
|
**Target UE Version:** 5.5–5.7
|
||||||
|
**Build Phase:** 2 — Interaction
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Overview
|
||||||
|
|
||||||
|
`BP_DoorActor` powers all door-like actors in the game world — standard hinged doors, sliding doors, double doors, and barricaded passages. It manages a discrete 6-state machine (`Closed → Opening → Open → Closing → Locked → Barricaded`), supports key/lock mechanics, item-required unlocking, one-way passage, partial-open positions, and networked replication.
|
||||||
|
|
||||||
|
The door state is exposed via Event Dispatchers so other systems (AudioManager, CameraStateLayer, AI, Narrative) can react.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Mermaid — Door State Machine & Interaction Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[Idle Closed] --> B{Interact?}
|
||||||
|
B -->|Player presses Interact| C[TryOpen]
|
||||||
|
C --> D{Is Locked?}
|
||||||
|
D -->|Yes: Has Required Item?| E{Remove Item?}
|
||||||
|
D -->|Yes: No Item| F[Play Locked Feedback]
|
||||||
|
E -->|Yes| G[Unlock & Open]
|
||||||
|
E -->|No| F
|
||||||
|
D -->|No| H[Play Open Anim]
|
||||||
|
H --> I[State = Opening]
|
||||||
|
I --> J[State = Open]
|
||||||
|
J --> K{Close Conditions Met?}
|
||||||
|
K -->|Auto-Close Timer| L[Play Close Anim]
|
||||||
|
K -->|Player Interact| L
|
||||||
|
K -->|Manual Call| L
|
||||||
|
L --> M[State = Closing]
|
||||||
|
M --> A
|
||||||
|
|
||||||
|
N[Locked Entry] --> O[State = Locked]
|
||||||
|
P[Barricaded Entry] --> Q[State = Barricaded]
|
||||||
|
Q --> R{Barricade Broken?}
|
||||||
|
R -->|Yes| A
|
||||||
|
R -->|No| Q
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Enums
|
||||||
|
|
||||||
|
### `E_DoorState`
|
||||||
|
Defined in `E_DoorState` enum asset.
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Closed` | Door fully closed, no movement. Can be interacted with. |
|
||||||
|
| `Opening` | Door playing open animation. Input blocked. |
|
||||||
|
| `Open` | Door fully open. Can be interacted with to close, or auto-closes. |
|
||||||
|
| `Closing` | Door playing close animation. Input blocked. |
|
||||||
|
| `Locked` | Door locked. Cannot be opened unless unlocked via key/item or external trigger. |
|
||||||
|
| `Barricaded` | Door physically blocked. Must be destroyed/breached. |
|
||||||
|
|
||||||
|
### `E_DoorType`
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Hinged` | Standard door that swings open/closed. |
|
||||||
|
| `Sliding` | Door slides horizontally into a wall pocket. |
|
||||||
|
| `Double` | Two doors that open outward/inward. |
|
||||||
|
| `RollUp` | Garage-style door that rolls upward. |
|
||||||
|
| `TrapDoor` | Floor/ceiling door that opens vertically. |
|
||||||
|
|
||||||
|
### `E_LockState`
|
||||||
|
Optional sub-state for locked doors.
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Unlocked` | Door can be opened normally. |
|
||||||
|
| `KeyRequired` | Requires a specific key item in inventory. |
|
||||||
|
| `ItemRequired` | Requires a specific non-key item (crowbar, code). |
|
||||||
|
| `PuzzleLinked` | Lock is controlled by an external puzzle actor. |
|
||||||
|
| `Breachable` | Lock can be broken with force (weapon/explosive). |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Structs
|
||||||
|
|
||||||
|
### `S_DoorConfig`
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `DoorType` | `E_DoorType` | Visual/mechanical type of door. |
|
||||||
|
| `DefaultState` | `E_DoorState` | State the door starts in at BeginPlay. |
|
||||||
|
| `LockState` | `E_LockState` | Lock type if DefaultState is Locked. |
|
||||||
|
| `RequiredItemTag` | `FGameplayTag` | Tag of item required to unlock (if KeyRequired/ItemRequired). |
|
||||||
|
| `RemoveItemOnUse` | `bool` | If true, the key/item is consumed on unlock. |
|
||||||
|
| `AutoCloseDelay` | `float` | Seconds before door auto-closes. 0 = never auto-close. |
|
||||||
|
| `OpenAngle` | `float` | Degrees the door opens (default 90). |
|
||||||
|
| `OpenSpeed` | `float` | Animation play rate / interpolation speed. |
|
||||||
|
| `CloseSpeed` | `float` | Animation play rate for closing. |
|
||||||
|
| `IsOneWay` | `bool` | If true, door only opens from one side. |
|
||||||
|
| `CanBeBarricaded` | `bool` | If true, door can transition to Barricaded state. |
|
||||||
|
| `BarricadeHealth` | `float` | Damage required to break barricade. |
|
||||||
|
| `bReplicates` | `bool` | Whether door state is replicated. |
|
||||||
|
|
||||||
|
### `S_DoorAudioConfig`
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `OpenSound` | `USoundBase*` | Sound played when door begins opening. |
|
||||||
|
| `CloseSound` | `USoundBase*` | Sound played when door begins closing. |
|
||||||
|
| `LockedSound` | `USoundBase*` | Sound played when player tries to open a locked door. |
|
||||||
|
| `UnlockSound` | `USoundBase*` | Sound played when door is unlocked. |
|
||||||
|
| `BarricadeBreakSound` | `USoundBase*` | Sound played when barricade is destroyed. |
|
||||||
|
| `SqueakInterval` | `float` | Seconds between squeak sounds during movement (0 = no squeak). |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Variables
|
||||||
|
|
||||||
|
### Configuration (Instance Editable, Expose On Spawn)
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `DoorConfig` | `S_DoorConfig` | Primary configuration struct. |
|
||||||
|
| `AudioConfig` | `S_DoorAudioConfig` | Audio configuration struct. |
|
||||||
|
| `LinkedActors` | `TArray<AActor*>` | Actors to notify on door open/close (lights, alarms, traps). |
|
||||||
|
| `RequiredItemTag` | `FGameplayTag` | Shortcut to DoorConfig.RequiredItemTag (for BP pin). |
|
||||||
|
|
||||||
|
### State (Blueprint Read Only, Replicated)
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `CurrentState` | `E_DoorState` | Current door state. Replicated using `OnRep_DoorState`. |
|
||||||
|
| `CurrentLockState` | `E_LockState` | Current lock state. Replicated. |
|
||||||
|
| `CurrentBarricadeHealth` | `float` | Remaining barricade health. Replicated. |
|
||||||
|
| `bIsOpenFromFront` | `bool` | Tracks which side the door was opened from (for one-way logic). |
|
||||||
|
|
||||||
|
### Internal (Blueprint Read Only, Not Replicated)
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `OwningDoorActor` | `AActor*` | Cached reference to owning actor (BP_DoorBase). |
|
||||||
|
| `DoorMeshComponent` | `UStaticMeshComponent*` | Cached reference to the door's static mesh (if hinged/sliding). |
|
||||||
|
| `DoorAudioComponent` | `UAudioComponent*` | Cached audio component for spatialized sounds. |
|
||||||
|
| `AutoCloseTimerHandle` | `FTimerHandle` | Handle for auto-close timer. |
|
||||||
|
| `AnimationTimeline` | `float` | Internal 0-1 progress of open/close animation. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Functions & Events
|
||||||
|
|
||||||
|
### Public Functions
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `Interact_Implementation` | Implements `I_Interactable.ExecuteInteraction`. Routes to `TryOpen` if Closed/Locked/Barricaded, or `TryClose` if Open. |
|
||||||
|
| `TryOpen` | Checks state: if Locked, attempts unlock (check inventory or puzzle); if Barricaded, plays blocked feedback; if Closed, begins open animation. Returns `E_DoorOperationResult`. |
|
||||||
|
| `TryClose` | If Open, begins close animation. If Opening/Closing, ignores. Returns bool. |
|
||||||
|
| `LockDoor` | Transitions to Locked state. Optionally sets lock type and required item. |
|
||||||
|
| `UnlockDoor` | Removes lock, plays unlock effects. If `bAutoOpenOnUnlock`, begins opening. |
|
||||||
|
| `BarricadeDoor` | Transitions to Barricaded state, sets health. |
|
||||||
|
| `DamageBarricade` | Reduces barricade health by damage amount. If health reaches 0, removes barricade and transitions to Closed. |
|
||||||
|
| `ForceSetState` | Bypasses animation; immediately sets door to any valid state. Used for save/load. |
|
||||||
|
| `IsDoorUsable` | Returns true if door can be interacted with (not Opening/Closing/Barricaded with health). |
|
||||||
|
| `GetDoorState` | Returns current `E_DoorState`. |
|
||||||
|
| `GetDoorType` | Returns `E_DoorType` from config. |
|
||||||
|
|
||||||
|
### Protected Functions
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `BeginPlay` | Caches references, applies default state, initialises audio component. |
|
||||||
|
| `PlayOpenAnimation` | Starts timeline/interp for door rotation/slide. Plays open sound. Calls `OnOpenComplete` when finished. |
|
||||||
|
| `PlayCloseAnimation` | Starts timeline/interp for reverse. Plays close sound. Calls `OnCloseComplete` when finished. |
|
||||||
|
| `OnOpenComplete` | Sets state to Open, clears auto-close timer if `AutoCloseDelay > 0`, notifies `LinkedActors`. |
|
||||||
|
| `OnCloseComplete` | Sets state to Closed. |
|
||||||
|
| `TryUnlockWithItem` | Checks player inventory for `RequiredItemTag`. If found and `RemoveItemOnUse`, consumes the item. Returns bool. |
|
||||||
|
| `CheckPuzzleUnlockCondition` | Returns true if linked puzzle is solved. |
|
||||||
|
| `PlayLockedFeedback` | Plays locked sound + optional UI prompt notifying player of required item. |
|
||||||
|
| `OnRep_DoorState` | RepNotify: when `CurrentState` changes, plays appropriate animation on remote clients. |
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Payload | Description |
|
||||||
|
|------------|---------|-------------|
|
||||||
|
| `OnDoorStateChanged` | `E_DoorState NewState`, `AActor* Instigator` | Fired on any state transition. |
|
||||||
|
| `OnDoorOpened` | `AActor* Instigator`, `bool bFromFront` | Fired when door reaches Open state. |
|
||||||
|
| `OnDoorClosed` | `AActor* Instigator` | Fired when door reaches Closed state. |
|
||||||
|
| `OnDoorLocked` | `AActor* Instigator` | Fired when door transitions to Locked. |
|
||||||
|
| `OnDoorUnlocked` | `AActor* Instigator` | Fired when door is unlocked. |
|
||||||
|
| `OnBarricadeBroken` | `AActor* Instigator` | Fired when barricade health reaches 0. |
|
||||||
|
| `OnInteractionFailed` | `E_DoorState CurrentState`, `FText FailReason` | Fired when player cannot interact. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blueprint Graph Flow
|
||||||
|
|
||||||
|
### Event Graph Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
Event BeginPlay
|
||||||
|
→ GetOwner → Cast to BP_DoorBase → Cache OwningDoorActor
|
||||||
|
→ FindComponentByClass UStaticMeshComponent → Cache DoorMeshComponent
|
||||||
|
→ Create Audio Component → Cache
|
||||||
|
→ Apply Default State from DoorConfig.DefaultState
|
||||||
|
→ If Locked → Set CurrentLockState → LockDoor
|
||||||
|
|
||||||
|
On Interact_Implementation
|
||||||
|
→ Branch: CurrentState
|
||||||
|
— Closed → Call TryOpen
|
||||||
|
— Locked → Call TryUnlockWithItem → Branch Result
|
||||||
|
— Success → Play Unlock Effects → Call TryOpen
|
||||||
|
— Fail → Play LockedFeedback → Broadcast OnInteractionFailed
|
||||||
|
— Barricaded → Play Blocked Feedback → Broadcast OnInteractionFailed
|
||||||
|
— Open → Call TryClose
|
||||||
|
— Opening/Closing → Ignore (return)
|
||||||
|
|
||||||
|
TryOpen
|
||||||
|
→ IsDoorUsable? → If not, return false
|
||||||
|
→ Set CurrentState = Opening
|
||||||
|
→ Broadcast OnDoorStateChanged
|
||||||
|
→ Set bIsOpenFromFront = (player side check)
|
||||||
|
→ PlayOpenAnimation
|
||||||
|
|
||||||
|
TryClose
|
||||||
|
→ IsDoorUsable? → If not, return false
|
||||||
|
→ Set CurrentState = Closing
|
||||||
|
→ Broadcast OnDoorStateChanged
|
||||||
|
→ Clear AutoCloseTimerHandle
|
||||||
|
→ PlayCloseAnimation
|
||||||
|
|
||||||
|
PlayOpenAnimation
|
||||||
|
→ Timeline: 0→1 over 1/OpenSpeed seconds
|
||||||
|
→ Interp: DoorMeshComponent RelativeRotation.Yaw from 0 → OpenAngle (or DoorConfig sign for direction)
|
||||||
|
→ PlaySound: AudioConfig.OpenSound
|
||||||
|
→ On Finished → OnOpenComplete
|
||||||
|
|
||||||
|
PlayCloseAnimation
|
||||||
|
→ Timeline: 1→0 over 1/CloseSpeed seconds
|
||||||
|
→ Interp: DoorMeshComponent RelativeRotation.Yaw from current → 0
|
||||||
|
→ PlaySound: AudioConfig.CloseSound
|
||||||
|
→ On Finished → OnCloseComplete
|
||||||
|
|
||||||
|
OnOpenComplete
|
||||||
|
→ Set CurrentState = Open
|
||||||
|
→ Broadcast OnDoorOpened
|
||||||
|
→ If AutoCloseDelay > 0 → Set Timer by Event (AutoCloseDelay, Call TryClose)
|
||||||
|
→ Notify LinkedActors (Open event)
|
||||||
|
|
||||||
|
OnCloseComplete
|
||||||
|
→ Set CurrentState = Closed
|
||||||
|
→ Broadcast OnDoorClosed
|
||||||
|
→ Notify LinkedActors (Close event)
|
||||||
|
|
||||||
|
LockDoor
|
||||||
|
→ Set CurrentState = Locked
|
||||||
|
→ Set CurrentLockState = Input LockState
|
||||||
|
→ Broadcast OnDoorLocked
|
||||||
|
|
||||||
|
UnlockDoor
|
||||||
|
→ Set CurrentLockState = Unlocked
|
||||||
|
→ If bAutoOpenOnUnlock → Call TryOpen
|
||||||
|
→ Broadcast OnDoorUnlocked
|
||||||
|
|
||||||
|
DamageBarricade
|
||||||
|
→ CurrentBarricadeHealth -= Damage
|
||||||
|
→ If CurrentBarricadeHealth <= 0
|
||||||
|
→ Set CurrentState = Closed
|
||||||
|
→ Broadcast OnBarricadeBroken
|
||||||
|
→ Broadcast OnDoorStateChanged
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Replication
|
||||||
|
|
||||||
|
| Variable | Replication | Callback |
|
||||||
|
|----------|-------------|----------|
|
||||||
|
| `CurrentState` | `RepNotify` | `OnRep_DoorState` — plays matching animation on remote |
|
||||||
|
| `CurrentLockState` | `Replicated` | — |
|
||||||
|
| `CurrentBarricadeHealth` | `Replicated` | — |
|
||||||
|
| `bIsOpenFromFront` | `Replicated` | — |
|
||||||
|
|
||||||
|
**Server authority:** All state transitions are executed on the server. Clients predict input via `Interact_Implementation`, but the server validates and broadcasts the authoritative state.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Dependencies & Communication
|
||||||
|
|
||||||
|
| System | Relationship |
|
||||||
|
|--------|--------------|
|
||||||
|
| `BPC_InteractionDetector` | Calls `Interact_Implementation` when player looks at door and presses Interact. |
|
||||||
|
| `BPC_InventorySystem` | Queried via `TryUnlockWithItem` for item possession and consumption. |
|
||||||
|
| `BPC_HealthSystem` | Listens to `OnBarricadeBroken` for environmental damage credit. |
|
||||||
|
| `BPC_CameraStateLayer` | May request FOV push when door opens into a large space. |
|
||||||
|
| `AudioManager` | Consumes `AudioConfig` and may play ambient/occlusion changes on door state. |
|
||||||
|
| `AI Perception` | AI listens to `OnDoorOpened`/`OnDoorClosed` to update pathfinding and awareness. |
|
||||||
|
| `Save/Load System` | Calls `ForceSetState` and `LockDoor`/`UnlockDoor` during restore. |
|
||||||
|
| `Narrative Subsystem` | May bind to `OnDoorUnlocked` to trigger dialogue when a story-key door is opened. |
|
||||||
|
| `BP_PuzzleDeviceActor` | `PuzzleLinked` lock type — puzzle completion calls `UnlockDoor`. |
|
||||||
|
| `BPC_DifficultyManager` | May adjust `AutoCloseDelay` and `BarricadeHealth` based on player performance metrics. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Success Criteria
|
||||||
|
|
||||||
|
1. Player can open/close a hinged door with standard interaction.
|
||||||
|
2. Locked door plays feedback sound and shows item-required prompt.
|
||||||
|
3. Player with correct key/item unlocks door; item is consumed if configured.
|
||||||
|
4. Door auto-closes after configurable delay; auto-close timer resets on re-open.
|
||||||
|
5. Barricaded door blocks passage until its health reaches 0 via damage.
|
||||||
|
6. `PuzzleLinked` door unlocks when linked `BP_PuzzleDeviceActor` reports completion.
|
||||||
|
7. One-way doors only open from the configured side.
|
||||||
|
8. Double doors open/close in sync as a paired animation.
|
||||||
|
9. Multiplayer: server-authoritative state syncs to all clients via `OnRep_DoorState`.
|
||||||
|
10. Save/load correctly restores door state, lock state, and barricade health.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Data Flow Summary
|
||||||
|
|
||||||
|
```
|
||||||
|
Player Presses Interact
|
||||||
|
→ BPC_InteractionDetector.ScanForInteractables
|
||||||
|
→ Closest valid target = BP_DoorActor
|
||||||
|
→ Call I_Interactable.ExecuteInteraction
|
||||||
|
→ BP_DoorActor.Interact_Implementation
|
||||||
|
→ State check / lock check / inventory check
|
||||||
|
→ Play animation / sound
|
||||||
|
→ Broadcast Event Dispatchers
|
||||||
|
→ Downstream systems react (AI, Audio, Narrative, Save)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 12. Reuse Notes
|
||||||
|
|
||||||
|
- This renamed file (formerly `BPC_InteractableDoorComponent`) is now an Actor-based system (`BP_DoorActor`) as per Master Section 3.5.
|
||||||
|
- The door actor encapsulates all door logic directly rather than as an ActorComponent.
|
||||||
|
- Cross-references updated: `BPC_InventoryComponent` → `BPC_InventorySystem`, `BPC_LeverPuzzleComponent` → `BP_PuzzleDeviceActor`.
|
||||||
97
docs/blueprints/03-interaction/20_BP_PuzzleDeviceActor.md
Normal file
97
docs/blueprints/03-interaction/20_BP_PuzzleDeviceActor.md
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
# BP_PuzzleDeviceActor — Puzzle Device Actor
|
||||||
|
|
||||||
|
**Parent Class:** `Actor`
|
||||||
|
**Category:** Interaction
|
||||||
|
**Target UE Version:** 5.5–5.7
|
||||||
|
**Build Phase:** 2 — Interaction
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Overview
|
||||||
|
|
||||||
|
`BP_PuzzleDeviceActor` is the base actor for all puzzle devices in the world — levers, pressure plates, valve wheels, keypads, combination locks, circuit boards, and custom logic puzzles. Each instance references a [`DA_PuzzleData`](../14-data-assets/DA_PuzzleData.md) that defines the puzzle rules, solution, and completion effects.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Enums
|
||||||
|
|
||||||
|
### `E_PuzzleType`
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Lever` | Single or multi-lever sequence |
|
||||||
|
| `PressurePlate` | Weight-activated plate |
|
||||||
|
| `ValveWheel` | Rotate-to-open valve |
|
||||||
|
| `Keypad` | Numeric code entry |
|
||||||
|
| `CombinationLock` | Multi-dial combination |
|
||||||
|
| `CircuitBoard` | Connect-the-pins puzzle |
|
||||||
|
| `Custom` | Script-driven puzzle |
|
||||||
|
|
||||||
|
### `E_PuzzleState`
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Unsolved` | Puzzle not yet completed |
|
||||||
|
| `InProgress` | Player interacting with puzzle |
|
||||||
|
| `Solved` | Puzzle completed successfully |
|
||||||
|
| `Failed` | Puzzle failed (if retryable) |
|
||||||
|
| `Locked` | Puzzle locked (requires item/flag) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `PuzzleData` | `DA_PuzzleData*` | Data asset defining this puzzle |
|
||||||
|
| `CurrentState` | `E_PuzzleState` | Current puzzle state |
|
||||||
|
| `bIsInteractable` | `bool` | Can player interact with this puzzle |
|
||||||
|
| `InteractionPrompt` | `FText` | Prompt text for interaction |
|
||||||
|
| `LinkedActor` | `AActor*` | Actor to notify on solve (door, gate, etc.) |
|
||||||
|
| `bResetOnFail` | `bool` | Auto-reset after failure |
|
||||||
|
| `MaxAttempts` | `int32` | Max attempts before lockout (-1 = unlimited) |
|
||||||
|
| `CurrentAttempts` | `int32` | Attempts used so far |
|
||||||
|
| `CooldownSeconds` | `float` | Time before re-interaction allowed |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `BeginInteraction` | Player starts interacting with puzzle |
|
||||||
|
| `EndInteraction` | Player exits puzzle UI |
|
||||||
|
| `CheckSolution` | Validates player input against puzzle data |
|
||||||
|
| `OnPuzzleSolved` | Processes completion — notifies LinkedActor |
|
||||||
|
| `OnPuzzleFailed` | Processes failure — resets or locks |
|
||||||
|
| `ResetPuzzle` | Resets puzzle to default state |
|
||||||
|
| `GetPuzzleState` | Returns current `E_PuzzleState` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Payload | Description |
|
||||||
|
|------------|---------|-------------|
|
||||||
|
| `OnPuzzleStateChanged` | `E_PuzzleState NewState` | State transition occurred |
|
||||||
|
| `OnPuzzleSolved` | `AActor* Solver` | Puzzle completed |
|
||||||
|
| `OnPuzzleFailed` | `int32 AttemptNumber` | Attempt failed |
|
||||||
|
| `OnPuzzleLinkedActorTriggered` | `AActor* LinkedActor` | Linked actor notified |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Dependencies & Communication
|
||||||
|
|
||||||
|
| System | Relationship |
|
||||||
|
|--------|--------------|
|
||||||
|
| `DA_PuzzleData` | Defines puzzle rules and solution |
|
||||||
|
| `BPC_InteractionDetector` | Focus and interaction initiation |
|
||||||
|
| `BP_DoorActor` | Common linked actor (unlock on solve) |
|
||||||
|
| `BPC_NarrativeStateSystem` | Set narrative flags on solve |
|
||||||
|
| `SS_SaveManager` | Persist puzzle state |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Reuse Notes
|
||||||
|
- All puzzle logic is data-driven via `DA_PuzzleData`
|
||||||
|
- LinkedActor pattern decouples puzzle from effect
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
# BPC_ContextualTraversalSystem — Contextual Traversal System
|
||||||
|
|
||||||
|
**Parent Class:** `ActorComponent`
|
||||||
|
**Category:** Interaction
|
||||||
|
**Target UE Version:** 5.5–5.7
|
||||||
|
**Build Phase:** 2 — Interaction
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Overview
|
||||||
|
|
||||||
|
`BPC_ContextualTraversalSystem` handles player traversal of environmental obstacles — vaulting, climbing, squeezing through gaps, sliding under obstacles. Uses motion warping and root motion for smooth traversal animations triggered by context-aware detection.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Enums
|
||||||
|
|
||||||
|
### `E_TraversalType`
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Vault` | Vault over low obstacles |
|
||||||
|
| `Mantle` | Climb up onto ledges |
|
||||||
|
| `Slide` | Slide under low barriers |
|
||||||
|
| `Squeeze` | Squeeze through narrow gaps |
|
||||||
|
| `LedgeGrab` | Grab and hang from ledges |
|
||||||
|
|
||||||
|
### `E_TraversalHeight`
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Low` | Below knee height (step up) |
|
||||||
|
| `Medium` | Waist height (vault) |
|
||||||
|
| `High` | Chest height (mantle) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `DetectionRange` | `float` | Max distance to detect traversable surfaces |
|
||||||
|
| `DetectionHalfAngle` | `float` | Cone angle for forward detection |
|
||||||
|
| `VaultHeightThreshold` | `float` | Max height for vault vs mantle decision |
|
||||||
|
| `TraversalSpeed` | `float` | Speed multiplier during traversal anim |
|
||||||
|
| `bIsTraversing` | `bool` | Currently playing traversal animation |
|
||||||
|
| `CurrentTraversalType` | `E_TraversalType` | Active traversal type |
|
||||||
|
| `MotionWarpingTarget` | `FVector` | Target location for motion warping |
|
||||||
|
| `TraversalCooldown` | `float` | Min time between traversals |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `DetectTraversableObstacle` | Performs forward trace to detect vault/mantle points |
|
||||||
|
| `AttemptTraversal` | Initiates traversal to detected point. Returns bool |
|
||||||
|
| `ExecuteTraversal` | Plays traversal montage with motion warping |
|
||||||
|
| `OnTraversalComplete` | Callback when montage finishes |
|
||||||
|
| `CancelTraversal` | Interrupts traversal (damage/player input) |
|
||||||
|
| `IsTraversing` | Returns `bIsTraversing` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Payload | Description |
|
||||||
|
|------------|---------|-------------|
|
||||||
|
| `OnTraversalStarted` | `E_TraversalType Type`, `FVector Target` | Traversal animation begins |
|
||||||
|
| `OnTraversalComplete` | `E_TraversalType Type` | Player reaches target position |
|
||||||
|
| `OnTraversalFailed` | `FText Reason` | Traversal attempt blocked |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Dependencies & Communication
|
||||||
|
|
||||||
|
| System | Relationship |
|
||||||
|
|--------|--------------|
|
||||||
|
| `BPC_MovementStateSystem` | Movement mode override during traversal |
|
||||||
|
| `BPC_CameraStateLayer` | Camera FOV adjustment during traversal |
|
||||||
|
| `BPC_StressSystem` | Stress modifier for close-call traversals |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Reuse Notes
|
||||||
|
- Uses UE5 Motion Warping for accurate target placement
|
||||||
|
- Detection uses capsule trace at player height ranges
|
||||||
104
docs/blueprints/03-interaction/22_BPC_PhysicsDragSystem.md
Normal file
104
docs/blueprints/03-interaction/22_BPC_PhysicsDragSystem.md
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
# BPC_PhysicsDragSystem — Physics Drag System
|
||||||
|
|
||||||
|
**Parent Class:** `ActorComponent`
|
||||||
|
**Category:** Interaction
|
||||||
|
**Target UE Version:** 5.5–5.7
|
||||||
|
**Build Phase:** 2 — Interaction
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Overview
|
||||||
|
|
||||||
|
`BPC_PhysicsDragSystem` allows the player to grab and physically drag rigid-body objects in the world. Supports grab-at-distance, hold-to-grab with physics constraints, object rotation, and throw mechanics. Integrates with the interaction detector for grab targeting.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Enums
|
||||||
|
|
||||||
|
### `E_DragMode`
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `PhysicsConstraint` | Attaches object via physics constraint to player hand |
|
||||||
|
| `Kinematic` | Sets object to kinematic and parents to hand |
|
||||||
|
| `Magnetic` | Objects smoothly lerp toward grab point |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `DragMode` | `E_DragMode` | Current drag physics mode |
|
||||||
|
| `GrabbedObject` | `AActor*` | Currently held actor |
|
||||||
|
| `ConstraintComp` | `UPhysicsConstraintComponent*` | Physics constraint for held object |
|
||||||
|
| `GrabDistance` | `float` | Max distance to grab (cm) |
|
||||||
|
| `HoldDistance` | `float` | Distance object is held from camera |
|
||||||
|
| `ThrowForce` | `float` | Impulse applied on release |
|
||||||
|
| `RotationSpeed` | `float` | Degrees/sec for object rotation input |
|
||||||
|
| `bCanThrow` | `bool` | Object can be thrown on release |
|
||||||
|
| `bIsGrabbing` | `bool` | Currently holding an object |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `GrabObject` | Attempts to grab targeted physics object. Returns bool. |
|
||||||
|
| `ReleaseObject` | Releases held object, applies throw force if bCanThrow. |
|
||||||
|
| `RotateObject` | Rotates held object by input axis values |
|
||||||
|
| `SetHoldDistance` | Adjusts distance of held object from camera |
|
||||||
|
| `GetGrabbedObject` | Returns currently held actor (or null) |
|
||||||
|
| `IsGrabbing` | Returns `bIsGrabbing` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Payload | Description |
|
||||||
|
|------------|---------|-------------|
|
||||||
|
| `OnObjectGrabbed` | `AActor* GrabbedObject` | Object successfully grabbed |
|
||||||
|
| `OnObjectReleased` | `AActor* ReleasedObject`, `bool bWasThrown` | Object released |
|
||||||
|
| `OnGrabFailed` | `FText FailReason` | Grab attempt failed |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
GrabObject:
|
||||||
|
→ Get targeted object from InteractionDetector
|
||||||
|
→ Check if object has physics simulation enabled
|
||||||
|
→ If DragMode == PhysicsConstraint:
|
||||||
|
→ Create physics constraint between hand socket and object
|
||||||
|
→ Set constraint limits (linear + angular)
|
||||||
|
→ If DragMode == Kinematic:
|
||||||
|
→ Set object simulated physics to false
|
||||||
|
→ Attach to hand socket
|
||||||
|
→ Set bIsGrabbing = true
|
||||||
|
→ Broadcast OnObjectGrabbed
|
||||||
|
|
||||||
|
ReleaseObject:
|
||||||
|
→ If bCanThrow → Apply forward impulse * ThrowForce
|
||||||
|
→ Destroy constraint / detach
|
||||||
|
→ Re-enable physics on object
|
||||||
|
→ Set bIsGrabbing = false
|
||||||
|
→ Broadcast OnObjectReleased
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Dependencies & Communication
|
||||||
|
|
||||||
|
| System | Relationship |
|
||||||
|
|--------|--------------|
|
||||||
|
| `BPC_InteractionDetector` | Provides grab target via focus |
|
||||||
|
| `BPC_CameraStateLayer` | Camera direction for throw vector |
|
||||||
|
| `BPC_MovementStateSystem` | Speed modifier while carrying objects |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Reuse Notes
|
||||||
|
- Physics drag uses UE5 physics constraint system for realistic object handling
|
||||||
|
- Objects must have `Simulate Physics = true` and `Mass` configured
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# BPC_UsableWorldObjectSystem — Usable World Object System
|
||||||
|
|
||||||
|
**Parent Class:** `ActorComponent`
|
||||||
|
**Category:** Interaction
|
||||||
|
**Target UE Version:** 5.5–5.7
|
||||||
|
**Build Phase:** 2 — Interaction
|
||||||
|
|
||||||
|
## 1. Overview
|
||||||
|
`BPC_UsableWorldObjectSystem` powers generic usable objects in the world — switches, buttons, valves, terminals, readable notes, audio logs. Provides a unified interaction interface for any object the player can "use" via the InteractionDetector.
|
||||||
|
|
||||||
|
## 2. Enums
|
||||||
|
### `E_UsableObjectType`
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Switch` | Toggleable switch |
|
||||||
|
| `Button` | Pressable button |
|
||||||
|
| `Valve` | Rotatable valve |
|
||||||
|
| `Terminal` | Interactive screen |
|
||||||
|
| `Readable` | Note/book |
|
||||||
|
| `AudioLog` | Audio playback device |
|
||||||
|
| `Generic` | Custom interaction |
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `ObjectType` | `E_UsableObjectType` | Type of usable object |
|
||||||
|
| `InteractionData` | `DA_InteractionData*` | Interaction definition |
|
||||||
|
| `bIsActive` | `bool` | Object enabled |
|
||||||
|
| `bSingleUse` | `bool` | Can only be used once |
|
||||||
|
| `bHasBeenUsed` | `bool` | Tracked if single-use |
|
||||||
|
| `CooldownSeconds` | `float` | Reuse cooldown |
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `OnUse` | Executes interaction logic |
|
||||||
|
| `SetEnabled` | Enable/disable object |
|
||||||
|
| `ResetObject` | Return to initial state |
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
| Dispatcher | Payload | Description |
|
||||||
|
|------------|---------|-------------|
|
||||||
|
| `OnObjectUsed` | `AActor* User` | Object successfully used |
|
||||||
|
| `OnObjectStateChanged` | `bool bIsActive` | State toggled |
|
||||||
|
|
||||||
|
## 6. Dependencies
|
||||||
|
| System | Relationship |
|
||||||
|
|--------|--------------|
|
||||||
|
| `BPC_InteractionDetector` | Focus and interaction routing |
|
||||||
|
| `DA_InteractionData` | Data-driven interaction config |
|
||||||
|
|
||||||
|
## 7. Reuse Notes
|
||||||
|
- Generic base for all "press E to use" world objects
|
||||||
|
- Can be subclassed for specialized behavior
|
||||||
264
docs/blueprints/04-inventory/24_BPC_ContainerInventory.md
Normal file
264
docs/blueprints/04-inventory/24_BPC_ContainerInventory.md
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
# `BPC_ContainerInventory` — Container / Loot Storage
|
||||||
|
|
||||||
|
**Parent Class:** `ActorComponent`
|
||||||
|
**Category:** Inventory
|
||||||
|
**Target UE Version:** 5.5–5.7
|
||||||
|
**Build Phase:** 3 — Inventory
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Overview
|
||||||
|
|
||||||
|
`BPC_ContainerInventory` provides an inventory for world actors like loot crates, drawers, cabinets, corpses, and safe storage. It shares the same `S_InventorySlot`/`S_InventoryEntry` structs as [`BPC_InventoryComponent`](docs/blueprints/04-inventory/22_BPC_InventoryComponent.md) but operates as a standalone container with no player controller — it is interacted with via [`BPC_InteractionDetector`](docs/blueprints/03-interaction/16_BPC_InteractionDetector.md) and transfers items to/from the player's inventory.
|
||||||
|
|
||||||
|
It supports lock state, loot tables (random fill from `DA_LootTable`), respawning, decay, and implements `I_Interactable` and `I_Persistable`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Enums
|
||||||
|
|
||||||
|
### `E_ContainerLockState`
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Unlocked` | Anyone can open and loot. |
|
||||||
|
| `Locked` | Requires key or lockpick to open. |
|
||||||
|
| `Jammed` | Lock is broken; cannot be opened (visual only). |
|
||||||
|
| `KeyRequired` | Requires specific key item from inventory. |
|
||||||
|
| `PuzzleRequired` | Requires external puzzle completion. |
|
||||||
|
|
||||||
|
### `E_ContainerFillMethod`
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Fixed` | Contents defined in a `DA_FixedContainerContents` data asset. |
|
||||||
|
| `LootTable` | Contents generated from a `DA_LootTable`. |
|
||||||
|
| `Empty` | Starts empty; items placed manually by designer. |
|
||||||
|
| `PlayerStored` | Items stored by the player (drop-off box, cupboard). |
|
||||||
|
|
||||||
|
### `E_ContainerLootRarity`
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Trash` | Junk items, low-value resources. |
|
||||||
|
| `Common` | Standard consumables, basic resources. |
|
||||||
|
| `Uncommon` | Better gear, moderate resources. |
|
||||||
|
| `Rare` | Good weapons, valuable items. |
|
||||||
|
| `Legendary` | Unique story items, top-tier gear. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Structs
|
||||||
|
|
||||||
|
### `S_ContainerConfig`
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `ContainerName` | `FText` | Display name shown in UI ("Wooden Crate", "File Cabinet"). |
|
||||||
|
| `MaxSlots` | `int32` | Number of inventory slots. |
|
||||||
|
| `FillMethod` | `E_ContainerFillMethod` | How contents are generated. |
|
||||||
|
| `LootTable` | `TArray<FName>` | Loot table IDs to roll on fill. |
|
||||||
|
| `LockState` | `E_ContainerLockState` | Starting lock state. |
|
||||||
|
| `RequiredKeyTag` | `FGameplayTag` | Required item tag if KeyRequired. |
|
||||||
|
| `bCanRespawnLoot` | `bool` | If true, loot respawns after a timer. |
|
||||||
|
| `RespawnTimeHours` | `float` | In-game hours before loot respawns. |
|
||||||
|
| `bDecayEnabled` | `bool` | If true, items decay and disappear over time. |
|
||||||
|
| `DecayTimeHours` | `float` | In-game hours before items decay entirely. |
|
||||||
|
| `bIsPlayerStorage` | `bool` | If true, items persist and are safe (player stash). |
|
||||||
|
| `bReplicates` | `bool` | Whether container state is replicated. |
|
||||||
|
|
||||||
|
### `S_ContainerLootEntry`
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `ItemData` | `DA_ItemData*` | Possible item to spawn. |
|
||||||
|
| `Weight` | `float` | Relative spawn weight for random selection. |
|
||||||
|
| `MinCount` | `int32` | Minimum stack count. |
|
||||||
|
| `MaxCount` | `int32` | Maximum stack count. |
|
||||||
|
| `Rarity` | `E_ContainerLootRarity` | Rarity tier for filtering. |
|
||||||
|
| `bGuaranteed` | `bool` | If true, guaranteed to spawn (bypassed weighted chance). |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Variables
|
||||||
|
|
||||||
|
### Configuration (Instance Editable)
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `ContainerConfig` | `S_ContainerConfig` | Primary container configuration. |
|
||||||
|
| `FixedContents` | `TArray<FStartingItemEntry>` | Fixed items to place at BeginPlay. |
|
||||||
|
|
||||||
|
### State (Blueprint Read Only, Replicated)
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `Slots` | `TArray<S_InventorySlot>` | Container inventory slots. |
|
||||||
|
| `LockState` | `E_ContainerLockState` | Current lock state. |
|
||||||
|
| `bIsLooted` | `bool` | True after initial loot has been taken. |
|
||||||
|
| `bIsOpen` | `bool` | Whether the container UI is open. |
|
||||||
|
|
||||||
|
### Internal (Not Replicated)
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `OwningInventoryComponent` | `BPC_InventoryComponent*` | Cached reference to interacting player's inventory (set when opened). |
|
||||||
|
| `OwningActor` | `AActor*` | Cached owning actor (the container mesh). |
|
||||||
|
| `RespawnTimerHandle` | `FTimerHandle` | Handle for loot respawn timer. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Functions & Events
|
||||||
|
|
||||||
|
### Public Functions
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `Interact_Implementation` | Implements `I_Interactable`: opens container UI if conditions met. |
|
||||||
|
| `OpenContainer` | Called on interaction: sends `Slots` data to player's UI. |
|
||||||
|
| `CloseContainer` | Closes container UI, clears interacting player reference. |
|
||||||
|
| `AddItemToContainer` | Adds item to container slots (transfer from player). |
|
||||||
|
| `RemoveItemFromContainer` | Removes item from container (transfer to player). |
|
||||||
|
| `TryUnlock` | Attempts to unlock container with key/lockpick. |
|
||||||
|
| `GenerateLoot` | Generates container contents from loot table or fixed data. |
|
||||||
|
| `ClearContainer` | Empties all slots. |
|
||||||
|
| `IsContainerEmpty` | Returns true if all slots are empty. |
|
||||||
|
| `HasPlayerTransferTarget` | Returns true if a player is actively interacting with this container. |
|
||||||
|
|
||||||
|
### Protected Functions
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `BeginPlay` | Caches references, calls `GenerateLoot` if fill method is `Fixed` or `LootTable`. |
|
||||||
|
| `ValidatePlayerInteraction` | Checks distance, lock state, player alive state. |
|
||||||
|
| `RollLootTable` | Performs weighted random selection from loot table. |
|
||||||
|
| `StartRespawnTimer` | Starts timer for loot respawn. |
|
||||||
|
| `OnRespawnTimer` | Calls `GenerateLoot` and refills slots. |
|
||||||
|
| `StartDecayTimer` | Starts timer for item decay. |
|
||||||
|
| `OnDecayTimer` | Removes all items, sets `bIsLooted = false`. |
|
||||||
|
| `OnRep_Slots` | RepNotify: updates UI on remote client. |
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Payload | Description |
|
||||||
|
|------------|---------|-------------|
|
||||||
|
| `OnContainerOpened` | `AActor* InteractingPlayer` | Fired when container is opened. |
|
||||||
|
| `OnContainerClosed` | — | Fired when container UI is closed. |
|
||||||
|
| `OnContainerItemAdded` | `S_InventoryEntry Item` | Fired on item transfer into container. |
|
||||||
|
| `OnContainerItemRemoved` | `S_InventoryEntry Item` | Fired on item transfer out of container. |
|
||||||
|
| `OnContainerUnlocked` | `AActor* Instigator` | Fired when container is unlocked. |
|
||||||
|
| `OnContainerLootGenerated` | — | Fired after loot generation. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Blueprint Graph Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
Event BeginPlay
|
||||||
|
→ Cache OwningActor
|
||||||
|
→ Create empty Slots array (MaxSlots)
|
||||||
|
→ If FillMethod == Fixed → Add FixedContents
|
||||||
|
→ If FillMethod == LootTable → Call GenerateLoot
|
||||||
|
→ If LockState != Unlocked → Set bIsLocked as appropriate
|
||||||
|
|
||||||
|
OpenContainer
|
||||||
|
→ ValidatePlayerInteraction → If fail → return
|
||||||
|
→ Set bIsOpen = true
|
||||||
|
→ Cache interacting player's inventory component
|
||||||
|
→ Broadcast OnContainerOpened
|
||||||
|
→ (BP implementation) Open UI widget showing container slots
|
||||||
|
|
||||||
|
CloseContainer
|
||||||
|
→ Set bIsOpen = false
|
||||||
|
→ Clear cached player inventory reference
|
||||||
|
→ Broadcast OnContainerClosed
|
||||||
|
→ If All slots empty → Set bIsLooted = true
|
||||||
|
→ If bIsLooted and bCanRespawnLoot → StartRespawnTimer
|
||||||
|
→ If bDecayEnabled and container has items → StartDecayTimer
|
||||||
|
|
||||||
|
GenerateLoot
|
||||||
|
→ Clear all slots
|
||||||
|
→ For each category in LootTable:
|
||||||
|
→ For each S_ContainerLootEntry:
|
||||||
|
→ If bGuaranteed → Add item to slot
|
||||||
|
→ Else → Random roll against Weight
|
||||||
|
→ If success → Random quantity between MinCount and MaxCount
|
||||||
|
→ Add to slot
|
||||||
|
→ Broadcast OnContainerLootGenerated
|
||||||
|
|
||||||
|
TryUnlock (ItemTag)
|
||||||
|
→ If LockState == KeyRequired and ItemTag == RequiredKeyTag:
|
||||||
|
→ Set LockState = Unlocked
|
||||||
|
→ Broadcast OnContainerUnlocked
|
||||||
|
→ Return true
|
||||||
|
→ Else → Return false
|
||||||
|
|
||||||
|
AddItemToContainer (ItemData, Quantity)
|
||||||
|
→ Find free slot → If none → return (container full)
|
||||||
|
→ Find existing stack → If found and not full → increment
|
||||||
|
→ Else → new slot
|
||||||
|
→ If bReplicates → Mark slots dirty
|
||||||
|
→ Broadcast OnContainerItemAdded
|
||||||
|
|
||||||
|
RemoveItemFromContainer (SlotIndex, Quantity)
|
||||||
|
→ Decrement stack, clear slot if zero
|
||||||
|
→ Broadcast OnContainerItemRemoved
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Replication
|
||||||
|
|
||||||
|
| Variable | Replication | Callback |
|
||||||
|
|----------|-------------|----------|
|
||||||
|
| `Slots` | `RepNotify` | `OnRep_Slots` — refreshes UI on remote clients |
|
||||||
|
| `LockState` | `Replicated` | — |
|
||||||
|
| `bIsOpen` | `Replicated` | — |
|
||||||
|
| `bIsLooted` | `Replicated` | — |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Dependencies & Communication
|
||||||
|
|
||||||
|
| System | Relationship |
|
||||||
|
|--------|--------------|
|
||||||
|
| `BPC_InteractionDetector` | Detects container and routes interaction to `OpenContainer`. |
|
||||||
|
| `BPC_InventoryComponent` | Target for item transfers when player loots the container. |
|
||||||
|
| `DA_LootTable` | Data asset containing weighted loot entries. |
|
||||||
|
| `BPC_InventoryUIManager` | Receives container slot data for display. |
|
||||||
|
| `I_Persistable` | Container saves its inventory state for persistence. |
|
||||||
|
| `BPC_LeverPuzzleComponent` | May unlock container when puzzle is solved (`PuzzleRequired` lock state). |
|
||||||
|
| `BPC_PlayerMetricsTracker` | Logs loot acquisition events. |
|
||||||
|
| `BPC_AdaptiveDifficulty` | May adjust loot table weights based on difficulty. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Success Criteria
|
||||||
|
|
||||||
|
1. Player interacts with container → UI opens showing contents.
|
||||||
|
2. Items can be transferred from container to player inventory.
|
||||||
|
3. Items can be transferred from player inventory to container.
|
||||||
|
4. Locked container plays feedback; unlocked with correct key.
|
||||||
|
5. Fixed containers spawn designer-defined items.
|
||||||
|
6. Loot-table containers generate random items with correct weighting.
|
||||||
|
7. Empty containers show empty slots correctly.
|
||||||
|
8. Respawn timer refills container after all loot is taken.
|
||||||
|
9. Decay timer clears items after configured time.
|
||||||
|
10. Multiplayer: container state syncs to all clients.
|
||||||
|
11. Save/load restores container inventory and lock state.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Data Flow Summary
|
||||||
|
|
||||||
|
```
|
||||||
|
Player approaches loot crate and presses Interact
|
||||||
|
→ BPC_InteractionDetector → BP_LootCrate.Interact_Implementation
|
||||||
|
→ BPC_ContainerInventory.OpenContainer
|
||||||
|
→ Validate distance, lock state, player alive
|
||||||
|
→ Send Slots array to UI
|
||||||
|
→ Player clicks item in container UI
|
||||||
|
→ BPC_ContainerInventory.RemoveItemFromContainer
|
||||||
|
→ BPC_InventoryComponent.AddItem (transfer)
|
||||||
|
→ Broadcast OnContainerItemRemoved
|
||||||
|
→ UI updates both panels
|
||||||
255
docs/blueprints/04-inventory/25_BP_ItemPickup.md
Normal file
255
docs/blueprints/04-inventory/25_BP_ItemPickup.md
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
# `BP_ItemPickup` — World Item Pickup Actor
|
||||||
|
|
||||||
|
**Parent Class:** `Actor`
|
||||||
|
**Category:** Inventory
|
||||||
|
**Target UE Version:** 5.5–5.7
|
||||||
|
**Build Phase:** 3 — Inventory
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Overview
|
||||||
|
|
||||||
|
`BP_ItemPickup` is a world-placed or runtime-spawned `Actor` representing an item the player can pick up. It implements [`I_Interactable`](docs/blueprints/01-core/03_I_InterfaceLibrary.md) and optionally [`I_Persistable`](docs/blueprints/01-core/03_I_InterfaceLibrary.md).
|
||||||
|
|
||||||
|
When the player interacts (via [`BPC_InteractionDetector`](docs/blueprints/03-interaction/16_BPC_InteractionDetector.md)), the pickup calls `BPC_InventoryComponent.AddItem` with the associated item data, plays feedback, and either destroys itself or depletes a stack count.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Enums
|
||||||
|
|
||||||
|
### `E_PickupSpawnReason`
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `PlacedInLevel` | Designer-placed in the map. |
|
||||||
|
| `DroppedFromInventory` | Player dropped from inventory. |
|
||||||
|
| `LootDrop` | Spawned as loot from a creature/container. |
|
||||||
|
| `QuestReward` | Spawned as a quest completion reward. |
|
||||||
|
|
||||||
|
### `E_PickupVisualState`
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Idle` | Default floating/rotating state. |
|
||||||
|
| `Highlighted` | Within interaction range; glowing outline. |
|
||||||
|
| `BeingPickedUp` | Playing pickup animation before destruction. |
|
||||||
|
| `Respawning` | Hidden while respawn timer counts down. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Structs
|
||||||
|
|
||||||
|
### `S_PickupConfig`
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `ItemData` | `DA_ItemData*` | Primary item data asset reference. |
|
||||||
|
| `Quantity` | `int32` | Stack quantity (1 for non-stackable). |
|
||||||
|
| `SpawnReason` | `E_PickupSpawnReason` | Initial spawn reason. |
|
||||||
|
| `bIsInfinite` | `bool` | If true, item is never consumed on pickup. |
|
||||||
|
| `bRespawns` | `bool` | If true, re-appears after a timer. |
|
||||||
|
| `RespawnTimeSeconds` | `float` | Seconds before respawning (in-game or real-time based on config). |
|
||||||
|
| `bUsePhysicsOnDrop` | `bool` | If true, dropped items have physics simulation. |
|
||||||
|
| `DropVelocity` | `FVector` | Initial velocity when dropped from inventory. |
|
||||||
|
| `bAutoRotate` | `bool` | Whether the pickup rotates slowly while idle. |
|
||||||
|
| `RotationRate` | `FRotator` | Rotation speed when auto-rotate is enabled. |
|
||||||
|
| `bBobUpDown` | `bool` | Whether the pickup bobs up and down. |
|
||||||
|
| `BobAmplitude` | `float` | Bobbing height offset. |
|
||||||
|
| `BobFrequency` | `float` | Bobbing speed. |
|
||||||
|
|
||||||
|
### `S_PickupWorldState`
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `SpawnTransform` | `FTransform` | Transform at spawn time (for respawn reference). |
|
||||||
|
| `SpawnTime` | `float` | Game time at spawn. |
|
||||||
|
| `bHasBeenPickedUp` | `bool` | Whether pickup has ever been collected. |
|
||||||
|
| `PickedUpBy` | `AActor*` | Last player to pick up this item. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Variables
|
||||||
|
|
||||||
|
### Configuration (Instance Editable)
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `Config` | `S_PickupConfig` | Primary configuration. |
|
||||||
|
| `bAutoSetup` | `bool` | If true, auto-configures mesh and collision at `BeginPlay`. |
|
||||||
|
| `PickupWidgetClass` | `TSubclassOf<UUserWidget>` | Optional widget class for pickup prompt. |
|
||||||
|
|
||||||
|
### Scene Components (Create & Attach at Construction)
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `RootScene` | `USceneComponent*` | Root component. |
|
||||||
|
| `MeshComponent` | `UStaticMeshComponent*` | Visual representation of item. |
|
||||||
|
| `InteractionCollision` | `USphereComponent*` | Overlap sphere for interaction detection. |
|
||||||
|
| `FloatCurveComponent` | `UFloatingPawnMovement*` | Optional floating/bobbing behaviour component. |
|
||||||
|
|
||||||
|
### State (Blueprint Read Only, Replicated)
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `VisualState` | `E_PickupVisualState` | Current visual state. |
|
||||||
|
| `bIsAvailable` | `bool` | Whether the pickup can be collected. |
|
||||||
|
|
||||||
|
### Internal (Blueprint Read Only)
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `WorldState` | `S_PickupWorldState` | Tracks pickup history. |
|
||||||
|
| `OwningInventory` | `BPC_InventoryComponent*` | Cached when player overlaps interaction trigger. |
|
||||||
|
| `RespawnTimerHandle` | `FTimerHandle` | Handle for respawn delay. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Functions & Events
|
||||||
|
|
||||||
|
### Public Functions
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `Interact_Implementation` | Implements `I_Interactable`: adds item to player inventory, plays feedback, destroys or depletes. |
|
||||||
|
| `OnDroppedFromInventory` | Called by inventory drop system: sets transform, velocity, and respawn behaviour. |
|
||||||
|
| `BeginPlay` | Caches references, sets up bobbing/rotation timeline, configures collision. |
|
||||||
|
| `SetPickupEnabled` | Toggles visibility and collision. |
|
||||||
|
| `StartRespawn` | Hides pickup and starts countdown timer. |
|
||||||
|
| `OnRespawnComplete` | Restores pickup to world (visual + collision). |
|
||||||
|
| `GetPickupDisplayInfo` | Returns `FText` name and `UTexture2D*` icon for UI prompt. |
|
||||||
|
|
||||||
|
### Protected Functions
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `OnOverlapBegin` | When player enters overlap: highlight, show pickup prompt. |
|
||||||
|
| `OnOverlapEnd` | When player leaves overlap: unhighlight, hide pickup prompt. |
|
||||||
|
| `OnPickupAnimationComplete` | Called after pickup animation ends (destroys actor or hides). |
|
||||||
|
| `PlayPickupFeedback` | Plays sound, particle, and/or widget animation. |
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Payload | Description |
|
||||||
|
|------------|---------|-------------|
|
||||||
|
| `OnPickupAvailableChanged` | `bool bIsAvailable` | Broadcast when pickup becomes available/unavailable. |
|
||||||
|
| `OnPickupCollected` | `AActor* Collector` | Broadcast when item is successfully collected. |
|
||||||
|
| `OnPickupRespawnStarted` | `float RespawnTime` | Broadcast when respawn timer begins. |
|
||||||
|
| `OnPickupRespawned` | — | Broadcast when pickup reappears. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Blueprint Graph Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
Event BeginPlay
|
||||||
|
→ Cache WorldState.SpawnTransform = ActorTransform
|
||||||
|
→ If bAutoSetup → Set MeshComponent from ItemData
|
||||||
|
→ Configure collision presets (OverlapOnlyPawn)
|
||||||
|
→ Attach Timeline for bobbing / rotation
|
||||||
|
→ Start idle loop
|
||||||
|
|
||||||
|
OnOverlapBegin (Player overlaps InteractionCollision)
|
||||||
|
→ Set VisualState = Highlighted
|
||||||
|
→ Show pickup prompt widget (if assigned)
|
||||||
|
→ Cache player's BPC_InventoryComponent
|
||||||
|
|
||||||
|
OnOverlapEnd (Player leaves InteractionCollision)
|
||||||
|
→ Set VisualState = Idle
|
||||||
|
→ Hide pickup prompt widget
|
||||||
|
→ Clear cached inventory component reference
|
||||||
|
|
||||||
|
Interact_Implementation (Called from BPC_InteractionDetector)
|
||||||
|
→ If !bIsAvailable → return (no interaction)
|
||||||
|
→ Validate player inventory has space → if full, show "Inventory Full" feedback → return
|
||||||
|
→ Set VisualState = BeingPickedUp
|
||||||
|
→ Play pickup sound + particle effect
|
||||||
|
→ Call BPC_InventoryComponent.AddItem(Config.ItemData, Config.Quantity)
|
||||||
|
→ If AddItem succeeded:
|
||||||
|
→ Broadcast OnPickupCollected(InteractingPlayer)
|
||||||
|
→ If bIsInfinite → do not destroy
|
||||||
|
→ Else → Deplete stack or destroy
|
||||||
|
→ If bRespawns → StartRespawn
|
||||||
|
→ Else → DestroyActor
|
||||||
|
→ Else:
|
||||||
|
→ Set VisualState = Idle
|
||||||
|
→ Show error feedback
|
||||||
|
|
||||||
|
OnDroppedFromInventory (ItemData, Quantity, DropLocation, DropVelocity)
|
||||||
|
→ Set Config with ItemData and Quantity
|
||||||
|
→ Set Config.SpawnReason = DroppedFromInventory
|
||||||
|
→ Set ActorTransform = DropLocation
|
||||||
|
→ If bUsePhysicsOnDrop → Enable physics on MeshComponent, apply velocity
|
||||||
|
→ Enable collision and visibility
|
||||||
|
→ Set bIsAvailable = true
|
||||||
|
|
||||||
|
StartRespawn
|
||||||
|
→ Set bIsAvailable = false
|
||||||
|
→ Hide MeshComponent, disable collision
|
||||||
|
→ Broadcast OnPickupRespawnStarted(RespawnTimeSeconds)
|
||||||
|
→ Start timer (RespawnTimerHandle, OnRespawnComplete, RespawnTimeSeconds)
|
||||||
|
|
||||||
|
OnRespawnComplete
|
||||||
|
→ Show MeshComponent, enable collision
|
||||||
|
→ Set bIsAvailable = true
|
||||||
|
→ Set VisualState = Idle
|
||||||
|
→ Broadcast OnPickupRespawned
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Replication
|
||||||
|
|
||||||
|
| Variable | Replication | Callback |
|
||||||
|
|----------|-------------|----------|
|
||||||
|
| `bIsAvailable` | `Replicated` | — |
|
||||||
|
| `VisualState` | `Replicated Using` | `OnRep_VisualState` |
|
||||||
|
| `Config.Quantity` | `Replicated` | — |
|
||||||
|
| `WorldState.SpawnTransform` | `Replicated` | — |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Dependencies & Communication
|
||||||
|
|
||||||
|
| System | Relationship |
|
||||||
|
|--------|--------------|
|
||||||
|
| `I_Interactable` | Implements the interface for interaction detection. |
|
||||||
|
| `BPC_InteractionDetector` | Sends overlap events and routes interaction call. |
|
||||||
|
| `BPC_InventoryComponent` | Target for item addition. |
|
||||||
|
| `DA_ItemData` | Supplies mesh, name, icon, stack limits. |
|
||||||
|
| `BPC_PlayerMetricsTracker` | Logs pickup event (item type, quantity, location). |
|
||||||
|
| `I_Persistable` | Optional: saves pickup state (available, transform, quantity). |
|
||||||
|
| `BPC_InventoryWeightSystem` | Checks weight capacity before allowing pickup. |
|
||||||
|
| `BPC_AdaptiveDifficulty` | May adjust item availability based on difficulty. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Success Criteria
|
||||||
|
|
||||||
|
1. World-placed pickup shows mesh and collision at game start.
|
||||||
|
2. Player overlaps pickup → highlight activates + prompt widget shows.
|
||||||
|
3. Player leaves overlap → highlight deactivates + prompt hides.
|
||||||
|
4. Player interacts → item added to inventory, feedback plays, pickup destroyed.
|
||||||
|
5. Dropped from inventory → pickup spawns at correct transform with physics velocity.
|
||||||
|
6. Infinite pickups never deplete on collection.
|
||||||
|
7. Respawn pickups re-appear after configured timer.
|
||||||
|
8. Respawning pickups hide collision and mesh during cooldown.
|
||||||
|
9. Multiplayer: pickups replicate state to all clients.
|
||||||
|
10. Pickup prompt displays correct item name and icon.
|
||||||
|
11. No interaction if player inventory is full (feedback shown).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Data Flow Summary
|
||||||
|
|
||||||
|
```
|
||||||
|
Player approaches world item → Overlap event
|
||||||
|
→ BPC_InteractionDetector → BP_ItemPickup sets VisualState = Highlighted
|
||||||
|
→ Show pickup prompt widget
|
||||||
|
Player presses Interact key
|
||||||
|
→ BPC_InteractionDetector.InteractTarget → BP_ItemPickup.Interact_Implementation
|
||||||
|
→ Validate availability + inventory space
|
||||||
|
→ BPC_InventoryComponent.AddItem (ItemData, Quantity)
|
||||||
|
→ Play pickup sound + particle
|
||||||
|
→ Broadcast OnPickupCollected
|
||||||
|
→ If bRespawns → StartRespawn (hide, wait, reappear)
|
||||||
|
→ Else → DestroyActor
|
||||||
86
docs/blueprints/04-inventory/26_BPC_ActiveItemSystem.md
Normal file
86
docs/blueprints/04-inventory/26_BPC_ActiveItemSystem.md
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
# BPC_ActiveItemSystem — Active Item System
|
||||||
|
|
||||||
|
**Parent Class:** `ActorComponent`
|
||||||
|
**Category:** Inventory
|
||||||
|
**Target UE Version:** 5.5–5.7
|
||||||
|
**Build Phase:** 3 — Inventory
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Overview
|
||||||
|
|
||||||
|
`BPC_ActiveItemSystem` manages the player's currently selected/active item and quick slot assignments. It reads from [`BPC_InventorySystem`](BPC_InventorySystem.md) and maps up to 8 quick slots to inventory items, handling hotkey input, item switching, and active item context (usable, throwable, consumable).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Enums
|
||||||
|
|
||||||
|
### `E_QuickSlot`
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Slot_1` | Hotkey 1 |
|
||||||
|
| `Slot_2` | Hotkey 2 |
|
||||||
|
| `Slot_3` | Hotkey 3 |
|
||||||
|
| `Slot_4` | Hotkey 4 |
|
||||||
|
| `Slot_5` | Hotkey 5 |
|
||||||
|
| `Slot_6` | Hotkey 6 |
|
||||||
|
| `Slot_7` | Hotkey 7 |
|
||||||
|
| `Slot_8` | Hotkey 8 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `QuickSlots` | `TMap<E_QuickSlot, FGuid>` | Hotkey → Inventory Item ID mapping |
|
||||||
|
| `ActiveSlot` | `E_QuickSlot` | Currently selected quick slot |
|
||||||
|
| `ActiveItem` | `S_InventoryEntry` | Currently active item data |
|
||||||
|
| `bHasActiveItem` | `bool` | An item is currently selected |
|
||||||
|
| `bCanSwitchItems` | `bool` | Item switching allowed (not during anim) |
|
||||||
|
| `SwitchCooldown` | `float` | Min time between item switches |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `SetQuickSlot` | Assigns an inventory item to a quick slot |
|
||||||
|
| `ClearQuickSlot` | Clears a quick slot assignment |
|
||||||
|
| `SelectSlot` | Sets the active quick slot by key press |
|
||||||
|
| `GetActiveItem` | Returns current `S_InventoryEntry` |
|
||||||
|
| `UseActiveItem` | Uses/consumes the active item |
|
||||||
|
| `CycleNextItem` | Selects the next populated quick slot |
|
||||||
|
| `CyclePreviousItem` | Selects the previous populated quick slot |
|
||||||
|
| `IsQuickSlotEmpty` | Returns true if quick slot has no item |
|
||||||
|
| `GetQuickSlotItem` | Returns item in a specific quick slot |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Payload | Description |
|
||||||
|
|------------|---------|-------------|
|
||||||
|
| `OnActiveItemChanged` | `S_InventoryEntry NewItem`, `E_QuickSlot Slot` | Active item switched |
|
||||||
|
| `OnQuickSlotAssigned` | `E_QuickSlot Slot`, `FGuid ItemID` | Item assigned to quick slot |
|
||||||
|
| `OnQuickSlotCleared` | `E_QuickSlot Slot` | Quick slot cleared |
|
||||||
|
| `OnActiveItemUsed` | `S_InventoryEntry Item` | Active item consumed |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Dependencies & Communication
|
||||||
|
|
||||||
|
| System | Relationship |
|
||||||
|
|--------|--------------|
|
||||||
|
| `BPC_InventorySystem` | Reads inventory slots; source of truth for items |
|
||||||
|
| `BPC_EquipmentSlotSystem` | Automatically updates quick slots on equipment change |
|
||||||
|
| `WBP_HUDController` | Displays active item in HUD |
|
||||||
|
| `BPC_HealthSystem` | Consumed health items trigger healing |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Reuse Notes
|
||||||
|
- Quick slots are configurable per project (4-8 slots)
|
||||||
|
- Active item system is the bridge between inventory data and gameplay input
|
||||||
49
docs/blueprints/04-inventory/27_BPC_CollectibleTracker.md
Normal file
49
docs/blueprints/04-inventory/27_BPC_CollectibleTracker.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# BPC_CollectibleTracker — Collectible Tracker
|
||||||
|
|
||||||
|
**Parent Class:** `ActorComponent`
|
||||||
|
**Category:** Inventory
|
||||||
|
**Target UE Version:** 5.5–5.7
|
||||||
|
**Build Phase:** 3 — Inventory
|
||||||
|
|
||||||
|
## 1. Overview
|
||||||
|
|
||||||
|
`BPC_CollectibleTracker` tracks player collection of rare/optional items — figurines, hidden artifacts, secret tapes. Supports per-collection progress, completion rewards, and UI indicators for collectible hunters.
|
||||||
|
|
||||||
|
## 2. Variables
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `CollectionProgress` | `TMap<FGameplayTag, int32>` | Collected count per collection type |
|
||||||
|
| `CollectionTargets` | `TMap<FGameplayTag, int32>` | Total items per collection |
|
||||||
|
| `bShowCollectibleNotifications` | `bool` | Toast on collectible pickup |
|
||||||
|
| `CompletionRewards` | `TMap<FGameplayTag, TArray<FPrimaryAssetId>>` | Items granted on collection completion |
|
||||||
|
|
||||||
|
## 3. Functions
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `OnCollectiblePickedUp` | Records collectible acquisition |
|
||||||
|
| `GetProgress` | Returns progress (current/total) for a collection |
|
||||||
|
| `IsCollectionComplete` | Checks if collection is fully completed |
|
||||||
|
| `GetAllCollections` | Returns all tracked collections with progress |
|
||||||
|
| `GetTotalCompletion` | Returns overall completion percentage |
|
||||||
|
|
||||||
|
## 4. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Payload | Description |
|
||||||
|
|------------|---------|-------------|
|
||||||
|
| `OnCollectibleFound` | `FGameplayTag CollectionTag`, `int32 NewCount` | New collectible acquired |
|
||||||
|
| `OnCollectionComplete` | `FGameplayTag CollectionTag` | Collection fully completed |
|
||||||
|
| `OnAllCollectionsComplete` | — | All collections complete |
|
||||||
|
|
||||||
|
## 5. Dependencies
|
||||||
|
|
||||||
|
| System | Relationship |
|
||||||
|
|--------|--------------|
|
||||||
|
| `BPC_InventorySystem` | Collectibles as inventory items |
|
||||||
|
| `BPC_AchievementManager` | Achievement triggers for completions |
|
||||||
|
| `WBP_HUDController` | Toast notification display |
|
||||||
|
|
||||||
|
## 6. Reuse Notes
|
||||||
|
- Data-driven collection definitions via gameplay tags
|
||||||
|
- Rewards are granted as inventory items on collection completion
|
||||||
62
docs/blueprints/04-inventory/28_BPC_ConsumableSystem.md
Normal file
62
docs/blueprints/04-inventory/28_BPC_ConsumableSystem.md
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
# BPC_ConsumableSystem — Consumable System
|
||||||
|
|
||||||
|
**Parent Class:** `ActorComponent`
|
||||||
|
**Category:** Inventory
|
||||||
|
**Target UE Version:** 5.5–5.7
|
||||||
|
**Build Phase:** 3 — Inventory
|
||||||
|
|
||||||
|
## 1. Overview
|
||||||
|
|
||||||
|
`BPC_ConsumableSystem` manages consumable item usage — health packs, stamina boosters, temporary buff items, and one-time-use consumables. Reads from [`BPC_InventorySystem`](BPC_InventorySystem.md) and applies effects through the relevant player systems.
|
||||||
|
|
||||||
|
## 2. Variables
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `ConsumableCooldown` | `float` | Global cooldown between consumable uses |
|
||||||
|
| `ActiveBuffs` | `TArray<S_ActiveBuff>` | Currently active temporary buffs |
|
||||||
|
| `bCanUseConsumables` | `bool` | Master toggle (blocked during certain states) |
|
||||||
|
|
||||||
|
## 3. Structs
|
||||||
|
|
||||||
|
### `S_ActiveBuff`
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `BuffTag` | `FGameplayTag` | Buff identifier |
|
||||||
|
| `RemainingDuration` | `float` | Time remaining |
|
||||||
|
| `BuffData` | `DA_ItemData*` | Source item data |
|
||||||
|
| `AffectedStat` | `E_PlayerStat` | Health, Stamina, Speed, Stress, Damage |
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `UseConsumable` | Uses a consumable item from inventory slot |
|
||||||
|
| `ApplyBuff` | Applies a temporary buff to the player |
|
||||||
|
| `RemoveBuff` | Removes a buff by tag |
|
||||||
|
| `GetActiveBuffs` | Returns all active buffs |
|
||||||
|
| `HasBuff` | Checks if a specific buff is active |
|
||||||
|
| `TickBuffs` | Decrements buff durations, removes expired |
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Payload | Description |
|
||||||
|
|------------|---------|-------------|
|
||||||
|
| `OnConsumableUsed` | `S_InventoryEntry Item` | Consumable item used |
|
||||||
|
| `OnBuffApplied` | `FGameplayTag BuffTag`, `float Duration` | Buff activated |
|
||||||
|
| `OnBuffExpired` | `FGameplayTag BuffTag` | Buff ended |
|
||||||
|
|
||||||
|
## 6. Dependencies
|
||||||
|
|
||||||
|
| System | Relationship |
|
||||||
|
|--------|--------------|
|
||||||
|
| `BPC_InventorySystem` | Reads and modifies consumable items |
|
||||||
|
| `BPC_HealthSystem` | Health restoration via consumables |
|
||||||
|
| `BPC_StaminaSystem` | Stamina restoration |
|
||||||
|
| `BPC_StressSystem` | Stress reduction items |
|
||||||
|
| `BPC_MovementStateSystem` | Speed buffs |
|
||||||
|
|
||||||
|
## 7. Reuse Notes
|
||||||
|
- All consumable effects are data-driven via DA_ItemData
|
||||||
|
- Buffs support stacking (multiple buffs active simultaneously)
|
||||||
49
docs/blueprints/04-inventory/29_BPC_DocumentArchiveSystem.md
Normal file
49
docs/blueprints/04-inventory/29_BPC_DocumentArchiveSystem.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# BPC_DocumentArchiveSystem — Document Archive System
|
||||||
|
|
||||||
|
**Parent Class:** `ActorComponent`
|
||||||
|
**Category:** Inventory
|
||||||
|
**Target UE Version:** 5.5–5.7
|
||||||
|
**Build Phase:** 3 — Inventory
|
||||||
|
|
||||||
|
## 1. Overview
|
||||||
|
|
||||||
|
`BPC_DocumentArchiveSystem` manages collected documents — notes, letters, audio logs, data files. Tracks read/unread status, organizes by category, and provides query functions for UI and narrative systems.
|
||||||
|
|
||||||
|
## 2. Variables
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `CollectedDocuments` | `TArray<S_DocumentEntry>` | All collected documents |
|
||||||
|
| `DocumentCategories` | `TArray<E_DocumentCategory>` | Notes, Letters, Audio, Data, Photos |
|
||||||
|
| `bShowUnreadBadge` | `bool` | Highlight unread documents |
|
||||||
|
| `UnreadCount` | `int32` | Total unread documents |
|
||||||
|
|
||||||
|
## 3. Functions
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `AddDocument` | Adds a document to the archive |
|
||||||
|
| `MarkAsRead` | Marks a document as read |
|
||||||
|
| `MarkAllAsRead` | Marks all as read |
|
||||||
|
| `GetDocumentsByCategory` | Returns filtered list |
|
||||||
|
| `GetUnreadCount` | Returns unread count |
|
||||||
|
| `HasDocument` | Checks if document is collected |
|
||||||
|
|
||||||
|
## 4. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Payload | Description |
|
||||||
|
|------------|---------|-------------|
|
||||||
|
| `OnDocumentCollected` | `S_DocumentEntry Document` | New document added |
|
||||||
|
| `OnDocumentRead` | `FGuid DocumentID` | Document marked read |
|
||||||
|
| `OnAllDocumentsRead` | — | All documents read |
|
||||||
|
|
||||||
|
## 5. Dependencies
|
||||||
|
|
||||||
|
| System | Relationship |
|
||||||
|
|--------|--------------|
|
||||||
|
| `BPC_InventorySystem` | Documents as inventory items |
|
||||||
|
| `WBP_JournalDocumentViewer` | UI for viewing documents |
|
||||||
|
| `BPC_LoreUnlockSystem` | Lore document triggers |
|
||||||
|
|
||||||
|
## 6. Reuse Notes
|
||||||
|
- Documents are inventory items with `E_ItemCategory = Document`
|
||||||
305
docs/blueprints/04-inventory/30_BPC_EquipmentSlotSystem.md
Normal file
305
docs/blueprints/04-inventory/30_BPC_EquipmentSlotSystem.md
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
# `BPC_EquipmentSlotSystem` — Equipment & Gear Slots
|
||||||
|
|
||||||
|
**Parent Class:** `ActorComponent`
|
||||||
|
**Category:** Inventory
|
||||||
|
**Target UE Version:** 5.5–5.7
|
||||||
|
**Build Phase:** 3 — Inventory
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Overview
|
||||||
|
|
||||||
|
`BPC_EquipmentSlotSystem` manages wearable gear slots on the player — head, torso, hands, legs, feet, backpack, eyewear, and accessory. It reads from the linked [`BPC_InventorySystem`](docs/blueprints/04-inventory/BPC_InventorySystem.md) and marks items as equipped/unequipped via the inventory's `S_InventoryEntry.bIsEquipped` flag.
|
||||||
|
|
||||||
|
Equipping moves stat modifiers (defense, move speed, carry capacity, sensory range) into a live `S_EquipmentModifiers` struct that other systems query. Unequipping removes them. Equipment also attaches skeletal meshes to character socket points for visual representation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Mermaid — Equipment Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[Player selects Equip Action] --> B{BPC_EquipmentSlotSystem.EquipItem}
|
||||||
|
B --> C{Slot Occupied?}
|
||||||
|
C -->|Yes| D[Unequip current item]
|
||||||
|
C -->|No| E[Set Item.bIsEquipped = true]
|
||||||
|
|
||||||
|
D --> F[Remove modifiers from active pool]
|
||||||
|
E --> G[Add modifiers to active pool]
|
||||||
|
F --> H[Detach skeletal mesh from socket]
|
||||||
|
G --> I[Attach skeletal mesh to socket]
|
||||||
|
|
||||||
|
H --> J[Broadcast OnEquipmentChanged]
|
||||||
|
I --> J
|
||||||
|
|
||||||
|
J --> K[Systems Query Modifiers]
|
||||||
|
K --> L[BPC_MovementStateSystem]
|
||||||
|
K --> M[BPC_InventorySystem Weight]
|
||||||
|
K --> N[BPC_HealthSystem Defense]
|
||||||
|
K --> O[InteractionDetector Range]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Enums
|
||||||
|
|
||||||
|
### `E_EquipmentSlot`
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Head` | Helmets, masks, headgear. |
|
||||||
|
| `Torso` | Chest armor, vests, jackets. |
|
||||||
|
| `Hands` | Gloves, wrist devices. |
|
||||||
|
| `Legs` | Pants, leg armor. |
|
||||||
|
| `Feet` | Boots, shoes. |
|
||||||
|
| `Backpack` | Back slot — increases carry capacity. |
|
||||||
|
| `Eyewear` | Night vision, goggles, glasses. |
|
||||||
|
| `Accessory_1` | Ring, necklace, badge (slot 1). |
|
||||||
|
| `Accessory_2` | Ring, necklace, badge (slot 2). |
|
||||||
|
| `Weapon_Primary` | Main weapon (slot shared with quick slots). |
|
||||||
|
| `Weapon_Secondary` | Sidearm / backup weapon. |
|
||||||
|
| `Tool` | Equipped tool (lockpick, scanner, flashlight). |
|
||||||
|
|
||||||
|
### `E_EquipResult`
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Success` | Item was equipped. |
|
||||||
|
| `SlotOccupied` | Slot is full; unequip first. |
|
||||||
|
| `NotEquippable` | Item's category does not match any slot. |
|
||||||
|
| `ItemNotFound` | Item is not in inventory. |
|
||||||
|
| `BlockedByState` | Cannot equip in current state (e.g., hiding). |
|
||||||
|
| `BlockedByCondition` | Player condition blocks equip (e.g., injured arm). |
|
||||||
|
|
||||||
|
### `E_EquipAnimationType`
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Instant` | No animation — immediate attach. |
|
||||||
|
| `Holster` | Holster/draw weapon animation. |
|
||||||
|
| `Equip` | Generic equip animation (armor, backpack). |
|
||||||
|
| `Unequip` | Generic unequip animation. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Structs
|
||||||
|
|
||||||
|
### `S_EquipmentSlotDef`
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Slot` | `E_EquipmentSlot` | Which slot this definition is for. |
|
||||||
|
| `SocketName` | `FName` | Skeletal mesh socket to attach equipment visuals to. |
|
||||||
|
| `AllowedCategories` | `TArray<E_ItemCategory>` | Item categories that can be equipped in this slot. |
|
||||||
|
| `bMustBeEmptyToEquip` | `bool` | If true, slot must be empty before equipping. |
|
||||||
|
| `bAutoEquipOnPickup` | `bool` | If true, items matching categories auto-equip. |
|
||||||
|
|
||||||
|
### `S_EquipmentModifiers`
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `DefenseBonus` | `float` | Flat damage reduction applied by HealthSystem (0.0 = none). |
|
||||||
|
| `MoveSpeedModifier` | `float` | Multiplier for character max walk speed (1.0 = normal). |
|
||||||
|
| `CarryCapacityBonus` | `float` | Additional kg the inventory can hold. |
|
||||||
|
| `StaminaDrainModifier` | `float` | Multiplier for stamina drain rate (1.0 = normal). |
|
||||||
|
| `SensoryRangeModifier` | `float` | Multiplier for detection range (1.0 = normal). |
|
||||||
|
| `StressResistanceModifier` | `float` | Multiplier for stress gain (1.0 = normal, 0.5 = half). |
|
||||||
|
| `HealthRegenPerSecond` | `float` | Passive health regen when equipped. |
|
||||||
|
|
||||||
|
### `S_EquipmentState`
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `EquippedItems` | `TMap<E_EquipmentSlot, FGuid>` | Map of slot → ItemID of equipped item. |
|
||||||
|
| `ActiveModifiers` | `S_EquipmentModifiers` | Current summed modifiers from all equipped items. |
|
||||||
|
| `VisualAttachments` | `TMap<E_EquipmentSlot, UChildActorComponent*>` | Map of slot → attached mesh component. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Variables
|
||||||
|
|
||||||
|
### Configuration (Instance Editable)
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `SlotDefinitions` | `TArray<S_EquipmentSlotDef>` | Definitions for each equipment slot. |
|
||||||
|
| `bAllowUnequipInCombat` | `bool` | If false, cannot unequip during combat. |
|
||||||
|
| `bAutoEquipStartingItems` | `bool` | If true, starting items with matching categories auto-equip. |
|
||||||
|
| `bReplicates` | `bool` | Whether equipment state is replicated. |
|
||||||
|
|
||||||
|
### State (Blueprint Read Only, Replicated)
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `EquippedItems` | `TMap<E_EquipmentSlot, FGuid>` | Currently equipped items by slot. |
|
||||||
|
| `ActiveModifiers` | `S_EquipmentModifiers` | Summed modifier values from all equipped gear. |
|
||||||
|
|
||||||
|
### Internal (Not Replicated)
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `OwningInventory` | `BPC_InventorySystem*` | Cached inventory reference. |
|
||||||
|
| `OwningCharacter` | `ACharacter*` | Cached character for skeletal mesh attachment. |
|
||||||
|
| `VisualAttachments` | `TMap<E_EquipmentSlot, AActor*>` | Spawned attachment actors/meshes. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Functions & Events
|
||||||
|
|
||||||
|
### Public Functions
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `EquipItem` | Equips an item from inventory to its matching slot. Returns `E_EquipResult`. Handles slot swap if occupied. |
|
||||||
|
| `UnequipItem` | Unequips item from a slot. Returns result. Item stays in inventory. |
|
||||||
|
| `UnequipAll` | Unequips all slots. Broadcasts final state. |
|
||||||
|
| `GetEquippedItemInSlot` | Returns `S_InventoryEntry` for the item in a given slot. |
|
||||||
|
| `GetSlotForItemCategory` | Returns the best `E_EquipmentSlot` for an item category. |
|
||||||
|
| `IsSlotOccupied` | Returns true if a slot has an equipped item. |
|
||||||
|
| `GetActiveModifiers` | Returns the current `S_EquipmentModifiers` struct. |
|
||||||
|
| `GetEquippedItems` | Returns `TMap<E_EquipmentSlot, FGuid>` of all equipped items. |
|
||||||
|
| `CanEquipItem` | Pre-checks if an item can be equipped (category, slot, state). Returns `E_EquipResult`. |
|
||||||
|
| `CanUnequipSlot` | Pre-checks if a slot can be unequipped (not locked by state). |
|
||||||
|
|
||||||
|
### Protected Functions
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `BeginPlay` | Caches inventory and character references, auto-equips starting items. |
|
||||||
|
| `FindMatchingSlot` | Finds an `E_EquipmentSlot` whose allowed categories include the item's category. |
|
||||||
|
| `ApplyEquipmentVisual` | Attaches a skeletal or static mesh to the character's socket. |
|
||||||
|
| `RemoveEquipmentVisual` | Detaches and destroys the visual for a slot. |
|
||||||
|
| `RecalculateModifiers` | Iterates all equipped items, sums modifier values into `ActiveModifiers`. |
|
||||||
|
| `SetItemEquippedFlag` | Sets `bIsEquipped` on the inventory entry to true/false. |
|
||||||
|
| `ValidateEquipState` | Called periodically or on save: verifies equipped items still exist in inventory. |
|
||||||
|
| `OnRep_EquippedItems` | RepNotify: rebuilds visual attachments on remote clients. |
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Payload | Description |
|
||||||
|
|------------|---------|-------------|
|
||||||
|
| `OnItemEquipped` | `E_EquipmentSlot Slot`, `S_InventoryEntry Item` | Fired after successful equip. |
|
||||||
|
| `OnItemUnequipped` | `E_EquipmentSlot Slot`, `S_InventoryEntry Item` | Fired after successful unequip. |
|
||||||
|
| `OnEquipmentChanged` | `S_EquipmentModifiers NewModifiers`, `S_EquipmentModifiers OldModifiers` | Fired when any modifier value changes. |
|
||||||
|
| `OnEquipFailed` | `E_EquipResult Reason`, `E_EquipmentSlot Slot` | Fired when equip is rejected. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blueprint Graph Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
Event BeginPlay
|
||||||
|
→ GetOwner → FindComponentByClass BPC_InventorySystem → Cache
|
||||||
|
→ GetOwner → Cast to ACharacter → Cache
|
||||||
|
→ If bAutoEquipStartingItems:
|
||||||
|
→ Inventory has starting items → For each with matching slot → Call EquipItem
|
||||||
|
|
||||||
|
EquipItem (ItemID, Slot)
|
||||||
|
→ CanEquipItem → If fail → return NotEquippable
|
||||||
|
→ Get item from inventory by ItemID
|
||||||
|
→ FindMatchingSlot (item's category)
|
||||||
|
→ If slot is occupied → Call UnequipItem (current slot)
|
||||||
|
→ SetItemEquippedFlag (true)
|
||||||
|
→ Add to EquippedItems map
|
||||||
|
→ ApplyEquipmentVisual (slot, item)
|
||||||
|
→ RecalculateModifiers
|
||||||
|
→ Broadcast OnItemEquipped
|
||||||
|
→ Broadcast OnEquipmentChanged
|
||||||
|
→ Return Success
|
||||||
|
|
||||||
|
UnequipItem (Slot)
|
||||||
|
→ CanUnequipSlot → If fail → return BlockedByState
|
||||||
|
→ Get ItemID from EquippedItems (Slot)
|
||||||
|
→ Get item from inventory by ItemID
|
||||||
|
→ SetItemEquippedFlag (false)
|
||||||
|
→ Remove from EquippedItems map
|
||||||
|
→ RemoveEquipmentVisual (Slot)
|
||||||
|
→ RecalculateModifiers
|
||||||
|
→ Broadcast OnItemUnequipped
|
||||||
|
→ Broadcast OnEquipmentChanged
|
||||||
|
→ Return Success
|
||||||
|
|
||||||
|
ApplyEquipmentVisual (Slot, Item)
|
||||||
|
→ Get SocketName from SlotDefinitions for this slot
|
||||||
|
→ If item has skeletal mesh → SpawnAttached SkeletalMeshComponent to socket
|
||||||
|
→ If item has static mesh → SpawnAttached StaticMeshComponent to socket
|
||||||
|
→ Store reference in VisualAttachments
|
||||||
|
|
||||||
|
RemoveEquipmentVisual (Slot)
|
||||||
|
→ If VisualAttachments contains slot → Destroy attached component
|
||||||
|
→ Remove from VisualAttachments map
|
||||||
|
|
||||||
|
RecalculateModifiers
|
||||||
|
→ Zero out all modifier fields
|
||||||
|
→ For each equipped item:
|
||||||
|
→ Read modifiers from DA_ItemData.EquipmentModifiers
|
||||||
|
→ Sum each field into ActiveModifiers
|
||||||
|
→ Broadcast OnEquipmentChanged
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Replication
|
||||||
|
|
||||||
|
| Variable | Replication | Callback |
|
||||||
|
|----------|-------------|----------|
|
||||||
|
| `EquippedItems` | `RepNotify` | `OnRep_EquippedItems` — rebuilds visuals on remote |
|
||||||
|
| `ActiveModifiers` | `Replicated` | — |
|
||||||
|
|
||||||
|
**Authority:** Server validates all equip/unequip operations. Client predicts input; server broadcasts authoritative state.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Dependencies & Communication
|
||||||
|
|
||||||
|
| System | Relationship |
|
||||||
|
|--------|--------------|
|
||||||
|
| `BPC_InventorySystem` | Source of item data; sets/reads `bIsEquipped` flag. |
|
||||||
|
| `BPC_ActiveItemSystem` | Automatically updates quick slots when equipment changes. |
|
||||||
|
| `BPC_HealthSystem` | Reads `ActiveModifiers.DefenseBonus` for damage calculations. |
|
||||||
|
| `BPC_StaminaSystem` | Reads `ActiveModifiers.StaminaDrainModifier`. |
|
||||||
|
| `BPC_MovementStateSystem` | Reads `ActiveModifiers.MoveSpeedModifier`. |
|
||||||
|
| `BPC_StressSystem` | Reads `ActiveModifiers.StressResistanceModifier`. |
|
||||||
|
| `BPC_CameraStateLayer` | May adjust camera based on head equipment (e.g., night vision overlay). |
|
||||||
|
| `BPC_InteractionDetector` | Reads `ActiveModifiers.SensoryRangeModifier` for detection range. |
|
||||||
|
| `BP_PlayerCharacter` (Skeletal Mesh) | Provides sockets for equipment visual attachment. |
|
||||||
|
| `Save/Load System` | Serializes `EquippedItems` for restore. |
|
||||||
|
| `UI (WBP_InventoryMenu)` | Subscribes to all dispatchers for visual updates. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Success Criteria
|
||||||
|
|
||||||
|
1. Item equip swaps into slot; old item is unequipped automatically.
|
||||||
|
2. Unequipped item stays in inventory, not lost.
|
||||||
|
3. Modifiers are correctly summed from all equipped items.
|
||||||
|
4. Visual meshes attach to the correct character sockets.
|
||||||
|
5. Equipping a helmet with night vision applies the camera overlay.
|
||||||
|
6. Carrying a backpack increases max carry weight.
|
||||||
|
7. Equipping armor reduces incoming damage.
|
||||||
|
8. Multiplayer: visual equipment and modifiers sync to remote clients.
|
||||||
|
9. Save/load restores equipped state.
|
||||||
|
10. Auto-equip on pickup works for configured item categories.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Data Flow Summary
|
||||||
|
|
||||||
|
```
|
||||||
|
Player equips armor vest from inventory
|
||||||
|
→ BPC_EquipmentSlotSystem.EquipItem (item ID, Torso slot)
|
||||||
|
→ Validate (not occupied? category matches?)
|
||||||
|
→ Set inventory entry bIsEquipped = true
|
||||||
|
→ Add to EquippedItems map
|
||||||
|
→ Attach vest skeletal mesh to Torso socket
|
||||||
|
→ RecalculateModifiers → DefenseBonus, MoveSpeed, etc.
|
||||||
|
→ Broadcast OnItemEquipped, OnEquipmentChanged
|
||||||
|
→ BPC_HealthSystem reads new DefenseBonus
|
||||||
|
→ BPC_MovementStateSystem reads new MoveSpeed
|
||||||
|
→ UI updates equipment panel
|
||||||
|
```
|
||||||
|
|
||||||
|
## 12. Reuse Notes
|
||||||
|
|
||||||
|
- Renamed from `BPC_EquipmentSystem` to `BPC_EquipmentSlotSystem` per Master naming convention.
|
||||||
|
- Cross-references updated: `BPC_InventoryComponent` → `BPC_InventorySystem`, `BPC_InventoryQuickSlot` → `BPC_ActiveItemSystem`, `BPC_InventoryWeightSystem` → `BPC_ActiveItemSystem`.
|
||||||
393
docs/blueprints/04-inventory/31_BPC_InventorySystem.md
Normal file
393
docs/blueprints/04-inventory/31_BPC_InventorySystem.md
Normal file
@@ -0,0 +1,393 @@
|
|||||||
|
# `BPC_InventorySystem` — Core Inventory System
|
||||||
|
|
||||||
|
**Parent Class:** `ActorComponent`
|
||||||
|
**Category:** Inventory
|
||||||
|
**Target UE Version:** 5.5–5.7
|
||||||
|
**Build Phase:** 3 — Inventory
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Overview
|
||||||
|
|
||||||
|
`BPC_InventorySystem` is the central inventory authority on the player. It manages an array of item instances (stackable, tagged, data-driven), handles add/remove/use/transfer operations, enforces capacity limits, and communicates state changes via Event Dispatchers. All other inventory subsystems (weight, quick slots, UI, equipment) read from this component as their single source of truth.
|
||||||
|
|
||||||
|
Items are stored as `S_InventoryEntry` structs referencing a `DA_ItemData` primary data asset plus runtime state (stack count, durability, custom metadata). Operations are validated by category, stack limits, and capacity before execution.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Mermaid — Inventory Architecture
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[External Request] --> B{BPC_InventorySystem}
|
||||||
|
B --> C{Operation Type}
|
||||||
|
C -->|AddItem| D[Validate: Has Space?]
|
||||||
|
C -->|RemoveItem| E[Validate: Has Item?]
|
||||||
|
C -->|UseItem| F[Validate: Usable?]
|
||||||
|
C -->|TransferItem| G[Validate: Target Has Space?]
|
||||||
|
C -->|DropItem| H[Spawn BP_ItemPickup in World]
|
||||||
|
|
||||||
|
D -->|Pass| I[Add to Array / Stack]
|
||||||
|
D -->|Fail| J[Return Failure Reason]
|
||||||
|
E -->|Pass| K[Remove from Array / Unstack]
|
||||||
|
E -->|Fail| L[Return Failure Reason]
|
||||||
|
F -->|Pass| M[Dispatch OnItemUsed]
|
||||||
|
F -->|Fail| N[Return Failure Reason]
|
||||||
|
|
||||||
|
I --> O[Broadcast OnInventoryChanged]
|
||||||
|
K --> O
|
||||||
|
M --> O
|
||||||
|
G --> P[Remove from Source / Add to Target]
|
||||||
|
P --> O
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Enums
|
||||||
|
|
||||||
|
### `E_InventoryOperationResult`
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Success` | Operation completed successfully. |
|
||||||
|
| `InventoryFull` | No free slots available. |
|
||||||
|
| `ItemNotFound` | Specified item not in inventory. |
|
||||||
|
| `StackLimitReached` | Item exists but stack is at maximum. |
|
||||||
|
| `InsufficientQuantity` | Not enough of the item to remove. |
|
||||||
|
| `ItemNotUsable` | Item cannot be used from inventory. |
|
||||||
|
| `CategoryRestricted` | Item category not allowed in this inventory. |
|
||||||
|
| `WeightCapacityExceeded` | Adding would exceed max weight. |
|
||||||
|
|
||||||
|
### `E_ItemCategory`
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Consumable` | Health items, food, drink, batteries. |
|
||||||
|
| `KeyItem` | Story keys, access cards, puzzle tokens. |
|
||||||
|
| `Weapon` | Firearms, melee weapons, tools. |
|
||||||
|
| `Ammo` | Bullets, shells, energy cells. |
|
||||||
|
| `Resource` | Crafting materials, scrap, components. |
|
||||||
|
| `Document` | Notes, letters, audio logs, data files. |
|
||||||
|
| `Equipment` | Wearable gear (armor, backpack, goggles). |
|
||||||
|
| `QuestItem` | Mission-specific objects. |
|
||||||
|
|
||||||
|
### `E_SortMode`
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `ByName` | Alphabetical by item name. |
|
||||||
|
| `ByCategory` | Grouped by item category. |
|
||||||
|
| `ByRarity` | Sorted by item rarity value. |
|
||||||
|
| `ByWeight` | Sorted by weight ascending. |
|
||||||
|
| `ByQuantity` | Sorted by stack count descending. |
|
||||||
|
| `ByRecent` | Most recently acquired first. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Structs
|
||||||
|
|
||||||
|
### `S_InventoryEntry`
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `ItemData` | `DA_ItemData*` | Reference to the item's data asset. |
|
||||||
|
| `ItemID` | `FGuid` | Unique runtime ID for this item instance. |
|
||||||
|
| `StackCount` | `int32` | Current number of this item in the stack. |
|
||||||
|
| `MaxStackSize` | `int32` | Max per slot (from DA, overrideable). |
|
||||||
|
| `Durability` | `float` | Current durability (0–1). |
|
||||||
|
| `CustomTags` | `FGameplayTagContainer` | Runtime-added tags for quest tracking. |
|
||||||
|
| `CustomData` | `TMap<FName, FString>` | Generic key-value store for per-instance data. |
|
||||||
|
| `SlotIndex` | `int32` | The slot this entry occupies. |
|
||||||
|
| `bIsEquipped` | `bool` | Whether this item is currently equipped. |
|
||||||
|
|
||||||
|
### `S_InventorySlot`
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `SlotIndex` | `int32` | Unique slot number within the inventory. |
|
||||||
|
| `Entry` | `S_InventoryEntry` | The item occupying this slot (empty = null ItemData). |
|
||||||
|
| `bIsLocked` | `bool` | If true, this slot cannot be modified. |
|
||||||
|
| `bIsQuickSlot` | `bool` | If true, this slot is mapped to a hotkey. |
|
||||||
|
| `CategoryRestriction` | `E_ItemCategory` | Only items of this category can occupy this slot. |
|
||||||
|
|
||||||
|
### `S_InventorySnapshot`
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Slots` | `TArray<S_InventorySlot>` | Copy of all inventory slots at snapshot time. |
|
||||||
|
| `TotalWeight` | `float` | Weight at snapshot time. |
|
||||||
|
| `TotalItems` | `int32` | Count of non-empty slots. |
|
||||||
|
| `SnapshotTimestamp` | `float` | Game time when snapshot was taken. |
|
||||||
|
|
||||||
|
### `S_ItemQuery`
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `CategoryFilter` | `E_ItemCategory` | Only return items of this category. |
|
||||||
|
| `TagFilter` | `FGameplayTag` | Only return items with this tag. |
|
||||||
|
| `NameFilter` | `FString` | Partial name match (case-insensitive). |
|
||||||
|
| `bIncludeEquipped` | `bool` | If false, exclude equipped items. |
|
||||||
|
| `bSortResult` | `bool` | If true, sort results by name. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Variables
|
||||||
|
|
||||||
|
### Configuration (Instance Editable, Expose On Spawn)
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `MaxSlots` | `int32` | Total number of inventory slots (default 24). |
|
||||||
|
| `MaxWeightCapacity` | `float` | Maximum carry weight in kg. |
|
||||||
|
| `bUseWeightSystem` | `bool` | If true, weight limits are enforced. |
|
||||||
|
| `AllowedCategories` | `TArray<E_ItemCategory>` | Categories permitted in this inventory. Empty = all. |
|
||||||
|
| `StartingItems` | `TArray<FStartingItemEntry>` | Items to add at BeginPlay. |
|
||||||
|
| `bReplicates` | `bool` | Whether inventory state is replicated. |
|
||||||
|
|
||||||
|
### `FStartingItemEntry`
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `ItemData` | `DA_ItemData*` | Item data asset to add. |
|
||||||
|
| `StackCount` | `int32` | Quantity to add. |
|
||||||
|
| `AutoEquip` | `bool` | If true, equip immediately if possible. |
|
||||||
|
|
||||||
|
### State (Blueprint Read Only)
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `Slots` | `TArray<S_InventorySlot>` | The actual inventory array. Replicated. |
|
||||||
|
| `TotalWeight` | `float` | Current total weight of all items. |
|
||||||
|
| `TotalItems` | `int32` | Current count of non-empty slots. |
|
||||||
|
| `bIsOverEncumbered` | `bool` | True when TotalWeight exceeds MaxWeightCapacity. |
|
||||||
|
|
||||||
|
### Internal (Not Replicated)
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `OwningPawn` | `APawn*` | Cached owning pawn reference. |
|
||||||
|
| `OnItemCooldowns` | `TMap<FGuid, float>` | Map of ItemID → remaining cooldown seconds. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Functions & Events
|
||||||
|
|
||||||
|
### Public Functions — Core Operations
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `AddItem` | Adds an item instance. Returns `E_InventoryOperationResult` and the slot index. Handles stacking if item already exists and stack not full. |
|
||||||
|
| `RemoveItem` | Removes `Quantity` of an item by `ItemID` or `SlotIndex`. Returns result and how many were actually removed. |
|
||||||
|
| `RemoveItemByTag` | Removes first matching item by `FGameplayTag`. Returns result. |
|
||||||
|
| `UseItem` | Uses/consumes item at slot. Returns result. Dispatches `OnItemUsed`. If consumable with limited uses, reduces stack. |
|
||||||
|
| `DropItem` | Drops `Quantity` of item at slot. Creates `BP_ItemPickup` in world. Returns result and spawned actor. |
|
||||||
|
| `TransferItem` | Moves item from this inventory to a target `BPC_InventorySystem`. Validates target capacity. |
|
||||||
|
| `MoveItemToSlot` | Moves item from one slot to another (rearrange). Handles swapping if target slot is occupied. |
|
||||||
|
| `SplitStack` | Splits a stack into two slots. Returns new slot index. |
|
||||||
|
| `CombineStack` | Combines two stacks of the same item into one (if max allows). |
|
||||||
|
|
||||||
|
### Public Functions — Query
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `HasItem` | Returns true if inventory contains an item with the given `ItemID` or matching `FGameplayTag`. |
|
||||||
|
| `HasItemQuantity` | Returns total count of items matching a tag or data asset. |
|
||||||
|
| `FindItemByTag` | Returns first `S_InventoryEntry` matching a gameplay tag. |
|
||||||
|
| `FindAllItemsByCategory` | Returns `TArray<S_InventoryEntry>` filtered by category. |
|
||||||
|
| `FindAllItemsByTag` | Returns `TArray<S_InventoryEntry>` filtered by gameplay tag. |
|
||||||
|
| `GetItemAtSlot` | Returns `S_InventoryEntry` at the given slot index (or empty if slot is free). |
|
||||||
|
| `GetSlotCount` | Returns total number of slots (empty + filled). |
|
||||||
|
| `GetFreeSlotCount` | Returns number of empty slots. |
|
||||||
|
| `IsInventoryFull` | Returns true if no free slots. |
|
||||||
|
| `SortInventory` | Reorders slots by the given `E_SortMode`. |
|
||||||
|
| `GetTotalWeight` | Returns current `TotalWeight`. |
|
||||||
|
| `IsOverEncumbered` | Returns `bIsOverEncumbered`. |
|
||||||
|
| `GetItemCooldownRemaining` | Returns seconds remaining for a given ItemID cooldown. |
|
||||||
|
|
||||||
|
### Protected Functions
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `BeginPlay` | Caches owner, adds starting items, initialises empty slots array. |
|
||||||
|
| `FindFreeSlot` | Returns the index of the first empty slot. Returns -1 if full. |
|
||||||
|
| `FindExistingStack` | Finds slot index with same ItemData that is below MaxStackSize. |
|
||||||
|
| `RecalculateWeight` | Iterates all slots and sums item weight. Updates `TotalWeight` and `bIsOverEncumbered`. |
|
||||||
|
| `ValidateAddItem` | Pre-checks slot availability, weight capacity, category restrictions. Returns result. |
|
||||||
|
| `ValidateRemoveItem` | Pre-checks item exists and has sufficient quantity. |
|
||||||
|
| `SetSlot` | Internal: writes an entry to a specific slot index. |
|
||||||
|
| `ClearSlot` | Internal: empties a slot. |
|
||||||
|
| `OnRep_Slots` | RepNotify: recalculates weight and broadcasts `OnInventoryChanged`. |
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Payload | Description |
|
||||||
|
|------------|---------|-------------|
|
||||||
|
| `OnInventoryChanged` | — | Fired on any add/remove/transfer/sort operation. |
|
||||||
|
| `OnItemAdded` | `S_InventoryEntry Item`, `int32 SlotIndex` | Fired after an item is successfully added. |
|
||||||
|
| `OnItemRemoved` | `S_InventoryEntry Item`, `int32 SlotIndex`, `int32 Quantity` | Fired after an item is removed. |
|
||||||
|
| `OnItemUsed` | `S_InventoryEntry Item`, `AActor* Instigator` | Fired when UseItem successfully executes. |
|
||||||
|
| `OnItemDropped` | `S_InventoryEntry Item`, `BP_ItemPickup* DroppedActor` | Fired when an item is dropped into the world. |
|
||||||
|
| `OnItemTransferred` | `S_InventoryEntry Item`, `BPC_InventorySystem* TargetInventory` | Fired after transfer to another inventory. |
|
||||||
|
| `OnStackSplit` | `S_InventoryEntry Original`, `S_InventoryEntry NewStack` | Fired when a stack is split into two. |
|
||||||
|
| `OnStackCombined` | `S_InventoryEntry CombinedStack` | Fired when two stacks are merged. |
|
||||||
|
| `OnOverEncumberedStateChanged` | `bool bIsOverEncumbered` | Fired when weight crosses the threshold. |
|
||||||
|
| `OnItemCooldownStarted` | `FGuid ItemID`, `float Duration` | Fired when an item cooldown begins. |
|
||||||
|
| `OnInventoryFull` | — | Fired when all slots are filled. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blueprint Graph Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
Event BeginPlay
|
||||||
|
→ Set MaxSlots (from config)
|
||||||
|
→ Create empty Slots array (Size = MaxSlots)
|
||||||
|
→ For each StartingItem:
|
||||||
|
→ Call AddItem (StartingItem.ItemData, StartingItem.StackCount)
|
||||||
|
→ If StartingItem.AutoEquip → Call EquipItem on slot (via EquipmentSlotSystem)
|
||||||
|
|
||||||
|
AddItem (ItemData, Quantity)
|
||||||
|
→ Call ValidateAddItem → If Fail → return result
|
||||||
|
→ FindExistingStack (same ItemData, not full)
|
||||||
|
→ Branch: Found?
|
||||||
|
→ Yes → Increment stack by Quantity (clamped to MaxStackSize)
|
||||||
|
→ If Quantity > remaining → FindFreeSlot for overflow
|
||||||
|
→ No → FindFreeSlot
|
||||||
|
→ If no free slot → return InventoryFull
|
||||||
|
→ Create new S_InventoryEntry → Assign SlotIndex
|
||||||
|
→ RecalculateWeight
|
||||||
|
→ If bReplicates → Mark slots array dirty
|
||||||
|
→ Broadcast OnItemAdded, OnInventoryChanged
|
||||||
|
→ Return Success
|
||||||
|
|
||||||
|
RemoveItem (SlotIndex, Quantity)
|
||||||
|
→ Call ValidateRemoveItem → If Fail → return result
|
||||||
|
→ Decrement stack count by Quantity
|
||||||
|
→ If stack count <= 0 → ClearSlot
|
||||||
|
→ RecalculateWeight
|
||||||
|
→ Broadcast OnItemRemoved, OnInventoryChanged
|
||||||
|
→ Return Success
|
||||||
|
|
||||||
|
UseItem (SlotIndex)
|
||||||
|
→ GetItemAtSlot → If empty → return ItemNotFound
|
||||||
|
→ Check cooldown → If on cooldown → return failure
|
||||||
|
→ Check bIsUsable from DA_ItemData → If not → return ItemNotUsable
|
||||||
|
→ If item has cooldown → Add to OnItemCooldowns map
|
||||||
|
→ Broadcast OnItemUsed
|
||||||
|
→ If item is consumable (bConsumeOnUse from DA):
|
||||||
|
→ Reduce stack by 1
|
||||||
|
→ If stack = 0 → ClearSlot
|
||||||
|
→ Broadcast OnInventoryChanged
|
||||||
|
→ Return Success
|
||||||
|
|
||||||
|
DropItem (SlotIndex, Quantity, SpawnLocation, SpawnRotation)
|
||||||
|
→ RemoveItem (SlotIndex, Quantity) → If fail → return
|
||||||
|
→ SpawnActor BP_ItemPickup at location/rotation
|
||||||
|
→ Set pickup's ItemData and StackCount
|
||||||
|
→ Broadcast OnItemDropped
|
||||||
|
→ Return Success + spawned actor
|
||||||
|
|
||||||
|
TransferItem (SlotIndex, TargetInventory, Quantity)
|
||||||
|
→ Validate item exists and quantity sufficient
|
||||||
|
→ Call TargetInventory.ValidateAddItem → If fail → return
|
||||||
|
→ RemoveItem (SlotIndex, Quantity)
|
||||||
|
→ TargetInventory.AddItem (ItemData, Quantity)
|
||||||
|
→ Broadcast OnItemTransferred
|
||||||
|
→ Return Success
|
||||||
|
|
||||||
|
SortInventory (SortMode)
|
||||||
|
→ Build TMap of slot index → entry for non-empty slots
|
||||||
|
→ Sort entries by SortMode rules
|
||||||
|
→ Clear all slots
|
||||||
|
→ Write sorted entries back into sequential slots
|
||||||
|
→ Broadcast OnInventoryChanged
|
||||||
|
|
||||||
|
RecalculateWeight
|
||||||
|
→ Sum = 0
|
||||||
|
→ For each slot with non-null ItemData:
|
||||||
|
→ Sum += ItemData.Weight * StackCount
|
||||||
|
→ TotalWeight = Sum
|
||||||
|
→ OldEncumbered = bIsOverEncumbered
|
||||||
|
→ bIsOverEncumbered = Sum > MaxWeightCapacity
|
||||||
|
→ If OldEncumbered != bIsOverEncumbered
|
||||||
|
→ Broadcast OnOverEncumberedStateChanged
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Replication
|
||||||
|
|
||||||
|
| Variable | Replication | Callback |
|
||||||
|
|----------|-------------|----------|
|
||||||
|
| `Slots` | `RepNotify` | `OnRep_Slots` — refreshes UI, recalculates weight |
|
||||||
|
| `TotalWeight` | `Replicated` | — |
|
||||||
|
| `bIsOverEncumbered` | `Replicated` | — |
|
||||||
|
| `TotalItems` | `Replicated` | — |
|
||||||
|
|
||||||
|
**Authority:** Server is authoritative for all add/remove/use operations. Clients predict UI updates but server validates and corrects.
|
||||||
|
|
||||||
|
**Cooldowns:** Managed locally (not replicated) but validated on server for authoritative actions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Dependencies & Communication
|
||||||
|
|
||||||
|
| System | Relationship |
|
||||||
|
|--------|--------------|
|
||||||
|
| `DA_ItemData` | Each slot references a Data Asset for item properties. |
|
||||||
|
| `BPC_ActiveItemSystem` | Listens to `OnInventoryChanged` and `OnOverEncumberedStateChanged`. |
|
||||||
|
| `WBP_InventoryMenu` | Subscribes to all event dispatchers to update UI. |
|
||||||
|
| `BPC_EquipmentSlotSystem` | Reads `bIsEquipped` flag; equips/unequips items via slot. |
|
||||||
|
| `BP_ItemPickup` | Receives dropped items; sends pickup requests to `AddItem`. |
|
||||||
|
| `BPC_InteractionDetector` | Routes pickup interaction to `AddItem`. |
|
||||||
|
| `BPC_HealthSystem` | Consumed health items via `UseItem` trigger healing. |
|
||||||
|
| `BPC_StaminaSystem` | Consumed stamina items via `UseItem` trigger stamina restore. |
|
||||||
|
| `Save/Load System` | Serializes `Slots` array for save/restore. |
|
||||||
|
| `BPC_PlayerMetricsTracker` | Logs item acquisition events. |
|
||||||
|
| `BPC_DifficultyManager` | May adjust `MaxSlots` or `MaxWeightCapacity` based on difficulty. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Success Criteria
|
||||||
|
|
||||||
|
1. Items can be added to inventory; duplicates stack up to `MaxStackSize`.
|
||||||
|
2. Adding beyond slot capacity returns `InventoryFull`.
|
||||||
|
3. Adding beyond weight capacity returns `WeightCapacityExceeded`.
|
||||||
|
4. Items can be removed by slot index or gameplay tag; stacks decrement correctly.
|
||||||
|
5. Using a consumable item reduces stack; item is removed at 0.
|
||||||
|
6. Dropping an item spawns `BP_ItemPickup` in the world with correct data.
|
||||||
|
7. Items can be transferred between inventories (player ↔ container).
|
||||||
|
8. Inventory can be sorted by all sort modes.
|
||||||
|
9. Stack splitting creates a new slot; combining merges stacks.
|
||||||
|
10. Event dispatchers fire correctly for all operations.
|
||||||
|
11. Multiplayer: inventory state is server-authoritative and syncs to clients.
|
||||||
|
12. Save/load restores exact inventory state including custom data.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Data Flow Summary
|
||||||
|
|
||||||
|
```
|
||||||
|
Player picks up item
|
||||||
|
→ BPC_InteractionDetector detects BP_ItemPickup
|
||||||
|
→ Player presses Interact
|
||||||
|
→ BP_ItemPickup.ExecuteInteraction → BPC_InventorySystem.AddItem
|
||||||
|
→ Validate (space, weight, category)
|
||||||
|
→ Add to array / increment stack
|
||||||
|
→ RecalculateWeight
|
||||||
|
→ Broadcast OnItemAdded / OnInventoryChanged
|
||||||
|
→ UI updates / Weight system reacts / Metrics logs
|
||||||
|
|
||||||
|
Player uses health item from inventory
|
||||||
|
→ UI calls BPC_InventorySystem.UseItem (SlotIndex)
|
||||||
|
→ Validate (exists, usable, cooldown)
|
||||||
|
→ Broadcast OnItemUsed
|
||||||
|
→ BPC_HealthSystem.OnItemUsed → ApplyHealing
|
||||||
|
→ If consumable → reduce stack
|
||||||
|
→ Broadcast OnInventoryChanged
|
||||||
|
```
|
||||||
|
|
||||||
|
## 12. Reuse Notes
|
||||||
|
|
||||||
|
- Renamed from `BPC_InventoryComponent` to `BPC_InventorySystem` per Master naming convention.
|
||||||
|
- Cross-references updated: `BPC_InventoryWeightSystem` → `BPC_ActiveItemSystem`, `BPC_InventoryQuickSlot` → `BPC_ActiveItemSystem`, `BPC_InventoryUIManager` → `WBP_InventoryMenu`, `BPC_EquipmentSystem` → `BPC_EquipmentSlotSystem`.
|
||||||
56
docs/blueprints/04-inventory/32_BPC_ItemCombineSystem.md
Normal file
56
docs/blueprints/04-inventory/32_BPC_ItemCombineSystem.md
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# BPC_ItemCombineSystem — Item Combine System
|
||||||
|
|
||||||
|
**Parent Class:** `ActorComponent`
|
||||||
|
**Category:** Inventory
|
||||||
|
**Target UE Version:** 5.5–5.7
|
||||||
|
**Build Phase:** 3 — Inventory
|
||||||
|
|
||||||
|
## 1. Overview
|
||||||
|
|
||||||
|
`BPC_ItemCombineSystem` handles item combination and crafting — combining two or more inventory items to create new items. Supports recipe-based combinations, ingredient validation, and result generation.
|
||||||
|
|
||||||
|
## 2. Enums
|
||||||
|
|
||||||
|
### `E_CombineResult`
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `Success` | Items combined successfully |
|
||||||
|
| `MissingIngredients` | Not all required items present |
|
||||||
|
| `IncompatibleItems` | Items cannot be combined |
|
||||||
|
| `InventoryFull` | No space for result item |
|
||||||
|
| `RecipeNotFound` | No recipe matches combination |
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `Recipes` | `TArray<DA_RecipeData*>` | Loaded recipe data assets |
|
||||||
|
| `CombineCooldown` | `float` | Time between combine operations |
|
||||||
|
| `bConsumeIngredients` | `bool` | Remove ingredients on combine |
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `CombineItems` | Attempts to combine two items by slot index |
|
||||||
|
| `GetValidRecipes` | Returns all recipes matching current inventory items |
|
||||||
|
| `PreviewResult` | Shows result item without consuming ingredients |
|
||||||
|
| `CanCombine` | Checks if two items have a valid recipe |
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Payload | Description |
|
||||||
|
|------------|---------|-------------|
|
||||||
|
| `OnCombineSuccess` | `S_InventoryEntry Result` | Items combined |
|
||||||
|
| `OnCombineFailed` | `E_CombineResult Reason` | Combination failed |
|
||||||
|
| `OnRecipeDiscovered` | `DA_RecipeData Recipe` | New recipe found |
|
||||||
|
|
||||||
|
## 6. Dependencies
|
||||||
|
|
||||||
|
| System | Relationship |
|
||||||
|
|--------|--------------|
|
||||||
|
| `BPC_InventorySystem` | Reads and modifies inventory items |
|
||||||
|
| `DA_RecipeData` | Recipe definitions |
|
||||||
|
|
||||||
|
## 7. Reuse Notes
|
||||||
|
- Data-driven recipe system; designers add recipes via data assets
|
||||||
46
docs/blueprints/04-inventory/33_BPC_JournalSystem.md
Normal file
46
docs/blueprints/04-inventory/33_BPC_JournalSystem.md
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# BPC_JournalSystem — Journal System
|
||||||
|
|
||||||
|
**Parent Class:** `ActorComponent`
|
||||||
|
**Category:** Inventory
|
||||||
|
**Target UE Version:** 5.5–5.7
|
||||||
|
**Build Phase:** 3 — Inventory
|
||||||
|
|
||||||
|
## 1. Overview
|
||||||
|
|
||||||
|
`BPC_JournalSystem` manages the player journal — tracking discovered entries, quest notes, character bios, and lore entries. Provides query functions for UI display and narrative progression tracking.
|
||||||
|
|
||||||
|
## 2. Variables
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `JournalEntries` | `TArray<S_JournalEntry>` | All journal entries |
|
||||||
|
| `EntryCategories` | `TArray<E_JournalCategory>` | Quest, Character, Lore, Notes, Tips |
|
||||||
|
| `UnreadCount` | `int32` | Total unread entries |
|
||||||
|
| `bAutoAddOnDiscovery` | `bool` | Auto-add entries on narrative flag |
|
||||||
|
|
||||||
|
## 3. Functions
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `AddJournalEntry` | Adds new entry |
|
||||||
|
| `MarkAsRead` | Marks entry read |
|
||||||
|
| `GetEntriesByCategory` | Returns filtered entries |
|
||||||
|
| `HasEntry` | Checks if entry exists |
|
||||||
|
| `GetUnreadCount` | Returns unread count |
|
||||||
|
|
||||||
|
## 4. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Payload | Description |
|
||||||
|
|------------|---------|-------------|
|
||||||
|
| `OnJournalEntryAdded` | `S_JournalEntry Entry` | New entry discovered |
|
||||||
|
| `OnJournalEntryRead` | `FGuid EntryID` | Entry marked read |
|
||||||
|
|
||||||
|
## 5. Dependencies
|
||||||
|
|
||||||
|
| System | Relationship |
|
||||||
|
|--------|--------------|
|
||||||
|
| `BPC_NarrativeStateSystem` | Narrative-driven journal entries |
|
||||||
|
| `WBP_JournalDocumentViewer` | UI display |
|
||||||
|
|
||||||
|
## 6. Reuse Notes
|
||||||
|
- Journal entries are narrative-gated via gameplay tags
|
||||||
49
docs/blueprints/04-inventory/34_BPC_KeyItemSystem.md
Normal file
49
docs/blueprints/04-inventory/34_BPC_KeyItemSystem.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# BPC_KeyItemSystem — Key Item System
|
||||||
|
|
||||||
|
**Parent Class:** `ActorComponent`
|
||||||
|
**Category:** Inventory
|
||||||
|
**Target UE Version:** 5.5–5.7
|
||||||
|
**Build Phase:** 3 — Inventory
|
||||||
|
|
||||||
|
## 1. Overview
|
||||||
|
|
||||||
|
`BPC_KeyItemSystem` manages key items — story-critical objects that unlock doors, trigger narrative events, or are required for puzzle completion. Tracks acquired keys, provides query for door/puzzle unlock checks, and supports key usage notifications.
|
||||||
|
|
||||||
|
## 2. Variables
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `AcquiredKeys` | `TArray<FGameplayTag>` | Tags of all acquired key items |
|
||||||
|
| `bShowKeyNotifications` | `bool` | Toast on key pickup |
|
||||||
|
| `KeyItemCategory` | `E_ItemCategory` | KeyItem category filter |
|
||||||
|
|
||||||
|
## 3. Functions
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `HasKey` | Checks if player has a specific key by tag |
|
||||||
|
| `AddKey` | Registers a key item acquisition |
|
||||||
|
| `RemoveKey` | Removes a key (if consumed) |
|
||||||
|
| `GetAllKeys` | Returns all acquired key tags |
|
||||||
|
| `CanUnlock` | Checks if player meets unlock requirements for a target |
|
||||||
|
|
||||||
|
## 4. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Payload | Description |
|
||||||
|
|------------|---------|-------------|
|
||||||
|
| `OnKeyAcquired` | `FGameplayTag KeyTag` | New key picked up |
|
||||||
|
| `OnKeyUsed` | `FGameplayTag KeyTag`, `AActor* Target` | Key used on target |
|
||||||
|
| `OnKeyRemoved` | `FGameplayTag KeyTag` | Key consumed/removed |
|
||||||
|
|
||||||
|
## 5. Dependencies
|
||||||
|
|
||||||
|
| System | Relationship |
|
||||||
|
|--------|--------------|
|
||||||
|
| `BPC_InventorySystem` | Keys as inventory items |
|
||||||
|
| `BP_DoorActor` | Door unlock queries |
|
||||||
|
| `BP_PuzzleDeviceActor` | Puzzle unlock gates |
|
||||||
|
| `BPC_NarrativeStateSystem` | Key-based narrative flags |
|
||||||
|
|
||||||
|
## 6. Reuse Notes
|
||||||
|
- Keys use gameplay tags for generic matching (any key with same tag works)
|
||||||
|
- Supports consumable keys (removed after single use) and persistent keys
|
||||||
216
docs/blueprints/05-saveload/35_SS_SaveManager.md
Normal file
216
docs/blueprints/05-saveload/35_SS_SaveManager.md
Normal 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`
|
||||||
103
docs/blueprints/05-saveload/36_I_Persistable.md
Normal file
103
docs/blueprints/05-saveload/36_I_Persistable.md
Normal 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
|
||||||
169
docs/blueprints/05-saveload/37_BP_Checkpoint.md
Normal file
169
docs/blueprints/05-saveload/37_BP_Checkpoint.md
Normal 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.
|
||||||
95
docs/blueprints/05-saveload/38_BPC_AltDeathSpaceSystem.md
Normal file
95
docs/blueprints/05-saveload/38_BPC_AltDeathSpaceSystem.md
Normal 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
|
||||||
170
docs/blueprints/05-saveload/39_BPC_DeathHandlingSystem.md
Normal file
170
docs/blueprints/05-saveload/39_BPC_DeathHandlingSystem.md
Normal 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()`
|
||||||
88
docs/blueprints/05-saveload/40_BPC_PersistentCorpseSystem.md
Normal file
88
docs/blueprints/05-saveload/40_BPC_PersistentCorpseSystem.md
Normal 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
|
||||||
@@ -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
|
||||||
97
docs/blueprints/05-saveload/42_BPC_PlayerRespawnSystem.md
Normal file
97
docs/blueprints/05-saveload/42_BPC_PlayerRespawnSystem.md
Normal 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
|
||||||
90
docs/blueprints/05-saveload/43_BPC_RunHistoryTracker.md
Normal file
90
docs/blueprints/05-saveload/43_BPC_RunHistoryTracker.md
Normal 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
|
||||||
116
docs/blueprints/06-ui/44_SS_UIManager.md
Normal file
116
docs/blueprints/06-ui/44_SS_UIManager.md
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
# SS_UIManager — Game Instance Subsystem
|
||||||
|
|
||||||
|
**Parent Class:** `UGameInstanceSubsystem`
|
||||||
|
**Dependencies:** [`GI_GameFramework`](../01-core/04_GI_GameFramework.md)
|
||||||
|
**File:** `SS_UIManager`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
The central UI orchestration subsystem. Owns all full-screen menu widgets (MainMenu, PauseMenu, Settings, Inventory, Journal) and manages their visibility, stacking, and transitions. In-world HUD widgets are owned by [`WBP_HUD`](33_WBP_HUD.md) directly.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Responsibilities
|
||||||
|
|
||||||
|
- Create and cache all full-screen menu widget instances on subsystem Init
|
||||||
|
- Manage a navigation stack (MenuStack) for push/pop/replace menu transitions
|
||||||
|
- React to `E_GamePhase` changes from [`GI_GameFramework`](../01-core/04_GI_GameFramework.md) to auto-show/hide menus
|
||||||
|
- Provide a static-style accessor pattern via [`FL_GameUtilities`](../01-core/02_FL_GameUtilities.md)`::GetUIManager`
|
||||||
|
- Route input mode changes (GameOnly → UIOnly → GameAndUI) when menus open/close
|
||||||
|
- Broadcast menu open/close events for other systems to react
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `MenuWidgets` | Map (`E_MenuType` → Widget) | Cached instances of all menu widgets |
|
||||||
|
| `MenuStack` | Array of `E_MenuType` | Navigation history for back-stack |
|
||||||
|
| `ActiveMenu` | `E_MenuType` | Currently visible full-screen menu (None = HUD only) |
|
||||||
|
| `bInputModeUI` | Bool | Are we in UI-only input mode |
|
||||||
|
| `HUDWidget` | `WBP_HUD` | Reference to root HUD (created separately) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Enums
|
||||||
|
|
||||||
|
### `E_MenuType`
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `None` | No menu active (game view) |
|
||||||
|
| `MainMenu` | Title screen |
|
||||||
|
| `PauseMenu` | In-game pause |
|
||||||
|
| `SettingsMenu` | Audio/Graphics/Controls/Accessibility |
|
||||||
|
| `InventoryMenu` | Full inventory screen |
|
||||||
|
| `JournalMenu` | Journal and documents viewer |
|
||||||
|
| `SaveLoadMenu` | Save/load slot selection |
|
||||||
|
| `LoadGameMenu` | Load from title screen |
|
||||||
|
| `DialogueMenu` | Dialogue choice UI |
|
||||||
|
| `ContainerMenu` | Container search/loot UI |
|
||||||
|
| `DeathScreen` | Game over / death screen |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Functions / Events
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `Initialize` | — | — | On subsystem init; creates all cached menu widgets (not visible) |
|
||||||
|
| `ShowMenu` | MenuType: `E_MenuType` | — | Shows a menu, pushes previous menu to stack, updates input mode |
|
||||||
|
| `HideMenu` | — | — | Hides current menu, pops stack, restores input mode |
|
||||||
|
| `PushMenu` | MenuType: `E_MenuType` | — | Pushes menu, stores current on stack (for sub-menus) |
|
||||||
|
| `PopMenu` | — | — | Returns to previous menu from stack |
|
||||||
|
| `ReplaceMenu` | MenuType: `E_MenuType` | — | Replaces current menu without stack history |
|
||||||
|
| `ClearToHUD` | — | — | Empties stack, hides all menus, shows HUD only |
|
||||||
|
| `IsMenuOpen` | MenuType | Bool | Checks if a specific menu is currently shown |
|
||||||
|
| `GetTopMenu` | — | `E_MenuType` | Returns current top of stack |
|
||||||
|
| `SetInputModeUI` | bUIOnly: Bool | — | Switches between GameOnly and GameAndUI input modes |
|
||||||
|
| `GetHUD` | — | `WBP_HUD` | Returns root HUD reference |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired When |
|
||||||
|
|------|-----------|------------|
|
||||||
|
| `OnMenuOpened` | MenuType: `E_MenuType` | Any menu becomes visible |
|
||||||
|
| `OnMenuClosed` | MenuType: `E_MenuType` | Any menu is hidden |
|
||||||
|
| `OnInputModeChanged` | bUIOnly: Bool | Input mode switches |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Blueprint Flow — ShowMenu Logic
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[ShowMenu Called] --> B{Menu already visible?}
|
||||||
|
B -->|Yes| C[Do nothing / return]
|
||||||
|
B -->|No| D[Push current ActiveMenu to MenuStack]
|
||||||
|
D --> E[Set ActiveMenu = NewMenuType]
|
||||||
|
E --> F[Create widget if not cached]
|
||||||
|
F --> G[Add to viewport]
|
||||||
|
G --> H[Set input mode to UIOnly]
|
||||||
|
H --> I[Broadcast OnMenuOpened]
|
||||||
|
I --> J[Pause game if MenuType is pause-capable?]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|--------------|--------|-----|
|
||||||
|
| [`GI_GameFramework`](../01-core/04_GI_GameFramework.md) | Dispatcher (`OnGamePhaseChanged`) | Auto-hide menus during cutscenes/loading |
|
||||||
|
| [`WBP_HUD`](33_WBP_HUD.md) | Direct reference | Controlled hide/show alongside menus |
|
||||||
|
| [`PC_CoreController`](../02-player/) | Direct | Input mode changes routed through controller |
|
||||||
|
| All menu widgets | Direct (cached references) | Show/hide/set data |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Reuse Notes
|
||||||
|
|
||||||
|
This subsystem is project-agnostic. Add new `E_MenuType` values per project. The widget caching pattern prevents repeated construction. The stack-based navigation handles any menu depth.
|
||||||
141
docs/blueprints/06-ui/45_WBP_AccessibilityUI.md
Normal file
141
docs/blueprints/06-ui/45_WBP_AccessibilityUI.md
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
# WBP_AccessibilityUI — Widget (Subtitle Display, Accessibility Overlays)
|
||||||
|
|
||||||
|
**Parent Class:** `UUserWidget` (created via Widget Blueprint)
|
||||||
|
**Dependencies:** `BPC_DialoguePlaybackSystem`, `BPC_NarrativeStateSystem`, `SS_SettingsSystem`
|
||||||
|
**File:** `WBP_AccessibilityUI`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Provides subtitle rendering, speaking-name display, subtitle background styling, and accessibility-focused UI overlays (high-contrast mode, colorblind filters, font scaling). Listens to dialogue events from `BPC_DialoguePlaybackSystem` and settings changes from `SS_SettingsSystem`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Responsibilities
|
||||||
|
|
||||||
|
- Display subtitles at the bottom-center of the screen with speaker name, dialogue text, and optional background
|
||||||
|
- Support multi-line subtitle display with timed progression
|
||||||
|
- Display speaker name above the subtitle text (optional per settings)
|
||||||
|
- React to accessibility settings: font size scaling, background opacity, colorblind mode, high-contrast mode
|
||||||
|
- Fade subtitles in/out on line start/end
|
||||||
|
- Queue subtitle lines if multiple fire rapidly
|
||||||
|
- Handle subtitle grouping: one line at a time per speaker, multi-speaker stacking
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `SubtitleContainer` | `VerticalBox` | Vertical stack for active subtitle lines |
|
||||||
|
| `SpeakerNameText` | `TextBlock` | Current speaker's display name |
|
||||||
|
| `SubtitleText` | `TextBlock` | Current subtitle line text |
|
||||||
|
| `SubtitleBackground` | `Image` or `Border` | Background behind subtitle text |
|
||||||
|
| `AnimSubtitleIn` | `WidgetAnimation` | Fade in subtitle |
|
||||||
|
| `AnimSubtitleOut` | `WidgetAnimation` | Fade out subtitle |
|
||||||
|
| `ActiveSubtitles` | Array of `FSubtitleLine` | Currently displayed subtitle lines |
|
||||||
|
| `SubtitleQueue` | Array of `FSubtitleLine` | Queue if multiple lines arrive simultaneously |
|
||||||
|
| `bSubtitlesEnabled` | Bool | From settings |
|
||||||
|
| `SubtitleFontScale` | Float | 0.8, 1.0, 1.2, 1.5 from settings |
|
||||||
|
| `SubtitleBackgroundOpacity` | Float | 0.0 to 1.0 from settings |
|
||||||
|
| `bHighContrastMode` | Bool | From settings |
|
||||||
|
| `bIsCurrentlySpeaking` | Bool | Prevents flicker between rapid lines |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Structs
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
FSubtitleLine
|
||||||
|
{
|
||||||
|
FText SpeakerName; // "Dr. Hartmann"
|
||||||
|
FText LineText; // "The tumor is benign..."
|
||||||
|
float Duration; // seconds, from dialogue data
|
||||||
|
float StartTime; // world time when line started
|
||||||
|
bool bIsFinalLine; // if true, marks end of dialogue sequence
|
||||||
|
FColor SpeakerColor; // optional per-speaker color
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Functions / Events
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `Construct` | — | — | Bind to dialogue dispatchers, load accessibility settings |
|
||||||
|
| `OnDialogueLineStarted` | Data: `FSubtitleLine` | — | Add subtitle to display, start fade-in timer |
|
||||||
|
| `OnDialogueLineComplete` | Data: `FSubtitleLine` | — | Start fade-out, remove from active list after animation |
|
||||||
|
| `OnDialogueSequenceEnded` | — | — | Clear all subtitles, fade out |
|
||||||
|
| `DisplaySubtitle` | Line: `FSubtitleLine` | — | Set speaker name, text, background; play AnimSubtitleIn |
|
||||||
|
| `HideSubtitle` | Line: `FSubtitleLine` | — | Play AnimSubtitleOut, remove from container |
|
||||||
|
| `SetSubtitlesEnabled` | bEnabled: Bool | — | Show/hide subtitle container |
|
||||||
|
| `ApplyFontScale` | Scale: Float | — | Set subtitle text font size (scalar) |
|
||||||
|
| `SetBackgroundOpacity` | Opacity: Float | — | Set subtitle background render opacity |
|
||||||
|
| `ApplyHighContrastMode` | bEnabled: Bool | — | Toggle high-contrast subtitle background/text colors |
|
||||||
|
| `OnAccessibilitySettingsChanged` | Settings: `S_AccessibilitySettings` | — | Apply all accessibility settings at once |
|
||||||
|
| `ClearAllSubtitles` | — | — | Remove all active subtitles immediately |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Blueprint Flow — Subtitle Lifecycle
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Dialogue as BPC_DialoguePlaybackSystem
|
||||||
|
participant Sub as WBP_AccessibilityUI
|
||||||
|
participant Timer as Internal Timer
|
||||||
|
|
||||||
|
Dialogue->>Sub: OnDialogueLineStarted LineData
|
||||||
|
Sub->>Sub: DisplaySubtitle LineData
|
||||||
|
Sub->>Sub: Set SpeakerNameText, SubtitleText
|
||||||
|
Sub->>Sub: PlayAnimation FadeIn
|
||||||
|
Sub->>Timer: SetTimer by LineData.Duration
|
||||||
|
Timer-->>Sub: Timer expired
|
||||||
|
Sub->>Sub: PlayAnimation FadeOut
|
||||||
|
Sub->>Sub: OnDialogueLineComplete LineData
|
||||||
|
Dialogue->>Sub: OnDialogueSequenceEnded
|
||||||
|
Sub->>Sub: ClearAllSubtitles
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Accessibility Settings Consumption
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
A[SS_SettingsSystem] -->|Read on Construct| B[Cache settings]
|
||||||
|
B --> C[SetSubtitlesEnabled]
|
||||||
|
B --> D[ApplyFontScale]
|
||||||
|
B --> E[SetBackgroundOpacity]
|
||||||
|
B --> F[ApplyHighContrastMode]
|
||||||
|
A -->|OnAccessibilitySettingsChanged dispatcher| G[Re-apply all]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired When |
|
||||||
|
|------|-----------|------------|
|
||||||
|
| `OnSubtitleStarted` | Line: `FSubtitleLine` | A new subtitle line appears |
|
||||||
|
| `OnSubtitleEnded` | Line: `FSubtitleLine` | A subtitle line ends |
|
||||||
|
| `OnAllSubtitlesCleared` | — | All subtitles removed |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|--------------|--------|-----|
|
||||||
|
| [`BPC_DialoguePlaybackSystem`](../07-narrative/) | Dispatchers (`OnLineStarted`, `OnLineComplete`, `OnSequenceEnded`) | Receive subtitle data |
|
||||||
|
| [`SS_SettingsSystem`] | Direct reads, dispatcher on settings changed | Apply accessibility preferences |
|
||||||
|
| [`WBP_HUD`](33_WBP_HUD.md) | Parent reference | Positioning within HUD canvas |
|
||||||
|
| [`BPC_NarrativeStateSystem`] | Query | Speaker display name overrides |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Reuse Notes
|
||||||
|
|
||||||
|
The subtitle display widget is purely reactive — it never fetches or filters dialogue data. It can be embedded in any HUD canvas (first-person, third-person, diegetic screen overlay). The `FSubtitleLine` struct is intentionally compatible with the `DA_DialogueSequence` line format. If no speaker name is provided, the `SpeakerNameText` collapses and the subtitle text fills the full width. High-contrast mode inverts colors for readability against any background.
|
||||||
89
docs/blueprints/06-ui/46_WBP_DiegeticHUDFrame.md
Normal file
89
docs/blueprints/06-ui/46_WBP_DiegeticHUDFrame.md
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
# WBP_DiegeticHUDFrame — Widget (Diegetic HUD Skin Container)
|
||||||
|
|
||||||
|
**File:** [`Content/Framework/UI/WBP_DiegeticHUDFrame`](Content/Framework/UI/WBP_DiegeticHUDFrame.uasset)
|
||||||
|
**Parent Class:** `UUserWidget`
|
||||||
|
**Dependencies:** [`I_DiegeticDisplay`](../01-core/03_I_InterfaceLibrary.md), [`WBP_HUDController`](WBP_HUDController.md), all player state component dispatchers
|
||||||
|
|
||||||
|
**Purpose:** The swappable container for the game's diegetic HUD presentation. This is the "skin" that changes per project — wristwatch, visor, bracelet, handheld device, etc. All data displayed here comes from system dispatchers; it contains zero game logic.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `HealthIndicator` | Widget | Child widget showing health (bar, number, pulse) |
|
||||||
|
| `StressIndicator` | Widget | Child widget showing stress tier |
|
||||||
|
| `StaminaBar` | Widget | Optional stamina display |
|
||||||
|
| `CompassWidget` | Widget | Optional directional compass |
|
||||||
|
| `ActiveItemWidget` | Widget | Currently held item icon and name |
|
||||||
|
| `TimeDisplay` | TextBlock | In-game world time or session timer |
|
||||||
|
| `ObjectivePreview` | TextBlock | Single-line current objective snippet |
|
||||||
|
| `bAnimatedEntries` | Bool | Whether values animate in/out with lerp |
|
||||||
|
| `PresentationSkin` | E_DiegeticSkin | Current visual skin (watch, visor, handheld) |
|
||||||
|
|
||||||
|
## Enums
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
E_DiegeticSkin
|
||||||
|
{
|
||||||
|
Wristwatch,
|
||||||
|
VisorHUDSuit,
|
||||||
|
HandheldDevice,
|
||||||
|
BraceletCharm,
|
||||||
|
DiegeticSignage,
|
||||||
|
CustomProjectSkin
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Functions / Events
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | What it does |
|
||||||
|
|------|--------|---------|--------------|
|
||||||
|
| `SetDiegeticValue` | Tag: GameplayTag, Value: Float/Text | — | Implements `I_DiegeticDisplay`. Routes to correct child widget |
|
||||||
|
| `RefreshDisplay` | — | — | Implements `I_DiegeticDisplay`. Force-refreshes all children |
|
||||||
|
| `SetSkin` | Skin: E_DiegeticSkin | — | Swaps visual presentation without changing data bindings |
|
||||||
|
| `SetAnimatedEntries` | bAnimated: Bool | — | Toggle value animation lerp |
|
||||||
|
| `UpdateHealth` | NormalisedValue: Float | — | Bound to BPC_HealthSystem dispatcher |
|
||||||
|
| `UpdateStress` | Tier: E_StressTier, Value: Float | — | Bound to BPC_StressSystem dispatcher |
|
||||||
|
| `UpdateStamina` | NormalisedValue: Float | — | Bound to BPC_StaminaSystem dispatcher |
|
||||||
|
| `UpdateActiveItem` | ItemTag: GameplayTag, DisplayName: FText | — | Bound to BPC_ActiveItemSystem dispatcher |
|
||||||
|
| `UpdateObjective` | ObjectiveText: FText | — | Bound to BPC_ObjectiveSystem dispatcher |
|
||||||
|
| `ShowHUD` | — | — | Animate all elements in |
|
||||||
|
| `HideHUD` | — | — | Animate all elements out |
|
||||||
|
|
||||||
|
## Blueprint Flow Diagram
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[WBP_HUDController creates WBP_DiegeticHUDFrame] --> B[bAnimatedEntries = Settings value]
|
||||||
|
B --> C[Bind to player component dispatchers]
|
||||||
|
C --> D[Wait for game phase = InGame]
|
||||||
|
D --> E[ShowHUD - animate all elements in]
|
||||||
|
|
||||||
|
F[Health changed] --> G[UpdateHealth - lerp HealthIndicator]
|
||||||
|
H[Stress changed] --> I[UpdateStress - update StressIndicator]
|
||||||
|
J[Item equipped] --> K[UpdateActiveItem - show icon + name]
|
||||||
|
|
||||||
|
L[Game phase changes to Cutscene] --> M[HideHUD]
|
||||||
|
N[Game phase returns to InGame] --> E
|
||||||
|
```
|
||||||
|
|
||||||
|
## Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|---------------|--------|-----|
|
||||||
|
| `WBP_HUDController` | Parent reference | Visibility control via game phase |
|
||||||
|
| `BPC_HealthSystem` | Dispatcher `OnHealthChanged` | Health display |
|
||||||
|
| `BPC_StressSystem` | Dispatcher `OnStressTierChanged` | Stress display |
|
||||||
|
| `BPC_StaminaSystem` | Dispatcher `OnStaminaChanged` | Stamina display |
|
||||||
|
| `BPC_ActiveItemSystem` | Dispatcher | Active hand item display |
|
||||||
|
| `BPC_ObjectiveSystem` | Dispatcher `OnObjectiveActivated` | Objective preview |
|
||||||
|
| `SS_SettingsSystem` | Direct read | Animated entries preference |
|
||||||
|
|
||||||
|
## Reuse Notes
|
||||||
|
|
||||||
|
- For a sci-fi game, create `WBP_DiegeticHUD_VisorSkin`. For period horror, create `WBP_DiegeticHUD_WatchSkin`. Only the art/layout changes — all data bindings use the same `I_DiegeticDisplay` interface.
|
||||||
|
- The `E_DiegeticSkin` enum can be extended per project without modifying this widget's logic.
|
||||||
|
- This widget implements `I_DiegeticDisplay` so external systems can push data without knowing which skin is active.
|
||||||
|
- All child widgets are created in `NativeConstruct` and bound to dispatchers there.
|
||||||
114
docs/blueprints/06-ui/47_WBP_HUDController.md
Normal file
114
docs/blueprints/06-ui/47_WBP_HUDController.md
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
# WBP_HUDController — Widget (Root HUD)
|
||||||
|
|
||||||
|
**Parent Class:** `UUserWidget` (created via HUD Blueprint)
|
||||||
|
**Dependencies:** `SS_UIManager`, `BPC_InteractionDetector`, `BPC_HealthSystem`, `BPC_StressSystem`, `BPC_StaminaSystem`, `BPC_NarrativeStateSystem`, `BPC_ObjectiveSystem`, `BPC_DialoguePlaybackSystem`
|
||||||
|
**File:** `WBP_HUDController`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
The master HUD container. Owns all in-world HUD sub-widgets and manages their visibility based on game phase and player state. Does NOT own full-screen menus — those are managed by `SS_UIManager`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Responsibilities
|
||||||
|
|
||||||
|
- Create and manage all HUD sub-widgets (diegetic display, interaction prompt, subtitles, notifications, objective overlay, screen effects)
|
||||||
|
- React to `E_GamePhase` changes (hide HUD during cutscenes, show during play)
|
||||||
|
- Route visibility of individual HUD elements via gameplay tags
|
||||||
|
- Never display raw data — always delegates to child widgets
|
||||||
|
- Handle fade-to-black transitions for death/loading
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `DiegeticHUDWidget` | `WBP_DiegeticHUDFrame` | The diegetic UI element (watch/visor/etc) |
|
||||||
|
| `InteractionPromptWidget` | `WBP_InteractionPromptDisplay` | Interaction cues |
|
||||||
|
| `SubtitleWidget` | `WBP_SubtitleDisplay` | Dialogue/event subtitles |
|
||||||
|
| `NotificationWidget` | `WBP_NotificationToast` | Toast messages |
|
||||||
|
| `ObjectiveWidget` | `WBP_ObjectiveDisplay` | Objective overlay |
|
||||||
|
| `ScreenEffectWidget` | `WBP_ScreenEffectController` | Full-screen effects |
|
||||||
|
| `MinimapWidget` | Widget | Optional minimap/compass |
|
||||||
|
| `bHUDVisible` | Bool | Master visibility flag |
|
||||||
|
| `FadeWidget` | Image | Black full-screen image for fades |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sub-Widget Class References
|
||||||
|
|
||||||
|
| Variable Name | Default Class |
|
||||||
|
|--------------|---------------|
|
||||||
|
| `DiegeticHUDClass` | `WBP_DiegeticHUDFrame` (subclass per project) |
|
||||||
|
| `InteractionPromptClass` | `WBP_InteractionPromptDisplay` |
|
||||||
|
| `SubtitleClass` | `WBP_SubtitleDisplay` |
|
||||||
|
| `NotificationClass` | `WBP_NotificationToast` |
|
||||||
|
| `ObjectiveClass` | `WBP_ObjectiveDisplay` |
|
||||||
|
| `ScreenEffectClass` | `WBP_ScreenEffectController` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Functions / Events
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `ConstructChildren` | — | — | Spawns all sub-widgets, adds as children |
|
||||||
|
| `SetHUDVisibility` | bVisible: Bool | — | Master show/hide (for cutscenes) |
|
||||||
|
| `FadeToBlack` | Duration: Float | — | Lerps FadeWidget opacity to 1 |
|
||||||
|
| `FadeFromBlack` | Duration: Float | — | Lerps FadeWidget opacity to 0 |
|
||||||
|
| `TriggerDamageVignette` | Intensity: Float | — | Calls ScreenEffectWidget.TriggerDamageFlash |
|
||||||
|
| `TriggerJumpScareFlash` | — | — | Calls ScreenEffectWidget.TriggerJumpScareFlash |
|
||||||
|
| `SetStressDistortion` | Amount: Float | — | Routes to ScreenEffectWidget |
|
||||||
|
| `OnPhaseChanged` | NewPhase: `E_GamePhase` | — | Listen from GI_GameFramework; hide/show HUD |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired When |
|
||||||
|
|------|-----------|------------|
|
||||||
|
| `OnHUDHidden` | — | HUD hidden (cutscene/loading) |
|
||||||
|
| `OnHUDShown` | — | HUD restored |
|
||||||
|
| `OnFadeComplete` | bFadedIn: Bool | Fade animation completes |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Blueprint Flow — Phase Change Reaction
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[OnPhaseChanged received] --> B{NewPhase}
|
||||||
|
B -->|InGame| C[SetHUDVisibility true]
|
||||||
|
B -->|Cutscene| D[SetHUDVisibility false]
|
||||||
|
B -->|Loading| E[FadeToBlack 0.5s]
|
||||||
|
B -->|DeathLoop| F[FadeToBlack 1.0s]
|
||||||
|
B -->|AltDeathSpace| G[Hide interaction prompt only]
|
||||||
|
B -->|Paused| H[Keep HUD but hide diegetic]
|
||||||
|
B -->|MainMenu / Credits| I[Destroy HUD entirely]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|--------------|--------|-----|
|
||||||
|
| [`SS_UIManager`](44_SS_UIManager.md) | Direct reference | Coordination with menus |
|
||||||
|
| [`GI_GameFramework`](../01-core/04_GI_GameFramework.md) | Dispatcher (`OnGamePhaseChanged`) | Phase-based visibility |
|
||||||
|
| [`BPC_InteractionDetector`](../03-interaction/16_BPC_InteractionDetector.md) | Dispatcher (`OnFocusGained`, `OnFocusLost`) | Show/hide interaction prompt |
|
||||||
|
| [`BPC_DialoguePlaybackSystem`](../07-narrative/) | Dispatcher (`OnLineStarted`, `OnLineComplete`) | Subtitle routing |
|
||||||
|
| [`BPC_ObjectiveSystem`](../07-narrative/) | Dispatcher (`OnObjectiveActivated`, `OnObjectiveCompleted`) | Objective overlay updates |
|
||||||
|
| [`BPC_HealthSystem`](../02-player/08_BPC_HealthSystem.md) | Dispatcher (`OnHealthChanged`, `OnHealthCritical`) | Damage vignette |
|
||||||
|
| [`BPC_StressSystem`](../02-player/10_BPC_StressSystem.md) | Dispatcher (`OnStressTierChanged`) | Stress distortion |
|
||||||
|
| [`BPC_NotificationSystem`] | Dispatcher | Toast messages |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Reuse Notes
|
||||||
|
|
||||||
|
The HUD class references are configurable so each project can override `DiegeticHUDClass` with a themed skin (watch, visor, bracelet, etc.) without modifying any logic. The fade system is used by death handling, chapter transitions, and loading screens interchangeably.
|
||||||
|
|
||||||
|
- Renamed from `WBP_HUD` to `WBP_HUDController` per Master naming convention.
|
||||||
135
docs/blueprints/06-ui/48_WBP_InteractionPromptDisplay.md
Normal file
135
docs/blueprints/06-ui/48_WBP_InteractionPromptDisplay.md
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
# WBP_InteractionPromptDisplay — Widget (Interaction Prompt Display)
|
||||||
|
|
||||||
|
**Parent Class:** `UUserWidget` (created via Widget Blueprint)
|
||||||
|
**Dependencies:** `BPC_InteractionDetector`, `I_Interactable`
|
||||||
|
**File:** `WBP_InteractionPromptDisplay`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Displays a context-sensitive interaction prompt at the center of the screen (or offset near the interactable world-location). Shows the interaction action name, hold-to-interact progress bar, and platform-specific input key icon.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Responsibilities
|
||||||
|
|
||||||
|
- Listen to `BPC_InteractionDetector.OnFocusGained` and `OnFocusLost` dispatchers
|
||||||
|
- When focus gained: fade in prompt text ("Press E to Open", "Hold F to Pull Lever")
|
||||||
|
- When hold interaction required: display a circular progress fill ring around the prompt
|
||||||
|
- When focus lost: fade out prompt immediately or animate out
|
||||||
|
- Display _context tags_ from the interactable (e.g. [Safe], [Dangerous], [Locked])
|
||||||
|
- Avoid overlapping with other HUD elements — offset from world-location if `bUseWorldOffset` is true
|
||||||
|
- Support a "look-at" mode (interaction prompt tracks the world location of the interactable)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `OwningDetector` | `BPC_InteractionDetector` | Cached reference (set on Construct) |
|
||||||
|
| `CurrentInteractable` | `I_Interactable` | Object currently in focus |
|
||||||
|
| `PromptText` | `TextBlock` | "Open Door", "Pick Up Note", etc. |
|
||||||
|
| `ActionKeyIcon` | `Image` | Platform-specific key icon |
|
||||||
|
| `HoldProgressRing` | `Image` | Circular fill ring for hold interactions |
|
||||||
|
| `ContextTagsContainer` | `HorizontalBox` | Row of context tag labels |
|
||||||
|
| `bIsVisible` | Bool | Current visibility state |
|
||||||
|
| `bUseWorldOffset` | Bool | If true, offset prompt to world location |
|
||||||
|
| `AnimFadeIn` | `WidgetAnimation` | Fade in animation |
|
||||||
|
| `AnimFadeOut` | `WidgetAnimation` | Fade out animation |
|
||||||
|
| `AnimHoldProgress` | `WidgetAnimation` | Hold fill progress animation |
|
||||||
|
| `PromptYOffset` | Float | Vertical offset from screen center (pixels) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Functions / Events
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `Construct` | — | — | Cache detector, bind dispatchers, set to hidden |
|
||||||
|
| `OnInteractionFocusGained` | Interactable: `I_Interactable`, InteractionInfo: `S_InteractionInfo` | — | Update prompt text/icon, play fade in |
|
||||||
|
| `OnInteractionFocusLost` | Interactable: `I_Interactable` | — | Play fade out, clear state |
|
||||||
|
| `UpdatePrompt` | Info: `S_InteractionInfo` | — | Set PromptText, ActionKeyIcon, hold duration |
|
||||||
|
| `SetHoldProgress` | Progress: Float (0-1) | — | Update HoldProgressRing fill amount |
|
||||||
|
| `OnHoldStarted` | Duration: Float | — | Play hold progress animation over Duration |
|
||||||
|
| `OnHoldCancelled` | — | — | Stop hold animation, reset ring to 0 |
|
||||||
|
| `OnHoldCompleted` | — | — | Ring fills to 1 at end of hold |
|
||||||
|
| `SetContextTags` | Tags: Array of Text | — | Add context tag labels to ContextTagsContainer |
|
||||||
|
| `UpdateScreenPosition` | — | — | Tick: if bUseWorldOffset, project world location to screen |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Structs
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Consumed from BPC_InteractionDetector
|
||||||
|
S_InteractionInfo
|
||||||
|
{
|
||||||
|
FText ActionLabel; // "Open", "Pick Up", etc
|
||||||
|
FText ObjectName; // "Wooden Door", "Old Note"
|
||||||
|
bool bRequiresHold;
|
||||||
|
float HoldDuration; // seconds
|
||||||
|
UTexture2D* ActionKeyIcon; // platform-specific input icon
|
||||||
|
TArray<FText> ContextTags; // "Safe", "Locked", "Hold"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Blueprint Flow — Focus Gained
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Player as Player
|
||||||
|
participant Detector as BPC_InteractionDetector
|
||||||
|
participant UI as WBP_InteractionPromptDisplay
|
||||||
|
participant Interactable as I_Interactable
|
||||||
|
|
||||||
|
Player->>Detector: Look at interactable
|
||||||
|
Detector->>Interactable: GetInteractionInfo()
|
||||||
|
Interactable-->>Detector: S_InteractionInfo
|
||||||
|
Detector-->>UI: OnFocusGained Interactable, Info
|
||||||
|
UI->>UI: UpdatePrompt Info
|
||||||
|
UI->>UI: SetVisibility Visible
|
||||||
|
UI->>UI: PlayAnimation FadeIn
|
||||||
|
Note over UI: Hold required?
|
||||||
|
alt Hold interaction
|
||||||
|
UI->>UI: OnHoldStarted Duration
|
||||||
|
loop Each tick
|
||||||
|
UI->>UI: SetHoldProgress elapsed/Duration
|
||||||
|
end
|
||||||
|
UI->>UI: OnHoldCompleted
|
||||||
|
else Tap interaction
|
||||||
|
UI->>UI: OnHoldCompleted instantly
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired When |
|
||||||
|
|------|-----------|------------|
|
||||||
|
| `OnPromptShown` | — | Prompt becomes visible |
|
||||||
|
| `OnPromptHidden` | — | Prompt becomes hidden |
|
||||||
|
| `OnHoldComplete` | — | Hold-fill reaches 100% |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|--------------|--------|-----|
|
||||||
|
| [`BPC_InteractionDetector`](../03-interaction/16_BPC_InteractionDetector.md) | Dispatchers | Receive focus gain/loss, hold progress |
|
||||||
|
| [`WBP_HUDController`](WBP_HUDController.md) | Parent reference | Positioning coordination |
|
||||||
|
| [`I_Interactable`](../01-core/03_I_InterfaceLibrary.md) | Function call | GetInteractionInfo for prompt data |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Reuse Notes
|
||||||
|
|
||||||
|
The prompt rendering pattern is used in both first-person and third-person contexts. The `bUseWorldOffset` flag lets this widget serve both diegetic HUD skins (watch screen show prompts) and traditional screen-center prompt systems. Context tags are optional — if the array is empty, the container collapses.
|
||||||
|
|
||||||
|
- Renamed from `WBP_InteractionUI` to `WBP_InteractionPromptDisplay` per Master naming convention.
|
||||||
|
- Cross-references updated: `WBP_HUD` → `WBP_HUDController`.
|
||||||
132
docs/blueprints/06-ui/49_WBP_InventoryMenu.md
Normal file
132
docs/blueprints/06-ui/49_WBP_InventoryMenu.md
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
# WBP_InventoryMenu — Widget (Inventory Screen)
|
||||||
|
|
||||||
|
**Parent Class:** `UUserWidget` (created via Widget Blueprint)
|
||||||
|
**Dependencies:** `BPC_InventorySystem`, `SS_UIManager`, `BPC_ActiveItemSystem`, `BPC_EquipmentSlotSystem`, `DA_ItemData`
|
||||||
|
**File:** `WBP_InventoryMenu`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Full-screen inventory grid view. Supports standard grid-list display, diegetic overlay mode, and radial quick-slot menu. Reads from `BPC_InventorySystem` but never writes — all mutations go through the component.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Responsibilities
|
||||||
|
|
||||||
|
- Display inventory slots in a scrollable grid (3 columns, N rows)
|
||||||
|
- Show item details in a side panel when an item is selected
|
||||||
|
- Support drag-and-drop between slots, between slots and equipment slots, and between slots and quick slots
|
||||||
|
- Support right-click / gamepad context menu (Use, Equip, Drop, Examine, Combine)
|
||||||
|
- Display item tooltip on hover
|
||||||
|
- Filter/sort by category via tabs (All, Documents, Tools, Keys, Consumables, Quest Items)
|
||||||
|
- Display weight bar at bottom (filled fractional, color-coded per weight tier)
|
||||||
|
- Support `E_InventoryViewMode` switching (Grid, Diegetic Overlay, Radial Quick)
|
||||||
|
- Close on B/Cancel or when `OnMenuClosed` fires from `SS_UIManager`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Enums
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Defined within the widget or a shared parent
|
||||||
|
E_InventoryViewMode
|
||||||
|
{
|
||||||
|
Grid,
|
||||||
|
DiegeticOverlay,
|
||||||
|
RadialQuick
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `OwningInventory` | `BPC_InventorySystem` | Cached reference (set on Construct) |
|
||||||
|
| `ViewMode` | `E_InventoryViewMode` | Current display mode |
|
||||||
|
| `SelectedSlotIndex` | Int | Currently highlighted slot (-1 = none) |
|
||||||
|
| `SelectedItemData` | `DA_ItemData` | Item currently shown in detail panel |
|
||||||
|
| `GridPanel` | `UniformGridPanel` | Main slot grid |
|
||||||
|
| `DetailPanel` | `WBP_ItemDetailPanel` | Right-side detail panel |
|
||||||
|
| `WeightBar` | `ProgressBar` | Bottom weight bar |
|
||||||
|
| `WeightText` | `TextBlock` | "12.5 / 50.0 kg" |
|
||||||
|
| `TabButtons` | Array of `Button` | All, Documents, Tools, Keys, etc. |
|
||||||
|
| `ActiveFilter` | `E_ItemCategory` | Current filter |
|
||||||
|
| `ContextMenu` | `WBP_ContextMenu` | Right-click popup |
|
||||||
|
| `RadialMenu` | `WBP_RadialMenu` | Radial quick-slot selector (if RadialQuick mode) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Functions / Events
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `Construct` | — | — | Cache inventory, bind dispatchers, rebuild grid |
|
||||||
|
| `RebuildGrid` | Filter: `E_ItemCategory` | — | Clear grid, add slot widgets for matching items |
|
||||||
|
| `OnSlotClicked` | SlotIndex: Int | — | Set SelectedSlotIndex, update detail panel |
|
||||||
|
| `ShowContextMenu` | SlotIndex: Int | — | Open context menu at mouse position |
|
||||||
|
| `ContextMenuAction` | Action: `E_ItemAction` | — | Route to UseItem, EquipItem, DropItem, etc. |
|
||||||
|
| `UseItem` | SlotIndex: Int | — | Call inventory.UseItem(SlotIndex) |
|
||||||
|
| `EquipItem` | SlotIndex: Int | — | Call inventory.EquipFromSlot(SlotIndex) |
|
||||||
|
| `DropItem` | SlotIndex: Int | — | Call inventory.DropItem(SlotIndex) |
|
||||||
|
| `CombineItems` | SourceIndex: Int, TargetIndex: Int | — | Call inventory.CombineItems(Source, Target) |
|
||||||
|
| `ExamineItem` | SlotIndex: Int | — | Request narrative system examine description |
|
||||||
|
| `SwitchViewMode` | NewMode: `E_InventoryViewMode` | — | Transition between Grid/Diegetic/Radial |
|
||||||
|
| `UpdateWeightBar` | CurrentWeight: Float, MaxWeight: Float | — | Set bar percent, color per tier, update text |
|
||||||
|
| `OnInventoryChanged` | SlotIndex: Int | — | Called via dispatcher; rebuild affected row |
|
||||||
|
| `HandleNavigation` | Direction: `E_NavDirection` | — | Keyboard/gamepad slot navigation |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired When |
|
||||||
|
|------|-----------|------------|
|
||||||
|
| `OnItemSelected` | ItemData: `DA_ItemData` | A slot is clicked |
|
||||||
|
| `OnItemUsed` | SlotIndex: Int | An item is used |
|
||||||
|
| `OnItemEquipped` | SlotIndex: Int | An item is equipped |
|
||||||
|
| `OnItemDropped` | SlotIndex: Int | An item is dropped from inventory |
|
||||||
|
| `OnCombineRequested` | SourceIndex: Int, TargetIndex: Int | Combine action initiated |
|
||||||
|
| `OnViewModeChanged` | NewMode: `E_InventoryViewMode` | View mode transitions |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Blueprint Flow — Grid Population
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[Construct widget] --> B[Cache BPC_InventorySystem]
|
||||||
|
B --> C[Bind OnInventoryChanged dispatcher]
|
||||||
|
C --> D[RebuildGrid current filter]
|
||||||
|
D --> E[Loop items in inventory]
|
||||||
|
E --> F{Matches filter?}
|
||||||
|
F -->|Yes| G[Create WBP_InventorySlot]
|
||||||
|
G --> H[Add to UniformGridPanel column row]
|
||||||
|
F -->|No| I[Skip]
|
||||||
|
H --> J[Bind slot.OnClicked]
|
||||||
|
J --> K[Update WeightBar]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|--------------|--------|-----|
|
||||||
|
| [`BPC_InventorySystem`](../04-inventory/BPC_InventorySystem.md) | Direct reference, dispatchers | Read items, execute mutations |
|
||||||
|
| [`SS_UIManager`](44_SS_UIManager.md) | `OpenMenu` / `CloseMenu` | Menu lifecycle |
|
||||||
|
| [`BPC_ActiveItemSystem`](../04-inventory/BPC_ActiveItemSystem.md) | Direct calls | Assign/clear quick slots from radial menu |
|
||||||
|
| [`BPC_EquipmentSlotSystem`](../04-inventory/BPC_EquipmentSlotSystem.md) | Dispatcher (`OnEquipmentChanged`) | Update equipped indicator |
|
||||||
|
| [`BPC_InteractionDetector`](../03-interaction/16_BPC_InteractionDetector.md) | Dispatcher | Drop item creates pickup actor |
|
||||||
|
| [`BPC_NarrativeStateSystem`] | Function | Examine item triggers narrative |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Reuse Notes
|
||||||
|
|
||||||
|
The grid population loop is the same pattern used by container inventories and shop UIs — the only difference is the source `BPC_InventorySystem` reference. Consider creating a shared `WBP_SlotGrid` base if containers need the same logic. The Diegetic Overlay and Radial Quick modes are purely visual transformations of the same slot data.
|
||||||
|
|
||||||
|
- Renamed from `WBP_InventoryUI` to `WBP_InventoryMenu` per Master naming convention.
|
||||||
|
- Cross-references updated: `BPC_InventoryComponent` → `BPC_InventorySystem`, `BPC_InventoryQuickSlot` → `BPC_ActiveItemSystem`, `BPC_EquipmentSystem` → `BPC_EquipmentSlotSystem`.
|
||||||
106
docs/blueprints/06-ui/50_WBP_JournalDocumentViewer.md
Normal file
106
docs/blueprints/06-ui/50_WBP_JournalDocumentViewer.md
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
# WBP_JournalDocumentViewer — Widget (Document and Journal Viewer)
|
||||||
|
|
||||||
|
**File:** [`Content/Framework/UI/WBP_JournalDocumentViewer`](Content/Framework/UI/WBP_JournalDocumentViewer.uasset)
|
||||||
|
**Parent Class:** `UUserWidget`
|
||||||
|
**Dependencies:** [`BPC_DocumentArchiveSystem`](../04-inventory/BPC_DocumentArchiveSystem.md), [`BPC_JournalSystem`](../04-inventory/BPC_JournalSystem.md)
|
||||||
|
|
||||||
|
**Purpose:** Displays collected documents, notes, and the player journal. Supports text scrolling, page turns, and image plates. Read-only presentation — never modifies game state.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `ActiveDocument` | S_DocumentEntry | Currently displayed document |
|
||||||
|
| `DocumentList` | Array of S_DocumentEntry | All collected documents for sidebar navigation |
|
||||||
|
| `ActiveJournalEntry` | S_JournalEntry | Currently displayed journal entry |
|
||||||
|
| `JournalEntryList` | Array of S_JournalEntry | All journal entries for sidebar navigation |
|
||||||
|
| `bShowPageTurnAnim` | Bool | Enable page turn animation |
|
||||||
|
| `FontStyle` | E_DocumentFontStyle | Handwritten, Typewritten, Digital |
|
||||||
|
| `ViewMode` | E_DocumentViewMode | Document, Journal, or Combined |
|
||||||
|
| `DocumentTitleText` | TextBlock | Active document title |
|
||||||
|
| `DocumentBodyText` | RichTextBlock | Scrollable document body |
|
||||||
|
| `DocumentImage` | Image | Optional plate/figure image |
|
||||||
|
| `SidebarList` | ListView | Navigation sidebar |
|
||||||
|
|
||||||
|
## Enums
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
E_DocumentFontStyle
|
||||||
|
{
|
||||||
|
Handwritten,
|
||||||
|
Typewritten,
|
||||||
|
Digital,
|
||||||
|
BloodScrawled
|
||||||
|
}
|
||||||
|
|
||||||
|
E_DocumentViewMode
|
||||||
|
{
|
||||||
|
DocumentsOnly,
|
||||||
|
JournalOnly,
|
||||||
|
CombinedTimeline
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Functions / Events
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | What it does |
|
||||||
|
|------|--------|---------|--------------|
|
||||||
|
| `OpenDocument` | DocumentTag: GameplayTag | — | Loads document from archive, displays with font style |
|
||||||
|
| `CloseDocument` | — | — | Returns to document list |
|
||||||
|
| `OpenJournal` | EntryTag: GameplayTag | — | Loads journal entry, displays |
|
||||||
|
| `NextDocument` | — | — | Selects next document in list |
|
||||||
|
| `PreviousDocument` | — | — | Selects previous document in list |
|
||||||
|
| `NextPage` | — | — | Scrolls body text to next page |
|
||||||
|
| `PreviousPage` | — | — | Scrolls body text to previous page |
|
||||||
|
| `SetViewMode` | Mode: E_DocumentViewMode | — | Switches between document/journal/combined |
|
||||||
|
| `MarkAsRead` | DocumentTag: GameplayTag | — | Sets bIsRead on document entry |
|
||||||
|
| `RefreshList` | — | — | Rebuilds sidebar from archive and journal systems |
|
||||||
|
| `SortByChapter` | — | — | Sorts documents/entries by discovery chapter |
|
||||||
|
| `SortByType` | — | — | Sorts documents by type, entries by date |
|
||||||
|
|
||||||
|
## Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired when |
|
||||||
|
|------|-----------|-----------|
|
||||||
|
| `OnDocumentOpened` | DocumentTag: GameplayTag | Player opens a document |
|
||||||
|
| `OnDocumentClosed` | — | Player closes document viewer |
|
||||||
|
| `OnDocumentRead` | DocumentTag: GameplayTag | Document marked as read |
|
||||||
|
|
||||||
|
## Blueprint Flow Diagram
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[Player opens Journal/Documents] --> B[ViewMode = CombinedTimeline]
|
||||||
|
B --> C[RefreshList from BPC_DocumentArchiveSystem + BPC_JournalSystem]
|
||||||
|
C --> D[Populate SidebarList]
|
||||||
|
D --> E[Wait for selection]
|
||||||
|
|
||||||
|
E --> F{Item selected?}
|
||||||
|
F -->|Document| G[OpenDocument with DocumentTag]
|
||||||
|
F -->|Journal Entry| H[OpenJournal with EntryTag]
|
||||||
|
|
||||||
|
G --> I[Set ActiveDocument = entry from archive]
|
||||||
|
I --> J[Display title, body text, optional image]
|
||||||
|
I --> K[MarkAsRead]
|
||||||
|
|
||||||
|
H --> L[Set ActiveJournalEntry]
|
||||||
|
L --> M[Display heading, body, timestamp]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|---------------|--------|-----|
|
||||||
|
| `BPC_DocumentArchiveSystem` | Direct read | Get all found documents |
|
||||||
|
| `BPC_JournalSystem` | Direct read | Get all journal entries |
|
||||||
|
| `WBP_InventoryMenu` | Parent navigation | Open from inventory tab |
|
||||||
|
| `SS_UIManager` | Push/Pop | Menu stack management |
|
||||||
|
|
||||||
|
## Reuse Notes
|
||||||
|
|
||||||
|
- The `E_DocumentFontStyle` enum lets each project set the visual tone per document type
|
||||||
|
- `ViewMode` can be restricted to DocumentsOnly for games without a journal
|
||||||
|
- The page-turn anim is optional; disable for performance or minimalist UI
|
||||||
|
- RichTextBlock supports markup — use for emphasis, different speaker colours, or lore highlight
|
||||||
47
docs/blueprints/06-ui/51_WBP_MainMenu.md
Normal file
47
docs/blueprints/06-ui/51_WBP_MainMenu.md
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# WBP_MainMenu — Widget (Main Menu)
|
||||||
|
|
||||||
|
**Parent:** `UUserWidget`
|
||||||
|
**Used by:** `SS_UIManager.OpenMenu("MainMenu")`
|
||||||
|
**Depends On:** `SS_UIManager`, `SS_SaveManager`, `GI_GameFramework`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Title screen with New Game, Continue, Load, Settings, Credits, Quit.
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `bContinueAvailable` | Bool | Set by SaveManager check on Construct |
|
||||||
|
| `AnimatedTitle` | `Image` or `TextBlock` | Animated game title |
|
||||||
|
| `ContinueButton` | `Button` | Highlights if save exists |
|
||||||
|
| `NewGameButton` | `Button` | Starts new game |
|
||||||
|
| `LoadGameButton` | `Button` | Opens load slot list |
|
||||||
|
| `SettingsButton` | `Button` | Opens settings menu |
|
||||||
|
| `CreditsButton` | `Button` | Opens credits |
|
||||||
|
| `QuitButton` | `Button` | Quit confirmation |
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `OnConstruct` | — | — | Check SS_SaveManager.HasAnySave for continue availability |
|
||||||
|
| `OnNewGame` | — | — | Call SS_SaveManager.ResetGameState, then OpenLevel "FirstLevel" |
|
||||||
|
| `OnContinue` | — | — | Call SS_SaveManager.LoadLatestSave, then OpenLevel from save header |
|
||||||
|
| `OnLoadGame` | — | — | Pushes load slot selection UI via SS_UIManager |
|
||||||
|
| `OnSettings` | — | — | SS_UIManager.PushMenu "SettingsMenu" |
|
||||||
|
| `OnCredits` | — | — | SS_UIManager.PushMenu "Credits" |
|
||||||
|
| `OnQuit` | — | — | Show quit confirmation dialog |
|
||||||
|
| `OnConfirmQuit` | — | — | `UKismetSystemLibrary.QuitGame` |
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|--------------|--------|-----|
|
||||||
|
| [`SS_UIManager`](44_SS_UIManager.md) | Push/Pop/Close/Open | Menu lifecycle |
|
||||||
|
| [`SS_SaveManager`](../05-saveload/28_SS_SaveManager.md) | Save/Load functions | Save slots |
|
||||||
|
| [`GI_GameFramework`](../01-core/04_GI_GameFramework.md) | SetGamePhase, dispatchers | Phase transitions |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
- Split from original bundled `36_WBP_MenuWidgets.md` per Clean Slate refactoring plan
|
||||||
83
docs/blueprints/06-ui/52_WBP_MenuFlowController.md
Normal file
83
docs/blueprints/06-ui/52_WBP_MenuFlowController.md
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
# WBP_MenuFlowController — Widget (Menu Flow Controller)
|
||||||
|
|
||||||
|
**Parent:** `UUserWidget` (non-visible overlay that lives under SS_UIManager)
|
||||||
|
**Depends On:** `SS_UIManager`, `GI_GameFramework`, `SS_SaveManager`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Orchestrates transitions between MenuFlow states: SplashScreen > MainMenu > Settings transitions > Loading transitions > Level transitions > Credits > Quit.
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `CurrentFlowState` | `E_MenuFlowState` | Current flow state |
|
||||||
|
| `FadeWidget` | `Image` | Black full-screen fade image |
|
||||||
|
| `FadeDuration` | Float | Fade duration in seconds (1.0) |
|
||||||
|
| `AnimFadeIn` | `WidgetAnimation` | Fade from 0 to 1 opacity |
|
||||||
|
| `AnimFadeOut` | `WidgetAnimation` | Fade from 1 to 0 opacity |
|
||||||
|
| `bIsTransitioning` | Bool | Block input during transition |
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
E_MenuFlowState
|
||||||
|
{
|
||||||
|
SplashScreen,
|
||||||
|
MainMenu,
|
||||||
|
Settings,
|
||||||
|
Loading,
|
||||||
|
InGame,
|
||||||
|
Paused,
|
||||||
|
Credits,
|
||||||
|
Quit
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `TransitionToState` | NewState: `E_MenuFlowState` | — | Fade out, switch state, fade in |
|
||||||
|
| `OnSplashComplete` | — | — | Auto-transition to MainMenu after splash timer |
|
||||||
|
| `OnFadeOutComplete` | — | — | Switch visibility, load levels, then fade in |
|
||||||
|
| `OpenMainMenu` | — | — | OpenLevel "MainMenu", then TransitionToState MainMenu |
|
||||||
|
| `StartNewGame` | — | — | Fade out, OpenLevel first level, set InGame |
|
||||||
|
| `ReturnToMainMenu` | — | — | OpenLevel "MainMenu", TransitionToState MainMenu |
|
||||||
|
| `ShowCredits` | — | — | OpenLevel "CreditsMap", TransitionToState Credits |
|
||||||
|
| `IsTransitioning` | — | Bool | Query lock |
|
||||||
|
|
||||||
|
### Blueprint Flow — Menu Flow Transitions
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
A[Splash] -->|timer| B[MainMenu]
|
||||||
|
B -->|New Game| C[Fade Out]
|
||||||
|
C --> D[OpenLevel FirstLevel]
|
||||||
|
D --> E[Fade In]
|
||||||
|
E --> F[InGame]
|
||||||
|
B -->|Settings| G[Push Settings Menu]
|
||||||
|
G -->|Back| B
|
||||||
|
F -->|ESC| H[Push Pause Menu]
|
||||||
|
H -->|Resume| F
|
||||||
|
H -->|Quit to Menu| I[Fade Out]
|
||||||
|
I --> J[OpenLevel MainMenu]
|
||||||
|
J --> B
|
||||||
|
F -->|Death| K[Fade to Black]
|
||||||
|
K --> L[Respawn or AltDeathSpace]
|
||||||
|
F -->|Credits| M[OpenLevel Credits]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|--------------|--------|-----|
|
||||||
|
| [`SS_UIManager`](44_SS_UIManager.md) | Push/Pop/Close/Open | Menu lifecycle |
|
||||||
|
| [`GI_GameFramework`](../01-core/04_GI_GameFramework.md) | SetGamePhase, dispatchers | Phase transitions |
|
||||||
|
| [`SS_SaveManager`](../05-saveload/28_SS_SaveManager.md) | Save/Load functions | Save slots |
|
||||||
|
| [`WBP_HUDController`](WBP_HUDController.md) | Parent reference | Fade coordination |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
- Manages transitions centrally — no individual menu needs to open levels or handle fade logic
|
||||||
|
- Split from original bundled `36_WBP_MenuWidgets.md` per Clean Slate refactoring plan
|
||||||
52
docs/blueprints/06-ui/53_WBP_NotificationToast.md
Normal file
52
docs/blueprints/06-ui/53_WBP_NotificationToast.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# WBP_NotificationToast — Widget (Toast Notification)
|
||||||
|
|
||||||
|
**Parent:** `UUserWidget`
|
||||||
|
**Used by:** Any system via dispatcher
|
||||||
|
**Depends On:** `WBP_HUDController`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Non-blocking toast notification that slides in from top/left edge, displays for a duration, then slides out.
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `ToastText` | `TextBlock` | Notification body |
|
||||||
|
| `ToastIcon` | `Image` | Optional icon |
|
||||||
|
| `ToastDuration` | Float | Seconds before auto-hide (2-5) |
|
||||||
|
| `AnimSlideIn` | `WidgetAnimation` | Slide in |
|
||||||
|
| `AnimSlideOut` | `WidgetAnimation` | Slide out |
|
||||||
|
| `ToastQueue` | Array of `FToastData` | Queued notifications if one is active |
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
FToastData
|
||||||
|
{
|
||||||
|
FText Message;
|
||||||
|
UTexture2D* Icon;
|
||||||
|
float Duration;
|
||||||
|
EToastPriority Priority; // Low, Normal, High
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `ShowToast` | Data: `FToastData` | — | If not playing, start; else queue |
|
||||||
|
| `PlaySlideIn` | — | — | Play AnimSlideIn, set timer for duration |
|
||||||
|
| `OnToastFinished` | — | — | Play AnimSlideOut, dequeue next |
|
||||||
|
| `DismissAll` | — | — | Clear queue, force hide |
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|--------------|--------|-----|
|
||||||
|
| [`WBP_HUDController`](WBP_HUDController.md) | Parent reference / Dispatcher | Toast display routing |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
- Uses a queue system — any system can fire a toast by calling the appropriate dispatcher on WBP_HUDController
|
||||||
|
- Split from original bundled `36_WBP_MenuWidgets.md` per Clean Slate refactoring plan
|
||||||
86
docs/blueprints/06-ui/54_WBP_ObjectiveDisplay.md
Normal file
86
docs/blueprints/06-ui/54_WBP_ObjectiveDisplay.md
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
# WBP_ObjectiveDisplay — Widget (Objective Display)
|
||||||
|
|
||||||
|
**Parent:** `UUserWidget`
|
||||||
|
**Used by:** `WBP_HUDController`, `BPC_ObjectiveSystem`
|
||||||
|
**Depends On:** `BPC_ObjectiveSystem`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Displays current objectives as an overlay on the HUD. Shows active objective text, objective markers, and progress indicators. Updates in real-time as objectives are added, completed, or updated.
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `ObjectiveText` | `TextBlock` | Current objective description |
|
||||||
|
| `ObjectiveMarker` | `Image` | Directional indicator if objective is off-screen |
|
||||||
|
| `ProgressBar` | `ProgressBar` | Objective progress (0-1) if applicable |
|
||||||
|
| `AnimFadeIn` | `WidgetAnimation` | Fade in on objective change |
|
||||||
|
| `AnimFadeOut` | `WidgetAnimation` | Fade out when objective completes |
|
||||||
|
| `AnimObjectiveComplete` | `WidgetAnimation` | Completion flourish |
|
||||||
|
| `ObjectiveQueue` | Array of `S_ObjectiveData` | Queued objectives for sequential display |
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
S_ObjectiveData
|
||||||
|
{
|
||||||
|
FText ObjectiveText;
|
||||||
|
FGameplayTag ObjectiveTag;
|
||||||
|
float Progress;
|
||||||
|
bool bIsComplete;
|
||||||
|
FVector WorldLocation; // For off-screen marker
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `OnConstruct` | — | — | Bind to BPC_ObjectiveSystem dispatchers |
|
||||||
|
| `OnObjectiveActivated` | Objective: `S_ObjectiveData` | — | Show objective with fade-in animation |
|
||||||
|
| `OnObjectiveUpdated` | Objective: `S_ObjectiveData` | — | Update progress bar and text |
|
||||||
|
| `OnObjectiveCompleted` | ObjectiveTag: GameplayTag | — | Play completion animation, then fade out |
|
||||||
|
| `UpdateOffScreenMarker` | WorldLocation: FVector | — | Calculate screen position, rotate marker |
|
||||||
|
| `ClearAllObjectives` | — | — | Clear all active objective displays |
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired When |
|
||||||
|
|------|-----------|------------|
|
||||||
|
| `OnObjectiveDisplayed` | ObjectiveText: FText | New objective shown |
|
||||||
|
| `OnObjectiveCleared` | — | All objectives cleared |
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|--------------|--------|-----|
|
||||||
|
| [`BPC_ObjectiveSystem`](../07-narrative/39_BPC_ObjectiveSystem.md) | Dispatcher | Receive objective changes |
|
||||||
|
| [`WBP_HUDController`](WBP_HUDController.md) | Parent reference | Coordinate visibility with other HUD elements |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[OnObjectiveActivated]
|
||||||
|
└─► Set ObjectiveText = Objective.ObjectiveText
|
||||||
|
└─► Set ProgressBar visibility based on objective type
|
||||||
|
└─► If objective has world location → Show ObjectiveMarker
|
||||||
|
└─► Play AnimFadeIn
|
||||||
|
└─► Broadcast OnObjectiveDisplayed
|
||||||
|
|
||||||
|
[OnObjectiveCompleted]
|
||||||
|
└─► Play AnimObjectiveComplete
|
||||||
|
└─► Delay 2.0 seconds
|
||||||
|
└─► Play AnimFadeOut
|
||||||
|
└─► Show next queued objective if any
|
||||||
|
|
||||||
|
[UpdateOffScreenMarker]
|
||||||
|
└─► Project world location to screen
|
||||||
|
└─► If behind camera → place at screen edge, rotate indicator
|
||||||
|
└─► Update ObjectiveMarker position + rotation
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
- Created as part of Clean Slate refactoring (Master Section 6.6)
|
||||||
|
- Follows TEMPLATE.md format for all new blueprint specs
|
||||||
59
docs/blueprints/06-ui/55_WBP_PauseMenu.md
Normal file
59
docs/blueprints/06-ui/55_WBP_PauseMenu.md
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# WBP_PauseMenu — Widget (Pause Menu)
|
||||||
|
|
||||||
|
**Parent:** `UUserWidget`
|
||||||
|
**Used by:** `SS_UIManager.PushMenu("PauseMenu")`
|
||||||
|
**Depends On:** `SS_UIManager`, `SS_SaveManager`, `GI_GameFramework`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
In-game pause overlay with Resume, Save, Load, Settings, Return to Main Menu.
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `ResumeButton` | `Button` | Closes menu, resumes game |
|
||||||
|
| `SaveButton` | `Button` | Manual save |
|
||||||
|
| `LoadButton` | `Button` | Load a different save |
|
||||||
|
| `SettingsButton` | `Button` | Opens settings |
|
||||||
|
| `QuitToMenuButton` | `Button` | Confirm quit to main menu |
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `OnResume` | — | — | SS_UIManager.CloseTopMenu, set game phase back to InGame |
|
||||||
|
| `OnSave` | — | — | SS_SaveManager.CreateManualSave, show notification |
|
||||||
|
| `OnLoad` | — | — | Push load slot selection via SS_UIManager |
|
||||||
|
| `OnSettings` | — | — | SS_UIManager.PushMenu "SettingsMenu" |
|
||||||
|
| `OnQuitToMenu` | — | — | Show confirmation, confirm calls OpenMainMenu |
|
||||||
|
|
||||||
|
### Blueprint Flow — Pause Menu Open
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Player as Player
|
||||||
|
participant UIMgr as SS_UIManager
|
||||||
|
participant GM as GI_GameFramework
|
||||||
|
participant HUD as WBP_HUDController
|
||||||
|
|
||||||
|
Player->>UIMgr: Press ESC
|
||||||
|
UIMgr->>GM: SetGamePhase Paused
|
||||||
|
GM->>HUD: OnPhaseChanged Paused
|
||||||
|
HUD->>HUD: Hide diegetic elements
|
||||||
|
UIMgr->>UIMgr: PushMenu PauseMenu
|
||||||
|
Note over UIMgr: Show mouse cursor
|
||||||
|
Note over UIMgr: Input mode = UI Only
|
||||||
|
```
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|--------------|--------|-----|
|
||||||
|
| [`SS_UIManager`](44_SS_UIManager.md) | Push/Pop/Close/Open | Menu lifecycle |
|
||||||
|
| [`GI_GameFramework`](../01-core/04_GI_GameFramework.md) | SetGamePhase, dispatchers | Phase transitions |
|
||||||
|
| [`SS_SaveManager`](../05-saveload/28_SS_SaveManager.md) | Save/Load functions | Save slots |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
- Split from original bundled `36_WBP_MenuWidgets.md` per Clean Slate refactoring plan
|
||||||
43
docs/blueprints/06-ui/56_WBP_ScreenEffectController.md
Normal file
43
docs/blueprints/06-ui/56_WBP_ScreenEffectController.md
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# WBP_ScreenEffectController — Widget (Screen Effects)
|
||||||
|
|
||||||
|
**Parent:** `UUserWidget`
|
||||||
|
**Used by:** `WBP_HUDController`, `BPC_HealthSystem`, `BPC_StressSystem`
|
||||||
|
**Depends On:** `WBP_HUDController`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Full-screen overlay for damage vignettes, jump scare flashes, stress distortion, and post-process material effects.
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `DamageVignetteImage` | `Image` | Red vignette overlay |
|
||||||
|
| `FlashImage` | `Image` | White flash for jump scares |
|
||||||
|
| `StressDistortionMaterial` | `MaterialInstanceDynamic` | Screen distortion MID |
|
||||||
|
| `DamageAnim` | `WidgetAnimation` | Fade in/out damage vignette |
|
||||||
|
| `JumpScareAnim` | `WidgetAnimation` | Quick white flash + fade |
|
||||||
|
| `StressAnim` | `WidgetAnimation` | Distortion intensity pulse |
|
||||||
|
| `bIsFlashing` | Bool | Prevent double-trigger |
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `TriggerDamageFlash` | Intensity: Float (0-1) | — | Set DamageVignette opacity, play DamageAnim |
|
||||||
|
| `TriggerJumpScareFlash` | — | — | Play JumpScareAnim, prevent re-trigger during flash |
|
||||||
|
| `SetStressDistortion` | Amount: Float (0-1) | — | Set scalar param on StressDistortionMaterial |
|
||||||
|
| `ClearEffects` | — | — | Reset all overlays to 0 opacity |
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|--------------|--------|-----|
|
||||||
|
| [`WBP_HUDController`](WBP_HUDController.md) | Parent reference | Routing damage/stress events |
|
||||||
|
| [`BPC_HealthSystem`](../02-player/08_BPC_HealthSystem.md) | Dispatcher | Damage flash triggers |
|
||||||
|
| [`BPC_StressSystem`](../02-player/10_BPC_StressSystem.md) | Dispatcher | Stress distortion |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
- Reusable VFX overlay; the same widget handles damage, stress, and jump scares without duplication
|
||||||
|
- Split from original bundled `36_WBP_MenuWidgets.md` per Clean Slate refactoring plan
|
||||||
103
docs/blueprints/06-ui/57_WBP_SettingsMenu.md
Normal file
103
docs/blueprints/06-ui/57_WBP_SettingsMenu.md
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
# WBP_SettingsMenu — Widget (Settings Menu)
|
||||||
|
|
||||||
|
**Parent:** `UUserWidget`
|
||||||
|
**Used by:** `SS_UIManager.PushMenu("SettingsMenu")`
|
||||||
|
**Depends On:** `SS_UIManager`, `SS_SettingsSystem`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Full settings screen with sections: Audio, Video/Graphics, Gameplay, Controls, Accessibility.
|
||||||
|
|
||||||
|
### Enums (local or global)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
E_SettingsTab
|
||||||
|
{
|
||||||
|
Audio,
|
||||||
|
Video,
|
||||||
|
Gameplay,
|
||||||
|
Controls,
|
||||||
|
Accessibility
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `ActiveTab` | `E_SettingsTab` | Currently visible tab |
|
||||||
|
| `TabButtons` | Array of `Button` | Tab navigation buttons |
|
||||||
|
| `TabPanels` | Array of `PanelWidget` | Tab content panels |
|
||||||
|
|
||||||
|
**Audio Tab Children:**
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `MasterVolumeSlider` | `Slider` | 0-100 |
|
||||||
|
| `SFXVolumeSlider` | `Slider` | 0-100 |
|
||||||
|
| `MusicVolumeSlider` | `Slider` | 0-100 |
|
||||||
|
| `VoiceVolumeSlider` | `Slider` | 0-100 |
|
||||||
|
| `SubtitlesToggle` | `CheckBox` | On/Off |
|
||||||
|
|
||||||
|
**Video Tab Children:**
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `ResolutionDropdown` | `ComboBoxString` | Available resolutions |
|
||||||
|
| `WindowModeDropdown` | `ComboBoxString` | Fullscreen, Windowed, Borderless |
|
||||||
|
| `VSyncToggle` | `CheckBox` | On/Off |
|
||||||
|
| `QualityPresetDropdown` | `ComboBoxString` | Low, Medium, High, Epic |
|
||||||
|
| `BrightnessSlider` | `Slider` | 0.5-2.0 gamma |
|
||||||
|
|
||||||
|
**Gameplay Tab Children:**
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `InvertYAxisToggle` | `CheckBox` | On/Off |
|
||||||
|
| `SensitivitySlider` | `Slider` | Mouse sensitivity 0.1-5.0 |
|
||||||
|
| `HoldDurationToggle` | `CheckBox` | Tap vs Hold for interactions |
|
||||||
|
| `AutoPickupToggle` | `CheckBox` | On/Off |
|
||||||
|
| `CrosshairToggle` | `CheckBox` | Show crosshair |
|
||||||
|
|
||||||
|
**Controls Tab Children:**
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `ActionMappingsList` | `ListView` | Key binding rows |
|
||||||
|
| `ResetDefaultsButton` | `Button` | Reset all bindings |
|
||||||
|
|
||||||
|
**Accessibility Tab Children:**
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `SubtitleSizeDropdown` | `ComboBoxString` | Small, Medium, Large |
|
||||||
|
| `SubtitleBackgroundOpacity` | `Slider` | 0-100% |
|
||||||
|
| `ColorblindModeDropdown` | `ComboBoxString` | None, Protanopia, Deuteranopia, Tritanopia |
|
||||||
|
| `HighContrastUIToggle` | `CheckBox` | On/Off |
|
||||||
|
| `CameraShakeIntensity` | `Slider` | 0-100% |
|
||||||
|
| `MotionBlurToggle` | `CheckBox` | On/Off |
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `OnConstruct` | — | — | Populate dropdowns from system, load saved settings |
|
||||||
|
| `SwitchTab` | Tab: `E_SettingsTab` | — | Show selected panel, hide others |
|
||||||
|
| `OnSettingChanged` | SettingName: FName, Value: Generic | — | Write to SS_SettingsSystem |
|
||||||
|
| `SaveSettings` | — | — | SS_SettingsSystem.SaveToDisk |
|
||||||
|
| `ApplyVideoSettings` | — | — | Apply resolution, window mode, quality |
|
||||||
|
| `ResetToDefaults` | — | — | Load default values, re-populate UI |
|
||||||
|
| `OnBack` | — | — | SaveSettings, SS_UIManager.PopMenu |
|
||||||
|
| `RebindKey` | ActionName: FName | — | Listen for next key press, map to action |
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|--------------|--------|-----|
|
||||||
|
| [`SS_UIManager`](44_SS_UIManager.md) | Push/Pop | Menu lifecycle |
|
||||||
|
| `SS_SettingsSystem` | Function calls | Read/write settings |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
- The tab panel pattern can be extended for mod settings or developer menus by adding new tab entries
|
||||||
|
- Split from original bundled `36_WBP_MenuWidgets.md` per Clean Slate refactoring plan
|
||||||
114
docs/blueprints/07-narrative/58_BPC_NarrativeStateSystem.md
Normal file
114
docs/blueprints/07-narrative/58_BPC_NarrativeStateSystem.md
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
# 38 — BPC_NarrativeStateSystem
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`I_Persistable`](../01-core/29_I_Persistable.md) Interface
|
||||||
|
- Gameplay Tag System
|
||||||
|
- [`SS_SaveManager`](../05-save/28_SS_SaveManager.md)
|
||||||
|
- [`BPC_ObjectiveSystem`](39_BPC_ObjectiveSystem.md)
|
||||||
|
- [`BPC_EndingAccumulatorSystem`](45_BPC_EndingAcceleratorSystem.md)
|
||||||
|
- [`BPC_BranchingConsequenceSystem`](42_BPC_BranchingConsequenceSystem.md)
|
||||||
|
- All `I_NarrativeActor` implementors
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
The authoritative store of all narrative flags. Every story decision, discovered secret, and consequence tag is registered here. Acts as the single source of truth for narrative state — all other systems read from here rather than maintaining their own flag copies.
|
||||||
|
|
||||||
|
### Responsibilities
|
||||||
|
- Store narrative flags as `GameplayTag → Bool` map
|
||||||
|
- Store numeric narrative values as `GameplayTag → Float` map
|
||||||
|
- Provide add / check / remove flag API
|
||||||
|
- Persist flags via [`I_Persistable`](../01-core/29_I_Persistable.md)
|
||||||
|
- Broadcast flag changes so reactive systems update (doors, dialogue, environment)
|
||||||
|
- Maintain ordered acquisition log for run summary
|
||||||
|
|
||||||
|
### Does NOT Handle
|
||||||
|
- What happens when a flag changes (that is [`BPC_BranchingConsequenceSystem`](42_BPC_BranchingConsequenceSystem.md) job)
|
||||||
|
- Dialogue flow or choices (those are separate systems)
|
||||||
|
- Objective tracking (that is [`BPC_ObjectiveSystem`](39_BPC_ObjectiveSystem.md))
|
||||||
|
- Ending condition evaluation (that is [`BPC_EndingAccumulatorSystem`](45_BPC_EndingAcceleratorSystem.md))
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `NarrativeFlags` | Map (GameplayTag → Bool) | Binary story flags |
|
||||||
|
| `NarrativeValues` | Map (GameplayTag → Float) | Numeric story values (reputation, corruption, etc.) |
|
||||||
|
| `NarrativeHistory` | Array of GameplayTag | Ordered flag acquisition log |
|
||||||
|
| `bPersistEnabled` | Bool | Whether state auto-persists through [`I_Persistable`](../01-core/29_I_Persistable.md) |
|
||||||
|
| `MaxHistorySize` | Integer | Limits history log to prevent save bloat (default: 500) |
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
|
||||||
|
None required. Flags are GameplayTag-driven.
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
None required. Flat Tag maps suffice.
|
||||||
|
|
||||||
|
### Functions / Events
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `SetFlag` | Tag: GameplayTag, Value: Bool | — | Sets a narrative flag to true/false |
|
||||||
|
| `GetFlag` | Tag: GameplayTag | Bool | Reads a flag; returns false if tag not present |
|
||||||
|
| `RemoveFlag` | Tag: GameplayTag | — | Removes a flag entirely from the map |
|
||||||
|
| `SetValue` | Tag: GameplayTag, Value: Float | — | Sets a numeric narrative value |
|
||||||
|
| `GetValue` | Tag: GameplayTag | Float | Reads numeric value; returns 0 if not present |
|
||||||
|
| `AddToValue` | Tag: GameplayTag, Delta: Float | — | Adds delta to existing value (creates if absent) |
|
||||||
|
| `HasAllFlags` | Tags: Array of GameplayTag | Bool | Returns true only if ALL flags are true |
|
||||||
|
| `HasAnyFlag` | Tags: Array of GameplayTag | Bool | Returns true if ANY flag is true |
|
||||||
|
| `GetAllActiveFlags` | — | Array of GameplayTag | Returns all currently-true flags |
|
||||||
|
| `ClearAllFlags` | — | — | Resets all flags and values (used on new game) |
|
||||||
|
| `CollectState` (I_Persistable) | — | S_WorldObjectState | Serializes flags and values into the custom data map |
|
||||||
|
| `RestoreState` (I_Persistable) | Data: S_WorldObjectState | — | Deserializes flags and values from the custom data map |
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired When |
|
||||||
|
|------|-----------|-----------|
|
||||||
|
| `OnFlagChanged` | Tag: GameplayTag, NewValue: Bool | Any flag value changes |
|
||||||
|
| `OnValueChanged` | Tag: GameplayTag, NewValue: Float | Any numeric value changes |
|
||||||
|
| `OnHistoryFull` | — | History log reaches MaxHistorySize |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[SetFlag called]
|
||||||
|
└─► Store value in NarrativeFlags map
|
||||||
|
└─► Append tag to NarrativeHistory (if becoming true)
|
||||||
|
└─► Broadcast OnFlagChanged
|
||||||
|
└─► Mark owner for persistence via I_Persistable
|
||||||
|
|
||||||
|
[GetFlag called]
|
||||||
|
└─► Look up tag in NarrativeFlags map
|
||||||
|
└─► Tag found? → return its value
|
||||||
|
└─► Tag missing? → return false
|
||||||
|
|
||||||
|
[CollectState for save]
|
||||||
|
└─► Serialize NarrativeFlags → array of (Tag, Bool) pairs
|
||||||
|
└─► Serialize NarrativeValues → array of (Tag, Float) pairs
|
||||||
|
└─► Bundle into S_WorldObjectState.CustomData with key "NarrativeData"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|---------------|--------|-----|
|
||||||
|
| [`BPC_ObjectiveSystem`](39_BPC_ObjectiveSystem.md) | Direct + Dispatcher | Objective activation / completion |
|
||||||
|
| [`BPC_BranchingConsequenceSystem`](42_BPC_BranchingConsequenceSystem.md) | Dispatcher | Consequence evaluation trigger |
|
||||||
|
| [`BPC_EndingAccumulatorSystem`](45_BPC_EndingAcceleratorSystem.md) | Direct | Ending condition evaluation |
|
||||||
|
| [`BPC_DialoguePlaybackSystem`](40_BPC_DialoguePlaybackSystem.md) | Direct | Dialogue condition checks |
|
||||||
|
| [`BPC_LoreUnlockSystem`](46_BPC_LoreUnlockSystem.md) | Dispatcher | Lore discovery on flag set |
|
||||||
|
| All `I_NarrativeActor` implementors (doors, lights, etc.) | Interface | World reacts to flag changes |
|
||||||
|
| [`SS_SaveManager`](../05-save/28_SS_SaveManager.md) | [`I_Persistable`](../01-core/29_I_Persistable.md) | Flag persistence across sessions |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
The entire system is tag-driven and fully generic. The `Narrative.Flag.*` and `Narrative.Phase.*` namespaces are empty by default — fill them per project with game-specific flags. Add new numeric value types (reputation, corruption, etc.) by simply calling `SetValue` with the appropriate tag. No code/blueprint changes needed.
|
||||||
117
docs/blueprints/07-narrative/59_BPC_ObjectiveSystem.md
Normal file
117
docs/blueprints/07-narrative/59_BPC_ObjectiveSystem.md
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
# 39 — BPC_ObjectiveSystem
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- Gameplay Tag System
|
||||||
|
- [`BPC_NarrativeStateSystem`](58_BPC_NarrativeStateSystem.md)
|
||||||
|
- [`WBP_ObjectiveDisplay`](../06-ui/35_WBP_InteractionUI.md) (via dispatcher)
|
||||||
|
- [`BPC_JournalSystem`](41_BPC_JournalSystem.md)
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Tracks active, completed, and failed objectives. Supports main objectives, sub-objectives, hidden objectives, and optional objectives. Acts as the query layer for UI and journal.
|
||||||
|
|
||||||
|
### Responsibilities
|
||||||
|
- Activate objectives by tag
|
||||||
|
- Complete / fail objectives by tag or narrative flag trigger
|
||||||
|
- Track objective dependency chains (must complete A before B activates)
|
||||||
|
- Notify UI and journal on changes
|
||||||
|
- Support objectives hidden until discovery condition met
|
||||||
|
- Persist objective state via [`I_Persistable`](../01-core/29_I_Persistable.md)
|
||||||
|
|
||||||
|
### Does NOT Handle
|
||||||
|
- What completing an objective does in the world (that is story content)
|
||||||
|
- UI display of objectives (that is `WBP_ObjectiveDisplay`)
|
||||||
|
- Ending condition evaluation
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `AllObjectives` | Map (GameplayTag → S_ObjectiveState) | Full objective register |
|
||||||
|
| `ActiveObjectiveTags` | Array of GameplayTag | Currently active objectives |
|
||||||
|
| `CompletedObjectiveTags` | Array of GameplayTag | Completed objectives |
|
||||||
|
| `FailedObjectiveTags` | Array of GameplayTag | Failed objectives |
|
||||||
|
| `ObjectiveOrderPriority` | Array of GameplayTag | Display sort order for UI |
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
|
||||||
|
| Enum | Values | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| `E_ObjectiveStatus` | Inactive, Active, Complete, Failed, Hidden | Objective lifecycle |
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
| Struct | Fields | Description |
|
||||||
|
|--------|--------|-------------|
|
||||||
|
| `S_ObjectiveState` | ObjectiveTag: GameplayTag, Status: E_ObjectiveStatus, DisplayText: FText, Description: FText, SubObjectives: Array of S_ObjectiveState, Dependencies: Array of GameplayTag, bIsHidden: Bool, bIsOptional: Bool | Runtime objective state |
|
||||||
|
| `S_ObjectiveDisplayData` | ObjectiveTag: GameplayTag, DisplayText: FText, bIsComplete: Bool, bIsOptional: Bool | Lightweight UI snapshot |
|
||||||
|
|
||||||
|
### Functions / Events
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `ActivateObjective` | ObjectiveTag: GameplayTag | — | Sets objective Active, checks dependencies |
|
||||||
|
| `CompleteObjective` | ObjectiveTag: GameplayTag | — | Marks complete, broadcasts, unlocks dependents |
|
||||||
|
| `FailObjective` | ObjectiveTag: GameplayTag | — | Marks failed |
|
||||||
|
| `GetActiveObjectives` | — | Array of S_ObjectiveDisplayData | For UI consumption |
|
||||||
|
| `GetCompletedObjectives` | — | Array of GameplayTag | Status check |
|
||||||
|
| `GetFailedObjectives` | — | Array of GameplayTag | Status check |
|
||||||
|
| `IsObjectiveComplete` | ObjectiveTag: GameplayTag | Bool | Single check |
|
||||||
|
| `IsObjectiveActive` | ObjectiveTag: GameplayTag | Bool | Single check |
|
||||||
|
| `RevealHiddenObjective` | ObjectiveTag: GameplayTag | — | Shows previously hidden objective |
|
||||||
|
| `RegisterObjectiveFromDataAsset` | ObjectiveTag: GameplayTag, DataAsset | — | Registers an objective definition from a DA_ObjectiveData |
|
||||||
|
| `CheckDependenciesMet` | ObjectiveTag: GameplayTag | Bool | Returns true if all dependency objectives are complete |
|
||||||
|
| `CollectState` (I_Persistable) | — | S_WorldObjectState | Serializes objective state |
|
||||||
|
| `RestoreState` (I_Persistable) | Data: S_WorldObjectState | — | Deserializes objective state |
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired When |
|
||||||
|
|------|-----------|-----------|
|
||||||
|
| `OnObjectiveActivated` | ObjectiveTag: GameplayTag, Data: S_ObjectiveDisplayData | New objective added to active list |
|
||||||
|
| `OnObjectiveCompleted` | ObjectiveTag: GameplayTag | Objective marked complete |
|
||||||
|
| `OnObjectiveFailed` | ObjectiveTag: GameplayTag | Objective marked failed |
|
||||||
|
| `OnObjectiveRevealed` | ObjectiveTag: GameplayTag | Hidden objective revealed |
|
||||||
|
| `OnAllObjectivesComplete` | — | All active objectives complete (chapter milestone) |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[ActivateObjective called]
|
||||||
|
└─► Look up objective in AllObjectives map
|
||||||
|
└─► Check Dependencies:
|
||||||
|
│ └─► DependenciesMet? → Set status = Active
|
||||||
|
│ └─► Not met? → Set status = Inactive, wait for dependency completion
|
||||||
|
└─► If Hidden → bIsHidden check
|
||||||
|
└─► Add to ActiveObjectiveTags
|
||||||
|
└─► Broadcast OnObjectiveActivated
|
||||||
|
└─► Notify UI and JournalSystem
|
||||||
|
|
||||||
|
[CompleteObjective called]
|
||||||
|
└─► Validate objective is currently Active
|
||||||
|
└─► Set status = Complete
|
||||||
|
└─► Move from ActiveObjectiveTags to CompletedObjectiveTags
|
||||||
|
└─► Broadcast OnObjectiveCompleted
|
||||||
|
└─► Check dependent objectives for activation
|
||||||
|
└─► Check if AllObjectivesComplete → broadcast milestone
|
||||||
|
└─► Set narrative flag via BPC_NarrativeStateSystem
|
||||||
|
```
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|---------------|--------|-----|
|
||||||
|
| [`BPC_NarrativeStateSystem`](58_BPC_NarrativeStateSystem.md) | Direct + Dispatcher | Flag-driven objective activation / completion |
|
||||||
|
| [`WBP_ObjectiveDisplay`](../06-ui/35_WBP_InteractionUI.md) | Dispatcher | UI updates |
|
||||||
|
| [`WBP_HUDController`](../06-ui/33_WBP_HUD.md) | Dispatcher | Objective display visibility |
|
||||||
|
| [`BPC_JournalSystem`](41_BPC_JournalSystem.md) | Direct | Journal entries on objective events |
|
||||||
|
| [`SS_SaveManager`](../05-save/28_SS_SaveManager.md) | [`I_Persistable`](../01-core/29_I_Persistable.md) | Objective state persistence |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
Define objectives via `DA_ObjectiveData` data assets (one per objective) that specify display text, dependencies, associated narrative flag, and optional/hidden flags. The objective system reads from these assets at level start. Adding a new objective type (like timed objectives) requires adding an `S_TimedObjective` struct extension and a timer function — no system redesign needed.
|
||||||
122
docs/blueprints/07-narrative/60_BPC_DialoguePlaybackSystem.md
Normal file
122
docs/blueprints/07-narrative/60_BPC_DialoguePlaybackSystem.md
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
# 40 — BPC_DialoguePlaybackSystem
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`DA_DialogueSequence`](47_DA_NarrativeDataAssets.md) data assets
|
||||||
|
- [`WBP_SubtitleDisplay`](../06-ui/49_WBP_SubtitleDisplay.md)
|
||||||
|
- [`BPC_NarrativeStateSystem`](58_BPC_NarrativeStateSystem.md)
|
||||||
|
- [`BPC_DialogueChoiceSystem`](41_BPC_DialogueChoiceSystem.md)
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Manages the playback of dialogue sequences: line queuing, timing, subtitle routing, audio playback, and lip-sync. Serves as the audio/visual delivery layer for all spoken narrative content.
|
||||||
|
|
||||||
|
### Responsibilities
|
||||||
|
- Receive dialogue sequence data from `DA_DialogueSequence`
|
||||||
|
- Queue lines and play in order with timing
|
||||||
|
- Trigger subtitle display via dispatcher
|
||||||
|
- Play voiceover audio
|
||||||
|
- Fire narrative flags on line completion (if configured in sequence data)
|
||||||
|
- Pause/interrupt dialogue on player action or external event
|
||||||
|
- Support skip-to-next-line and full-skip-sequence
|
||||||
|
|
||||||
|
### Does NOT Handle
|
||||||
|
- Choice presentation (that is [`BPC_DialogueChoiceSystem`](41_BPC_DialogueChoiceSystem.md))
|
||||||
|
- What dialogue plays when (that is level or narrative flow)
|
||||||
|
- Subtitle styling (that is `WBP_SubtitleDisplay`)
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `ActiveSequence` | DA_DialogueSequence | Currently playing sequence asset |
|
||||||
|
| `LineQueue` | Array of S_DialogueLine | Remaining lines to play |
|
||||||
|
| `bIsPlaying` | Bool | Dialogue currently active |
|
||||||
|
| `bIsPaused` | Bool | Dialogue paused |
|
||||||
|
| `CurrentLineIndex` | Integer | Index in sequence |
|
||||||
|
| `LineTimer` | TimerHandle | Auto-advance timer |
|
||||||
|
| `bBlockInputWhilePlaying` | Bool | Suppress player input during dialogue |
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
| Struct | Fields | Description |
|
||||||
|
|--------|--------|-------------|
|
||||||
|
| `S_DialogueLine` | SpeakerTag: GameplayTag, LineText: FText, VoiceAudio: USoundBase, Duration: Float, LipSyncData: UAnimSequence, FlagToSetOnComplete: GameplayTag, AnimTag: GameplayTag, bIsChoicePoint: Bool | One dialogue line with metadata |
|
||||||
|
| `S_DialoguePlaybackOptions` | bCanSkipLine: Bool, bCanSkipSequence: Bool, bShowSubtitles: Bool, SubtitleDelay: Float | Per-sequence playback settings |
|
||||||
|
|
||||||
|
### Functions / Events
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `PlaySequence` | Sequence: DA_DialogueSequence | — | Loads and begins playback of a dialogue sequence |
|
||||||
|
| `PlaySequenceWithOptions` | Sequence, Options: S_DialoguePlaybackOptions | — | Play with overrides |
|
||||||
|
| `QueueSequence` | Sequence | — | Adds sequence to pending queue (for chaining) |
|
||||||
|
| `PlayNextLine` | — | — | Advances to next line in queue |
|
||||||
|
| `SkipCurrentLine` | — | — | Ends current line early, plays next |
|
||||||
|
| `SkipSequence` | — | — | Aborts entire sequence |
|
||||||
|
| `PauseDialogue` | — | — | Pause audio, hold subtitles |
|
||||||
|
| `ResumeDialogue` | — | — | Resume from pause |
|
||||||
|
| `IsDialoguePlaying` | — | Bool | Query |
|
||||||
|
| `SetSpeakerOverride` | SpeakerTag: GameplayTag | — | Override speaker for accessibility |
|
||||||
|
| `GetCurrentLine` | — | S_DialogueLine | For UI binding |
|
||||||
|
| `GetRemainingLineCount` | — | Integer | For UI progress indicator |
|
||||||
|
| `EnqueueDialogueFromVolume` | OverlapActor | — | Called by trigger volume overlap |
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired When |
|
||||||
|
|------|-----------|-----------|
|
||||||
|
| `OnDialogueStarted` | SequenceTag: GameplayTag | Sequence begins |
|
||||||
|
| `OnLineStarted` | Line: S_DialogueLine | New line begins playback |
|
||||||
|
| `OnLineCompleted` | Line: S_DialogueLine | Line audio finishes |
|
||||||
|
| `OnSequenceCompleted` | SequenceTag: GameplayTag | Full sequence done |
|
||||||
|
| `OnDialogueSkipped` | SequenceTag: GameplayTag | Player skipped sequence |
|
||||||
|
| `OnDialoguePaused` | — | Pause triggered |
|
||||||
|
| `OnDialogueResumed` | — | Resume triggered |
|
||||||
|
| `OnChoicePointReached` | Choices: Array of S_DialogueChoice | Sequence reaches a branching point |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[PlaySequence called]
|
||||||
|
└─► If bIsPlaying → return (or QueueSequence)
|
||||||
|
└─► Set bIsPlaying = true
|
||||||
|
└─► Validate sequence conditions (RequiredFlags from DA_DialogueSequence)
|
||||||
|
└─► Load line queue from sequence asset
|
||||||
|
└─► Broadcast OnDialogueStarted
|
||||||
|
└─► Call PlayNextLine
|
||||||
|
|
||||||
|
[PlayNextLine]
|
||||||
|
└─► Queue empty? → Broadcast OnSequenceCompleted → bIsPlaying = false → return
|
||||||
|
└─► Get next S_DialogueLine
|
||||||
|
└─► Start LineTimer (Duration)
|
||||||
|
└─► Play VoiceAudio
|
||||||
|
└─► Broadcast OnLineStarted
|
||||||
|
└─► If line has AnimTag → notify ABP via dispatcher
|
||||||
|
└─► If line is choice point → hand off to BPC_DialogueChoiceSystem
|
||||||
|
└─► Wait for LineTimer or SkipCurrentLine
|
||||||
|
|
||||||
|
[OnLineCompleted]
|
||||||
|
└─► If line has FlagToSetOnComplete → BPC_NarrativeStateSystem.SetFlag()
|
||||||
|
└─► Advance CurrentLineIndex
|
||||||
|
└─► Call PlayNextLine
|
||||||
|
```
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|---------------|--------|-----|
|
||||||
|
| [`WBP_SubtitleDisplay`](../06-ui/49_WBP_SubtitleDisplay.md) | Dispatcher | Show/hide subtitles |
|
||||||
|
| [`BPC_NarrativeStateSystem`](58_BPC_NarrativeStateSystem.md) | Direct | Set flags on line completion |
|
||||||
|
| [`BPC_DialogueChoiceSystem`](41_BPC_DialogueChoiceSystem.md) | Dispatcher | Hand off at choice points |
|
||||||
|
| [`BPC_CameraStateLayer`](../02-player/14_BPC_CameraStateLayer.md) | Dispatcher | Cinematic camera mode for dialogue |
|
||||||
|
| [`BPC_EmbodimentSystem`](../02-player/13_BPC_EmbodimentSystem.md) | Dispatcher | Gesture animation tags |
|
||||||
|
| [`GI_GameFramework`](../01-core/04_GI_GameFramework.md) | Direct | Set game phase during dialogue |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
`DA_DialogueSequence` data assets hold all content — add new sequences per project without touching this system. Voice audio is optional: sequences work without audio (text-only dialogue). Lip-sync data is per-line and can use audio-driven or procedural lip sync.
|
||||||
107
docs/blueprints/07-narrative/61_BPC_DialogueChoiceSystem.md
Normal file
107
docs/blueprints/07-narrative/61_BPC_DialogueChoiceSystem.md
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
# 41 — BPC_DialogueChoiceSystem
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`BPC_NarrativeStateSystem`](58_BPC_NarrativeStateSystem.md)
|
||||||
|
- [`BPC_DialoguePlaybackSystem`](40_BPC_DialoguePlaybackSystem.md)
|
||||||
|
- [`WBP_DialogueChoiceDisplay`](../06-ui/46_WBP_DialogueChoiceDisplay.md)
|
||||||
|
- [`DA_DialogueSequence`](48_DA_NarrativeDataAssets.md)
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Presents branching dialogue choices to the player and routes the selected response back to the narrative system. Manages choice availability based on narrative flags and time limits.
|
||||||
|
|
||||||
|
### Responsibilities
|
||||||
|
- Receive choice set from dialogue flow (via dispatcher from `BPC_DialoguePlaybackSystem`)
|
||||||
|
- Filter choices by required flags (only show choices player qualifies for)
|
||||||
|
- Display choices via `WBP_DialogueChoiceDisplay`
|
||||||
|
- Apply time limit if configured (auto-select default on expiry)
|
||||||
|
- Route selected choice tag to `BPC_NarrativeStateSystem`
|
||||||
|
- Trigger consequence dialogue branches by returning next sequence tag
|
||||||
|
|
||||||
|
### Does NOT Handle
|
||||||
|
- Playing dialogue lines (that is `BPC_DialoguePlaybackSystem`)
|
||||||
|
- Evaluating narrative consequences beyond setting choice flag (that is `BPC_BranchingConsequenceSystem`)
|
||||||
|
- UI visual styling (that is `WBP_DialogueChoiceDisplay`)
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `CurrentChoices` | Array of S_DialogueChoice | Available options for active choice |
|
||||||
|
| `DefaultChoiceIndex` | Integer | Auto-selected if timer expires |
|
||||||
|
| `ChoiceTimeLimit` | Float | Seconds before auto-select (0 = no limit) |
|
||||||
|
| `ChoiceTimer` | TimerHandle | Countdown timer |
|
||||||
|
| `bChoiceActive` | Bool | Player is currently choosing |
|
||||||
|
| `TimeRemaining` | Float | Remaining time for UI display |
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
| Struct | Fields | Description |
|
||||||
|
|--------|--------|-------------|
|
||||||
|
| `S_DialogueChoice` | ChoiceText: FText, ResultFlagTag: GameplayTag, NextSequenceTag: GameplayTag, RequiredFlagTag: GameplayTag, bIsHidden: Bool, Priority: Integer, FlavorText: FText (tooltip/thought) | One choice option |
|
||||||
|
|
||||||
|
### Functions / Events
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `PresentChoices` | Choices: Array of S_DialogueChoice, TimeLimit: Float | — | Opens choice UI and starts timer |
|
||||||
|
| `SelectChoice` | ChoiceIndex: Integer | — | Player or system selects a choice |
|
||||||
|
| `GetValidChoices` | Choices: Array of S_DialogueChoice | Array of S_DialogueChoice | Filters choices by RequiredFlagTag against narrative state |
|
||||||
|
| `HasValidChoices` | Choices: Array | Bool | At least one choice available? |
|
||||||
|
| `OnChoiceTimedOut` | — | — | Default choice auto-selected |
|
||||||
|
| `CancelChoice` | — | — | Exits choice without selecting |
|
||||||
|
| `ProcessSelectedChoice` | Choice: S_DialogueChoice | GameplayTag (NextSequenceTag) | Sets narrative flag and returns next sequence |
|
||||||
|
| `IsChoiceActive` | — | Bool | Query |
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired When |
|
||||||
|
|------|-----------|-----------|
|
||||||
|
| `OnChoicesPresented` | Choices: Array of S_DialogueChoice | Choice UI opens |
|
||||||
|
| `OnChoiceSelected` | SelectedChoice: S_DialogueChoice, ChoiceIndex: Integer | Player makes selection |
|
||||||
|
| `OnChoiceTimedOut` | DefaultChoiceIndex: Integer | Timer expired |
|
||||||
|
| `OnChoiceCancelled` | — | Choice dismissed without selection |
|
||||||
|
| `OnChoiceCompleted` | NextSequenceTag: GameplayTag | Choice processed, ready for next dialogue |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[PresentChoices called]
|
||||||
|
└─► Call GetValidChoices (filter by narrative flags)
|
||||||
|
└─► If no valid choices → skip (choose default or cancel)
|
||||||
|
└─► Set CurrentChoices to valid set
|
||||||
|
└─► Set bChoiceActive = true
|
||||||
|
└─► Broadcast OnChoicesPresented to WBP_DialogueChoiceDisplay
|
||||||
|
└─► If TimeLimit > 0 → start ChoiceTimer
|
||||||
|
|
||||||
|
[SelectChoice called]
|
||||||
|
└─► Validate ChoiceIndex is in range
|
||||||
|
└─► Get selected S_DialogueChoice
|
||||||
|
└─► Clear ChoiceTimer
|
||||||
|
└─► Broadcast OnChoiceSelected
|
||||||
|
└─► Call ProcessSelectedChoice
|
||||||
|
|
||||||
|
[ProcessSelectedChoice]
|
||||||
|
└─► If ResultFlagTag is valid → BPC_NarrativeStateSystem.SetFlag(ResultFlagTag)
|
||||||
|
└─► Set bChoiceActive = false
|
||||||
|
└─► Broadcast OnChoiceCompleted with NextSequenceTag
|
||||||
|
└─► Hand NextSequenceTag back to BPC_DialoguePlaybackSystem
|
||||||
|
```
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|---------------|--------|-----|
|
||||||
|
| [`BPC_NarrativeStateSystem`](58_BPC_NarrativeStateSystem.md) | Direct | Set choice flag, check required flags |
|
||||||
|
| [`BPC_DialoguePlaybackSystem`](40_BPC_DialoguePlaybackSystem.md) | Dispatcher (receives) + Direct (returns) | Receives choice points, returns next sequence |
|
||||||
|
| [`WBP_DialogueChoiceDisplay`](../06-ui/46_WBP_DialogueChoiceDisplay.md) | Dispatcher | Opens/closes choice UI |
|
||||||
|
| [`BPC_BranchingConsequenceSystem`](42_BPC_BranchingConsequenceSystem.md) | Dispatcher | Choice flag changes trigger consequence evaluation |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
Choice filtering by RequiredFlagTag allows context-sensitive dialogue without branching logic in the system. Choices can be hidden (e.g., secret dialogue options only appear if player has a specific lore unlock). The system handles all choice patterns: timed, untimed, locked, hidden, and priority-sorted.
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
# 42 — BPC_BranchingConsequenceSystem
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`BPC_NarrativeStateSystem`](58_BPC_NarrativeStateSystem.md)
|
||||||
|
- [`DA_ConsequenceRule`](48_DA_NarrativeDataAssets.md)
|
||||||
|
- [`BPC_ObjectiveSystem`](39_BPC_ObjectiveSystem.md)
|
||||||
|
- [`BPC_DialoguePlaybackSystem`](40_BPC_DialoguePlaybackSystem.md)
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Evaluates narrative consequences when flags change. Listens for flag changes across the narrative state and fires off deferred or immediate consequences: new dialogue, objective updates, world-state changes, or scene transitions.
|
||||||
|
|
||||||
|
### Responsibilities
|
||||||
|
- Watch for flag changes (via dispatcher from `BPC_NarrativeStateSystem`)
|
||||||
|
- Match changed flags against `DA_ConsequenceRule` entries
|
||||||
|
- Execute matched consequence actions (play dialogue, update objective, modify world, trigger cutscene)
|
||||||
|
- Handle priority and stacking for multiple consequences firing simultaneously
|
||||||
|
- Separate immediate vs. delayed consequences (allow narrative beat to breathe)
|
||||||
|
|
||||||
|
### Does NOT Handle
|
||||||
|
- Setting flags (that is `BPC_NarrativeStateSystem`)
|
||||||
|
- Playing dialogue lines (that is `BPC_DialoguePlaybackSystem`)
|
||||||
|
- Evaluating choices (that is `BPC_DialogueChoiceSystem`)
|
||||||
|
- Triggering cutscene play (that is `BPC_CutsceneBridge`)
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `ConsequenceRules` | Array of DA_ConsequenceRule | Loaded rules from content registry |
|
||||||
|
| `PendingConsequences` | Array of FConsequencePayload | Queue of delayed consequences |
|
||||||
|
| `ActiveConsequenceCount` | Integer | Prevent stacking beyond max |
|
||||||
|
| `MaxConcurrentConsequences` | Integer | Max simultaneous actions (default 3) |
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
| Struct | Fields | Description |
|
||||||
|
|--------|--------|-------------|
|
||||||
|
| `FConsequencePayload` | RuleRef: DA_ConsequenceRule, TriggerFlag: GameplayTag, FireTime: Float (world time for delayed), bIsImmediate: Bool | Holds a consequence awaiting execution |
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
|
||||||
|
| Enum | Values | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| `EConsequenceActionType` | PlayDialogue, UpdateObjective, SetWorldState, TriggerCutscene, UnlockLore, ModifyStat, SpawnActor, DestroyActor | Types of actions a consequence can perform |
|
||||||
|
|
||||||
|
### Functions / Events
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `RegisterConsequenceRule` | Rule: DA_ConsequenceRule | — | Add a rule to active set |
|
||||||
|
| `OnFlagChanged` | FlagTag: GameplayTag, bIsSet: Bool | — | Dispatcher callback from narrative state |
|
||||||
|
| `EvaluateRulesForFlag` | FlagTag: GameplayTag | Array of DA_ConsequenceRule | Find matching rules |
|
||||||
|
| `ExecuteConsequence` | Rule: DA_ConsequenceRule | — | Fire immediate or queue delayed |
|
||||||
|
| `ExecuteImmediateAction` | Action: FConsequenceData | — | Internal: performs single action |
|
||||||
|
| `ProcessDelayedConsequences` | — | — | Called every tick or on timer for delayed queue |
|
||||||
|
| `ClearPendingConsequences` | — | — | Clear all pending (e.g., on scene transition) |
|
||||||
|
| `GetActiveRuleCount` | — | Integer | |
|
||||||
|
|
||||||
|
### Data: FConsequenceData (Inline in DA_ConsequenceRule)
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| ActionType | Enum(EConsequenceActionType) | What to do |
|
||||||
|
| TargetTag | GameplayTag | Dialogue sequence, objective, or world flag to apply |
|
||||||
|
| FloatValue | Float | Numeric modifier (e.g., stat change) |
|
||||||
|
| DelaySeconds | Float | 0 = immediate, >0 = delayed |
|
||||||
|
| bBlocking | Bool | If true, wait for this to complete before next |
|
||||||
|
| Priority | Integer | Higher = execute first |
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired When |
|
||||||
|
|------|-----------|-----------|
|
||||||
|
| `OnConsequenceTriggered` | Rule: DA_ConsequenceRule, ActionType: EConsequenceActionType | Consequence begins execution |
|
||||||
|
| `OnConsequenceCompleted` | Rule: DA_ConsequenceRule | Consequence finished |
|
||||||
|
| `OnConsequenceQueueChanged` | PendingCount: Integer | Delayed queue size changed |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[BPC_NarrativeStateSystem dispatches OnFlagChanged]
|
||||||
|
└─► BPC_BranchingConsequenceSystem.OnFlagChanged(FlagTag, bIsSet)
|
||||||
|
└─► EvaluateRulesForFlag(FlagTag)
|
||||||
|
└─► For each matching DA_ConsequenceRule:
|
||||||
|
└─► Check rule's required flag state matches current state
|
||||||
|
└─► If match → ExecuteConsequence(Rule)
|
||||||
|
|
||||||
|
[ExecuteConsequence - immediate]
|
||||||
|
└─► Broadcast OnConsequenceTriggered
|
||||||
|
└─► ExecuteImmediateAction(Action)
|
||||||
|
└─► Switch on ActionType:
|
||||||
|
PlayDialogue → request BPC_DialoguePlaybackSystem.PlaySequence(TargetTag)
|
||||||
|
UpdateObjective → request BPC_ObjectiveSystem.AddOrUpdateObjective(...)
|
||||||
|
SetWorldState → BPC_NarrativeStateSystem.SetStateFlag(TargetTag, true)
|
||||||
|
TriggerCutscene → request BPC_CutsceneBridge.BeginCutscene(TargetTag)
|
||||||
|
UnlockLore → BPC_NarrativeStateSystem.UnlockLoreEntry(TargetTag)
|
||||||
|
ModifyStat → through player's stat component
|
||||||
|
SpawnActor → spawn from registry
|
||||||
|
DestroyActor → destroy by tag lookup
|
||||||
|
└─► Broadcast OnConsequenceCompleted
|
||||||
|
|
||||||
|
[ExecuteConsequence - delayed]
|
||||||
|
└─► Create FConsequencePayload with FireTime = WorldTime + DelaySeconds
|
||||||
|
└─► Add to PendingConsequences array
|
||||||
|
└─► Sort by FireTime
|
||||||
|
└─► Broadcast OnConsequenceQueueChanged
|
||||||
|
```
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|---------------|--------|-----|
|
||||||
|
| [`BPC_NarrativeStateSystem`](58_BPC_NarrativeStateSystem.md) | Dispatcher (receives) + Direct (sets world state) | Listens for flag changes, may set additional flags |
|
||||||
|
| [`BPC_DialoguePlaybackSystem`](40_BPC_DialoguePlaybackSystem.md) | Direct | Trigger dialogue sequences as consequences |
|
||||||
|
| [`BPC_ObjectiveSystem`](39_BPC_ObjectiveSystem.md) | Direct | Update/add objectives as consequences |
|
||||||
|
| [`BPC_CutsceneBridge`](44_BPC_CutsceneBridge.md) | Direct | Trigger cutscenes as consequences |
|
||||||
|
| [`BPC_LoreUnlockSystem`](46_BPC_LoreUnlockSystem.md) | Direct | Unlock lore entries |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
Consequence rules are data-driven via `DA_ConsequenceRule`, enabling designers to author branch logic without blueprint edits. The system supports both immediate and delayed consequences, allowing dramatic pacing. Priority sorting ensures critical consequences fire first. Blocking flag prevents race conditions between sequential narrative beats.
|
||||||
131
docs/blueprints/07-narrative/63_BPC_TrialScenarioSystem.md
Normal file
131
docs/blueprints/07-narrative/63_BPC_TrialScenarioSystem.md
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
# 43 — BPC_TrialScenarioSystem
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`BPC_NarrativeStateSystem`](58_BPC_NarrativeStateSystem.md)
|
||||||
|
- [`BPC_ObjectiveSystem`](39_BPC_ObjectiveSystem.md)
|
||||||
|
- [`DA_ScenarioData`](../12-content/48_DA_NarrativeDataAssets.md)
|
||||||
|
- [`BPC_CheckpointSystem`](../04-save/34_BPC_CheckpointSystem.md)
|
||||||
|
- [`BPC_DeathHandlingSystem`](../04-save/35_BPC_DeathHandlingSystem.md)
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Manages trial-based gameplay scenarios: combat gauntlets, escape sequences, investigation time-limits, survival waves. Tracks trial state (setup / active / success / fail), enforces rules, and reports outcome to the narrative system.
|
||||||
|
|
||||||
|
### Responsibilities
|
||||||
|
- Load scenario definition from `DA_ScenarioData`
|
||||||
|
- Initialize world state for the trial (spawn enemies, lock doors, set lighting)
|
||||||
|
- Track trial objectives and completion conditions
|
||||||
|
- Monitor failure conditions (player death, time expiry, target escape)
|
||||||
|
- Broadcast success/failure to narrative state
|
||||||
|
- Clean up trial elements on completion
|
||||||
|
|
||||||
|
### Does NOT Handle
|
||||||
|
- Individual enemy AI (that is Phase 8 AI system)
|
||||||
|
- Lighting or atmosphere changes (that is Phase 9 AtmosphereController)
|
||||||
|
- Checkpoint creation (that is BPC_CheckpointSystem)
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `ActiveScenario` | DA_ScenarioData | Currently running scenario (null if none) |
|
||||||
|
| `ScenarioState` | EScenarioState | Current phase of scenario |
|
||||||
|
| `ScenarioStartTime` | Float | World time when scenario began |
|
||||||
|
| `SuccessTags` | Array of GameplayTag | Tags to set on success (fired to NarrativeState) |
|
||||||
|
| `FailureTags` | Array of GameplayTag | Tags to set on failure |
|
||||||
|
| `bHasCheckedFailure` | Bool | Already evaluated fail conditions this tick |
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
|
||||||
|
| Enum | Values | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| `EScenarioState` | Inactive, SetupRunning, ActiveRunning, Success, Failure, Cleanup | Lifecycle of a trial scenario |
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
| Struct | Fields | Description |
|
||||||
|
|--------|--------|-------------|
|
||||||
|
| `FScenarioObjective` | TargetTag: GameplayTag, RequiredCount: Integer, CurrentCount: Integer, bIsComplete: Bool | A tracked objective within the scenario |
|
||||||
|
| `FScenarioCompletionReport` | bSuccess: Bool, ElapsedTime: Float, ObjectivesCompleted: Integer, ObjectivesTotal: Integer, FlagsSet: Array of GameplayTag | Outcome report for narrative |
|
||||||
|
|
||||||
|
### Functions / Events
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `BeginScenario` | Scenario: DA_ScenarioData | — | Start a new trial scenario |
|
||||||
|
| `EndScenario` | bIsSuccess: Bool | — | End current scenario with outcome |
|
||||||
|
| `UpdateObjectiveProgress` | ObjectiveTag: GameplayTag, Delta: Integer | — | Increment/update objective tracker |
|
||||||
|
| `CheckAllObjectives` | — | Bool | Have all objectives been met? |
|
||||||
|
| `CheckFailureConditions` | — | Bool | Has any failure condition triggered? |
|
||||||
|
| `GetTimeRemaining` | — | Float | Time left if scenario has time limit |
|
||||||
|
| `GetElapsedTime` | — | Float | Seconds since scenario started |
|
||||||
|
| `GetScenarioCompletionReport` | — | FScenarioCompletionReport | |
|
||||||
|
| `AbortScenario` | — | — | Force-end with failure, clean up |
|
||||||
|
| `IsScenarioActive` | — | Bool | |
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired When |
|
||||||
|
|------|-----------|-----------|
|
||||||
|
| `OnScenarioStarted` | Scenario: DA_ScenarioData | Scenario begins setup |
|
||||||
|
| `OnScenarioActive` | Scenario: DA_ScenarioData | Setup complete, trial is live |
|
||||||
|
| `OnObjectiveProgressed` | ObjectiveTag: GameplayTag, Progress: Integer, Required: Integer | Any objective count changed |
|
||||||
|
| `OnScenarioSuccess` | Report: FScenarioCompletionReport | All objectives complete |
|
||||||
|
| `OnScenarioFailure` | Report: FScenarioCompletionReport | Failure condition met |
|
||||||
|
| `OnScenarioAborted` | Scenario: DA_ScenarioData | Force-aborted |
|
||||||
|
| `OnScenarioCleanup` | Scenario: DA_ScenarioData | Cleanup complete |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[BeginScenario: DA_ScenarioData]
|
||||||
|
└─► Set ScenarioState = SetupRunning
|
||||||
|
└─► Broadcast OnScenarioStarted
|
||||||
|
└─► Trigger checkpoint auto-save (if configured)
|
||||||
|
└─► Execute scenario setup actions from DA_ScenarioData:
|
||||||
|
└─► Spawn enemies on spawn points
|
||||||
|
└─► Lock / unlock doors by tag
|
||||||
|
└─► Set initial objective state from scenario definition
|
||||||
|
└─► Apply lighting overrides (via AtmosphereController if present)
|
||||||
|
└─► Set ScenarioState = ActiveRunning
|
||||||
|
└─► Broadcast OnScenarioActive
|
||||||
|
└─► Record ScenarioStartTime + start tick monitors
|
||||||
|
|
||||||
|
[Tick / UpdateObjectiveProgress]
|
||||||
|
└─► CheckAllObjectives → if true → EndScenario(success = true)
|
||||||
|
└─► If not bHasCheckedFailure → CheckFailureConditions
|
||||||
|
└─► If failure condition true → EndScenario(success = false)
|
||||||
|
|
||||||
|
[EndScenario: bIsSuccess]
|
||||||
|
└─► ScenarioState = Success or Failure
|
||||||
|
└─► Build FScenarioCompletionReport
|
||||||
|
└─► Broadcast appropriate dispatcher
|
||||||
|
└─► Set narrative success/failure tags via BPC_NarrativeStateSystem
|
||||||
|
└─► ScenarioState = Cleanup
|
||||||
|
└─► Execute cleanup actions:
|
||||||
|
└─► Despawn trial-specific actors
|
||||||
|
└─► Reset doors, lights, etc.
|
||||||
|
└─► Unlock doors that were locked for trial
|
||||||
|
└─► Broadcast OnScenarioCleanup
|
||||||
|
└─► ScenarioState = Inactive
|
||||||
|
└─► ActiveScenario = null
|
||||||
|
```
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|---------------|--------|-----|
|
||||||
|
| [`BPC_NarrativeStateSystem`](58_BPC_NarrativeStateSystem.md) | Direct | Set success/failure flags upon completion |
|
||||||
|
| [`BPC_ObjectiveSystem`](39_BPC_ObjectiveSystem.md) | Direct | Sync scenario objectives with objective UI |
|
||||||
|
| [`BPC_CheckpointSystem`](../04-save/34_BPC_CheckpointSystem.md) | Direct | Create checkpoint on scenario start |
|
||||||
|
| [`BPC_DeathHandlingSystem`](../04-save/35_BPC_DeathHandlingSystem.md) | Dispatcher | Player death triggers scenario failure |
|
||||||
|
| Phase 8 AI | Indirect | Spawn/despawn enemy actors by tag |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
Scenarios are fully data-driven via `DA_ScenarioData`. Designers configure spawn tags, objective tags, time limits, and cleanup actions without blueprint modifications. Supports mixed scenarios: combat + investigation, timed + untimed, survival + objective.
|
||||||
142
docs/blueprints/07-narrative/64_BPC_CutsceneBridge.md
Normal file
142
docs/blueprints/07-narrative/64_BPC_CutsceneBridge.md
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
# 44 — BPC_CutsceneBridge
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`BPC_NarrativeStateSystem`](58_BPC_NarrativeStateSystem.md)
|
||||||
|
- [`BPC_DialoguePlaybackSystem`](40_BPC_DialoguePlaybackSystem.md)
|
||||||
|
- [`BPC_CheckpointSystem`](../04-save/34_BPC_CheckpointSystem.md)
|
||||||
|
- [`DA_CutsceneData`](48_DA_NarrativeDataAssets.md)
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Bridges gameplay and pre-authored cinematics. Manages cutscene triggers, playback, and cleanup. Handles player input suppression, camera transfer, HUD hiding, and seamless return to gameplay.
|
||||||
|
|
||||||
|
### Responsibilities
|
||||||
|
- Load cutscene definition from `DA_CutsceneData`
|
||||||
|
- Transition gameplay camera to cinematic camera
|
||||||
|
- Suppress player input and hide HUD
|
||||||
|
- Play Level Sequence actor or Sequencer track
|
||||||
|
- Listen for sequence completion or skip input
|
||||||
|
- Restore gameplay state on completion
|
||||||
|
- Set narrative flags at cutscene milestones (start / beat / end)
|
||||||
|
|
||||||
|
### Does NOT Handle
|
||||||
|
- Creating cutscene sequences in Sequencer (that is a content authoring task)
|
||||||
|
- Dialogue during cutscenes (uses `BPC_DialoguePlaybackSystem` for subtitled lines)
|
||||||
|
- Triggering cutscene based on narrative state (that is `BPC_NarrativeStateSystem` or level blueprints)
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `ActiveCutscene` | DA_CutsceneData | Currently playing cutscene data |
|
||||||
|
| `CutsceneSequence` | LevelSequenceActor | Reference to the spawned sequence |
|
||||||
|
| `CinematicCameraActor` | CineCameraActor | Camera used during cutscene |
|
||||||
|
| `bIsPlaying` | Bool | Currently in cutscene playback |
|
||||||
|
| `bCanSkip` | Bool | Player can skip this cutscene |
|
||||||
|
| `TimeBeforeSkip` | Float | Seconds before skip becomes available |
|
||||||
|
| `bInputSuppressed` | Bool | Player input disabled |
|
||||||
|
| `SkipHoldDuration` | Float | Seconds player must hold skip key |
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
|
||||||
|
| Enum | Values | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| `ECutsceneTransitionType` | FadeInOut, HardCut, LetterBox, BlackBars | Visual transition style for cutscene |
|
||||||
|
| `ECutsceneTriggerType` | OnVolumeEnter, OnNarrativeFlag, OnInteraction, OnLevelLoad, OnScenarioComplete | What triggers the cutscene |
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
| Struct | Fields | Description |
|
||||||
|
|--------|--------|-------------|
|
||||||
|
| `FCutsceneMilestone` | TimeSeconds: Float, FlagTag: GameplayTag, bFireOnce: Bool | A narrative flag fired at a specific sequence time |
|
||||||
|
| `FCutsceneSkipResult` | bWasSkipped: Bool, SkipTime: Float | Skip outcome |
|
||||||
|
|
||||||
|
### Functions / Events
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `PlayCutscene` | CutsceneData: DA_CutsceneData | — | Begin cutscene playback |
|
||||||
|
| `OnCutsceneStarted` | — | — | Internal: after transition |
|
||||||
|
| `OnCutsceneTick` | DeltaSeconds: Float | — | Check milestones, input, etc. |
|
||||||
|
| `OnCutsceneEnded` | SkipResult: FCutsceneSkipResult | — | Cleanup and return to gameplay |
|
||||||
|
| `SkipCutscene` | — | — | Player-initiated skip (with hold timer) |
|
||||||
|
| `UpdateSkipHoldProgress` | DeltaSeconds: Float | Float (0-1) | For UI skip progress bar |
|
||||||
|
| `SetNarrativeMilestone` | Milestone: FCutsceneMilestone | — | Fire flag at correct time |
|
||||||
|
| `RestoreGameplayState` | — | — | Return camera, enable input, show HUD |
|
||||||
|
| `CanSkipCutscene` | — | Bool | Checks if skip is available |
|
||||||
|
| `GetActiveCutscene` | — | DA_CutsceneData | |
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired When |
|
||||||
|
|------|-----------|-----------|
|
||||||
|
| `OnCutscenePlayRequested` | CutsceneData: DA_CutsceneData | Cutscene about to play |
|
||||||
|
| `OnCutsceneFadeOutStarted` | — | Fade out begins |
|
||||||
|
| `OnCutsceneFadeInStarted` | — | Fade in begins (after black) |
|
||||||
|
| `OnCutscenePlaying` | CutsceneData: DA_CutsceneData | Cutscene is actively playing |
|
||||||
|
| `OnCutsceneMilestoneReached` | Milestone: FCutsceneMilestone | Narrative flag fired |
|
||||||
|
| `OnCutsceneSkipAvailable` | — | Skip button becomes available |
|
||||||
|
| `OnCutsceneSkipRequested` | — | Player holding skip |
|
||||||
|
| `OnCutsceneSkipped` | — | Cutscene was skipped |
|
||||||
|
| `OnCutsceneCompleted` | CutsceneData: DA_CutsceneData | Cutscene finished (skipped or ended) |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[PlayCutscene: DA_CutsceneData]
|
||||||
|
└─► Set bIsPlaying = true
|
||||||
|
└─► Broadcast OnCutscenePlayRequested
|
||||||
|
└─► Auto-save checkpoint (if configured)
|
||||||
|
└─► Apply transition (fade out / letterbox)
|
||||||
|
└─► Suppress input: set PlayerController input mode to UI Only
|
||||||
|
└─► Hide HUD widgets (disable visibility on HUD parent)
|
||||||
|
└─► Spawn LevelSequenceActor from DA_CutsceneData.SequenceSoftReference
|
||||||
|
└─► If cinematics camera is in data → blend to it (SetViewTarget with blend)
|
||||||
|
└─► Broadcast OnCutsceneFadeInStarted
|
||||||
|
└─► Fade in
|
||||||
|
└─► Broadcast OnCutscenePlaying
|
||||||
|
└─► Set bCanSkip timer (if configured)
|
||||||
|
|
||||||
|
[OnCutsceneTick: DeltaSeconds]
|
||||||
|
└─► Update milestones (check if any FCutsceneMilestone should fire)
|
||||||
|
└─► If milestone reached → SetNarrativeMilestone → fire flag via BPC_NarrativeStateSystem
|
||||||
|
└─► Update skip hold progress if player holding skip key
|
||||||
|
└─► Check if sequence has finished → OnCutsceneEnded
|
||||||
|
|
||||||
|
[OnCutsceneEnded: SkipResult]
|
||||||
|
└─► Broadcast milestone flags at end time if not already fired
|
||||||
|
└─► RestoreGameplayState:
|
||||||
|
└─► Blend back to player camera
|
||||||
|
└─► Enable player input
|
||||||
|
└─► Show HUD
|
||||||
|
└─► Destroy LevelSequenceActor
|
||||||
|
└─► Remove cinematic camera
|
||||||
|
└─► Set bIsPlaying = false
|
||||||
|
└─► Broadcast OnCutsceneCompleted
|
||||||
|
|
||||||
|
[SkipCutscene]
|
||||||
|
└─► If !CanSkipCutscene → return
|
||||||
|
└─► Broadcast OnCutsceneSkipped
|
||||||
|
└─► Jump to end of sequence (or stop immediately)
|
||||||
|
└─► Go to OnCutsceneEnded with SkipResult.bWasSkipped = true
|
||||||
|
```
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|---------------|--------|-----|
|
||||||
|
| [`BPC_NarrativeStateSystem`](58_BPC_NarrativeStateSystem.md) | Direct | Fire milestone flags during cutscene |
|
||||||
|
| [`BPC_DialoguePlaybackSystem`](40_BPC_DialoguePlaybackSystem.md) | Direct | Play subtitled dialogue for spoken lines in cutscene |
|
||||||
|
| [`BPC_CheckpointSystem`](../04-save/34_BPC_CheckpointSystem.md) | Direct | Auto-save before cutscene |
|
||||||
|
| PlayerController | Direct | Suppress/restore input and view target |
|
||||||
|
| HUD Widget | Dispatcher | Show/hide HUD |
|
||||||
|
| [`BPC_TrialScenarioSystem`](43_BPC_TrialScenarioSystem.md) | Direct | Cutscene can trigger scenario start/end |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
Cutscene data assets allow designers to configure skip availability, milestone flags, and transition types per cutscene without modifying the system. The milestone system enables narrative flags to fire at exact sequence timecodes for branching logic post-cutscene.
|
||||||
138
docs/blueprints/07-narrative/65_BPC_LoreUnlockSystem.md
Normal file
138
docs/blueprints/07-narrative/65_BPC_LoreUnlockSystem.md
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
# 46 — BPC_LoreUnlockSystem
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`BPC_NarrativeStateSystem`](58_BPC_NarrativeStateSystem.md)
|
||||||
|
- [`DA_LoreEntryData`](48_DA_NarrativeDataAssets.md)
|
||||||
|
- [`WBP_JournalDisplay`](../06-ui/43_WBP_JournalDisplay.md)
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Manages the discovery, unlocking, and tracking of lore entries (documents, notes, recordings, environmental texts, bestiary entries). Provides query methods for lore completeness, and dispatches events when new lore is discovered.
|
||||||
|
|
||||||
|
### Responsibilities
|
||||||
|
- Register all discoverable lore entries from `DA_LoreEntryData` content
|
||||||
|
- Unlock lore entries when narrative flags are set or when player picks up physical lore items
|
||||||
|
- Track which lore entries have been read / unread
|
||||||
|
- Provide category-based filtering (e.g., "All", "Notes", "Audio Logs", "Bestiary")
|
||||||
|
- Dispatch notifications for newly discovered lore
|
||||||
|
- Support lore completion tracking (X of Y discovered in a category)
|
||||||
|
- Handle lore entry grouping (collections, series)
|
||||||
|
|
||||||
|
### Does NOT Handle
|
||||||
|
- Displaying lore entries in UI (that is `WBP_JournalDisplay`)
|
||||||
|
- Playing audio logs (that is `BPC_DialoguePlaybackSystem` or audio component)
|
||||||
|
- Physical lore pickup interaction (that is `BPC_InteractionDetector`)
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `AllLoreEntries` | Array of DA_LoreEntryData | All discoverable lore in game |
|
||||||
|
| `UnlockedLoreTags` | Set of GameplayTag | Tags for unlocked entries |
|
||||||
|
| `ReadLoreTags` | Set of GameplayTag | Tags for entries player has opened |
|
||||||
|
| `LoreByCategory` | Map: GameplayTag (category) → Array of DA_LoreEntryData | Grouped for UI |
|
||||||
|
| `UnreadCount` | Integer | Number of unread unlocked entries |
|
||||||
|
| `NewLoreQueue` | Array of DA_LoreEntryData | Recently unlocked entries for notification |
|
||||||
|
| `TotalLoreCount` | Integer | Total discoverable entries |
|
||||||
|
| `TotalUnlockedCount` | Integer | Count of unlocked entries |
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
| Struct | Fields | Description |
|
||||||
|
|--------|--------|-------------|
|
||||||
|
| `FLoreDiscovery` | Entry: DA_LoreEntryData, DiscoveredAt: Float (game time), DiscoverySource: FName (e.g., "pickup", "narrative", "exploration") | Record of a lore discovery event |
|
||||||
|
| `FLoreCategoryProgress` | CategoryTag: GameplayTag, CategoryName: FText, TotalInCategory: Integer, UnlockedInCategory: Integer, ReadInCategory: Integer | Completion per category |
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
|
||||||
|
| Enum | Values | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| `ELoreEntryType` | Document, AudioLog, Note, EnvironmentalText, BestiaryEntry, Collectible, Epistolary, VHSRecording, Photo | Type of lore entry (affects UI icon) |
|
||||||
|
| `ELoreDiscoveryMethod` | OnPickup, OnNarrativeFlag, OnLocationEnter, OnInteraction, OnConsequence, OnEndingReward | How the entry is discovered |
|
||||||
|
|
||||||
|
### Functions / Events
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `RegisterLoreEntry` | Entry: DA_LoreEntryData | — | Add entry to tracking set |
|
||||||
|
| `UnlockLoreByTag` | LoreTag: GameplayTag | Bool (success) | Unlock specific entry by tag |
|
||||||
|
| `UnlockLoreByMethod` | DiscoveryMethod: ELoreDiscoveryMethod | Array of DA_LoreEntryData | Unlock all entries matching a discovery method |
|
||||||
|
| `MarkLoreAsRead` | LoreTag: GameplayTag | — | Player has opened/viewed the entry |
|
||||||
|
| `MarkLoreAsUnread` | LoreTag: GameplayTag | — | Reset read status |
|
||||||
|
| `IsLoreUnlocked` | LoreTag: GameplayTag | Bool | Is the entry unlocked? |
|
||||||
|
| `IsLoreRead` | LoreTag: GameplayTag | Bool |
|
||||||
|
| `GetLoreByCategory` | CategoryTag: GameplayTag | Array of DA_LoreEntryData | Filter by category |
|
||||||
|
| `GetAllUnlockedLore` | — | Array of DA_LoreEntryData |
|
||||||
|
| `GetUnreadLore` | — | Array of DA_LoreEntryData |
|
||||||
|
| `GetCategoryProgress` | CategoryTag: GameplayTag | FLoreCategoryProgress |
|
||||||
|
| `GetOverallProgress` | — | FLoreCategoryProgress (total all categories) |
|
||||||
|
| `GetLatestDiscoveries` | Count: Integer | Array of DA_LoreEntryData | Newest N unlocked entries |
|
||||||
|
| `GetUnlockedCountByType` | Type: ELoreEntryType | Integer |
|
||||||
|
| `DismissNewLoreNotification` | — | — | Clears new lore queue after UI displays |
|
||||||
|
| `HasUnreadLore` | — | Bool |
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired When |
|
||||||
|
|------|-----------|-----------|
|
||||||
|
| `OnLoreEntryUnlocked` | Entry: DA_LoreEntryData, DiscoveryMethod: ELoreDiscoveryMethod | Any lore entry is newly unlocked |
|
||||||
|
| `OnLoreEntryRead` | Entry: DA_LoreEntryData | Lore entry marked as read |
|
||||||
|
| `OnLoreCategoryCompleted` | CategoryTag: GameplayTag, CategoryName: FText | All entries in a category discovered |
|
||||||
|
| `OnNewLoreNotification` | NewEntries: Array of DA_LoreEntryData, Count: Integer | Batch of new lore for notification queue |
|
||||||
|
| `OnLoreProgressUpdated` | OverallProgress: FLoreCategoryProgress | Any unlock/read changed progress |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[Initialization]
|
||||||
|
└─► At game start: load all DA_LoreEntryData from asset registry
|
||||||
|
└─► Group by CategoryTag into LoreByCategory map
|
||||||
|
└─► Set TotalLoreCount = AllLoreEntries.Num()
|
||||||
|
└─► Bind to BPC_NarrativeStateSystem.OnFlagChanged for OnNarrativeFlag unlocks
|
||||||
|
|
||||||
|
[UnlockLoreByTag: LoreTag]
|
||||||
|
└─► Find DA_LoreEntryData by LoreTag from AllLoreEntries
|
||||||
|
└─► If not found → return false
|
||||||
|
└─► If already in UnlockedLoreTags → return true (already unlocked, no duplicate notification)
|
||||||
|
└─► Add LoreTag to UnlockedLoreTags
|
||||||
|
└─► Increment TotalUnlockedCount
|
||||||
|
└─► Add to NewLoreQueue
|
||||||
|
└─► Broadcast OnLoreEntryUnlocked(Entry, DiscoveryMethod)
|
||||||
|
└─► Broadcast OnLoreProgressUpdated
|
||||||
|
└─► If NewLoreQueue size == 1 → broadcast OnNewLoreNotification
|
||||||
|
└─► Check if category is now complete → broadcast OnLoreCategoryCompleted if yes
|
||||||
|
└─► Return true
|
||||||
|
|
||||||
|
[MarkLoreAsRead: LoreTag]
|
||||||
|
└─► If LoreTag not in UnlockedLoreTags → return
|
||||||
|
└─► If already in ReadLoreTags → return
|
||||||
|
└─► Add to ReadLoreTags
|
||||||
|
└─► Decrement UnreadCount
|
||||||
|
└─► Broadcast OnLoreEntryRead
|
||||||
|
└─► Broadcast OnLoreProgressUpdated
|
||||||
|
|
||||||
|
[GetCategoryProgress: CategoryTag]
|
||||||
|
└─► Get entries in this category
|
||||||
|
└─► Count unlocked in category
|
||||||
|
└─► Count read in category
|
||||||
|
└─► Return FLoreCategoryProgress
|
||||||
|
```
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|---------------|--------|-----|
|
||||||
|
| [`BPC_NarrativeStateSystem`](58_BPC_NarrativeStateSystem.md) | Dispatcher (receives) + Direct (unlock by flag) | Narrative flags trigger lore unlocks |
|
||||||
|
| [`WBP_JournalDisplay`](../06-ui/43_WBP_JournalDisplay.md) | Dispatcher | Notify UI of new lore, progress updates |
|
||||||
|
| [`BPC_InteractionDetector`](../02-interaction/15_BPC_InteractionDetector.md) | Dispatcher (on pickup) | Physical lore items call UnlockLoreByTag |
|
||||||
|
| [`BPC_BranchingConsequenceSystem`](42_BPC_BranchingConsequenceSystem.md) | Dispatcher | Consequences can unlock lore entries |
|
||||||
|
| [Save System](../04-save/32_SS_SaveManager.md) | Direct (via I_Persistable) | Save unlocked/read state |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
Pure data-driven: all lore content is defined in `DA_LoreEntryData` assets. The system requires zero blueprint changes to add new lore entries. Supports any mix of discovery methods (pickup, narrative flag, location trigger, consequence). Lore categories are defined by gameplay tags, allowing flexible grouping without code changes.
|
||||||
250
docs/blueprints/07-narrative/66_DA_NarrativeDataAssets.md
Normal file
250
docs/blueprints/07-narrative/66_DA_NarrativeDataAssets.md
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
# 48 — DA_NarrativeDataAssets (Collection)
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
This file defines all Data Asset types used by the Narrative systems. Each data asset subclass is a standalone asset that designers create in the Content Browser to author narrative content without blueprint modifications.
|
||||||
|
|
||||||
|
### Relationship to Systems
|
||||||
|
|
||||||
|
| Data Asset | Consumed By | Purpose |
|
||||||
|
|-----------|-------------|---------|
|
||||||
|
| `DA_DialogueSequence` | [`BPC_DialoguePlaybackSystem`](40_BPC_DialoguePlaybackSystem.md) | Dialogue lines, voiceover, subtitles |
|
||||||
|
| `DA_ConsequenceRule` | [`BPC_BranchingConsequenceSystem`](42_BPC_BranchingConsequenceSystem.md) | Flag → action mapping |
|
||||||
|
| `DA_EndingData` | [`BPC_EndingAccumulatorSystem`](45_BPC_EndingAccumulatorSystem.md) | Ending evaluation criteria |
|
||||||
|
| `DA_LoreEntryData` | [`BPC_LoreUnlockSystem`](46_BPC_LoreUnlockSystem.md) | Lore content and metadata |
|
||||||
|
| `DA_ScenarioData` | [`BPC_TrialScenarioSystem`](43_BPC_TrialScenarioSystem.md) | Trial scenario definition |
|
||||||
|
| `DA_CutsceneData` | [`BPC_CutsceneBridge`](44_BPC_CutsceneBridge.md) | Cutscene playback configuration |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 48.1 — DA_DialogueSequence
|
||||||
|
|
||||||
|
**Parent Class:** `PrimaryDataAsset`
|
||||||
|
|
||||||
|
#### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `DialogueTag` | GameplayTag | Unique identifier for this sequence |
|
||||||
|
| `SpeakerName` | FText | Display name for UI subtitle |
|
||||||
|
| `DialogueLines` | Array of FDialogueLine | Ordered lines in this sequence |
|
||||||
|
| `bLooping` | Bool | Repeat sequence until manually stopped |
|
||||||
|
| `bInterruptible` | Bool | Can be skipped / interrupted |
|
||||||
|
| `Priority` | Integer | Higher priority interrupts lower |
|
||||||
|
| `Category` | GameplayTag | e.g., "Dialogue.Major", "Dialogue.Ambient" |
|
||||||
|
| `PrerequisiteFlags` | Array of GameplayTag | Must all be set for this sequence to play |
|
||||||
|
| `ConsequenceFlagsOnComplete` | Array of GameplayTag | Flags set when sequence finishes |
|
||||||
|
|
||||||
|
#### Struct: FDialogueLine
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `LineText` | FText | Subtitle text |
|
||||||
|
| `VoiceoverSoftRef` | SoftObjectPath (USoundWave) | Audio file for this line |
|
||||||
|
| `Duration` | Float | Seconds (0 = auto-calculate from audio) |
|
||||||
|
| `EmotionOverride` | GameplayTag | e.g., "Emotion.Angry", "Emotion.Whisper" |
|
||||||
|
| `bWaitForInput` | Bool | Pause after line until player presses continue |
|
||||||
|
| `LineFlagsToSet` | Array of GameplayTag | Narrative flags set when this line plays |
|
||||||
|
| `CustomDelayAfter` | Float | Extra pause after this line before next |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 48.2 — DA_ConsequenceRule
|
||||||
|
|
||||||
|
**Parent Class:** `PrimaryDataAsset`
|
||||||
|
|
||||||
|
#### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `RuleTag` | GameplayTag | Unique identifier |
|
||||||
|
| `TriggerFlagTag` | GameplayTag | Which flag change triggers this rule |
|
||||||
|
| `bTriggerOnSet` | Bool | Fire when flag is set |
|
||||||
|
| `bTriggerOnClear` | Bool | Fire when flag is cleared |
|
||||||
|
| `RequiredContextFlags` | Array of GameplayTag | Additional flags that must be set for rule to fire |
|
||||||
|
| `ExclusiveContextFlags` | Array of GameplayTag | Flags that block this rule |
|
||||||
|
| `Actions` | Array of FConsequenceAction | Actions to execute |
|
||||||
|
| `bIsImmediate` | Bool | Always immediate (overrides per-action delay) |
|
||||||
|
| `Priority` | Integer | Execution order (higher first) |
|
||||||
|
| `bOnlyOnce` | Bool | Remove rule after first fire |
|
||||||
|
| `CooldownSeconds` | Float | Prevent re-fire within cooldown |
|
||||||
|
|
||||||
|
#### Struct: FConsequenceAction
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `ActionType` | EConsequenceActionType | PlayDialogue / UpdateObjective / SetFlag / ClearFlag / TriggerCutscene / UnlockLore / ModifyStat / SpawnActor / DestroyActor |
|
||||||
|
| `TargetTag` | GameplayTag | Target dialogue sequence, lore entry, etc. |
|
||||||
|
| `FloatValue` | Float | Numeric modifier for stat changes |
|
||||||
|
| `StringValue` | FString | Actor tag for spawn/destroy |
|
||||||
|
| `DelaySeconds` | Float | Delay before this action executes |
|
||||||
|
| `bBlocking` | Bool | Wait for completion before next action |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 48.3 — DA_EndingData
|
||||||
|
|
||||||
|
**Parent Class:** `PrimaryDataAsset`
|
||||||
|
|
||||||
|
#### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `EndingTag` | GameplayTag | Unique identifier |
|
||||||
|
| `EndingName` | FText | Display name for UI / debug |
|
||||||
|
| `EndingDescription` | FText | Brief description |
|
||||||
|
| `Priority` | Integer | Higher priority endings win over lower |
|
||||||
|
| `RequiredFlagTags` | Array of GameplayTag | All must be set to qualify |
|
||||||
|
| `ExclusiveFlagTags` | Array of GameplayTag | None must be set to qualify |
|
||||||
|
| `MinScore` | Float | Minimum accumulated score to qualify |
|
||||||
|
| `ScoreEntries` | Array of FEndingScoreEntry | Flags that contribute score |
|
||||||
|
| `CutsceneData` | TSoftObjectPtr<DA_CutsceneData> | Ending cutscene to play |
|
||||||
|
| `bIsFallbackEnding` | Bool | Always available if no other ending qualifies |
|
||||||
|
| `EndingRewards` | Array of GameplayTag | Bonuses, unlocks, achievements granted |
|
||||||
|
|
||||||
|
#### Struct: FEndingScoreEntry
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `FlagTag` | GameplayTag | Flag that grants score when set |
|
||||||
|
| `ScoreValue` | Float | Points awarded |
|
||||||
|
| `ScoreCategory` | FName | Group for UI display |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 48.4 — DA_LoreEntryData
|
||||||
|
|
||||||
|
**Parent Class:** `PrimaryDataAsset`
|
||||||
|
|
||||||
|
#### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `LoreTag` | GameplayTag | Unique identifier |
|
||||||
|
| `EntryType` | ELoreEntryType | Document, AudioLog, Note, EnvironmentalText, BestiaryEntry, Collectible |
|
||||||
|
| `CategoryTag` | GameplayTag | e.g., "Lore.Notes.DrWatson", "Lore.Bestiary.Creatures" |
|
||||||
|
| `Title` | FText | Entry title |
|
||||||
|
| `BodyText` | FText | Main content text |
|
||||||
|
| `FlavorText` | FText | Optional flavor/subtitle |
|
||||||
|
| `AudioSoftRef` | SoftObjectPath (USoundWave) | Audio narration (if audio log) |
|
||||||
|
| `AudioDuration` | Float | Length of audio in seconds |
|
||||||
|
| `ImageSoftRef` | SoftObjectPath (UTexture2D) | Optional image / photo |
|
||||||
|
| `UnlockMethod` | ELoreDiscoveryMethod | OnPickup / OnNarrativeFlag / OnLocationEnter / OnInteraction / OnConsequence |
|
||||||
|
| `UnlockFlagTag` | GameplayTag | If OnNarrativeFlag, which flag triggers unlock |
|
||||||
|
| `RequiredFlags` | Array of GameplayTag | Prerequisite flags |
|
||||||
|
| `SeriesTag` | GameplayTag | Part of a series (e.g., "Lore.Series.InvestigationNotes") |
|
||||||
|
| `SeriesIndex` | Integer | Order within series |
|
||||||
|
| `IsTimedEntry` | Bool | Entry expires after game time |
|
||||||
|
| `TimeToLiveSeconds` | Float | How long entry remains available |
|
||||||
|
| `bAutoReadOnUnlock` | Bool | Auto-mark as read when discovered |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 48.5 — DA_ScenarioData
|
||||||
|
|
||||||
|
**Parent Class:** `PrimaryDataAsset`
|
||||||
|
|
||||||
|
#### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `ScenarioTag` | GameplayTag | Unique identifier |
|
||||||
|
| `ScenarioName` | FText | Display name |
|
||||||
|
| `ScenarioType` | EScenarioType | CombatGauntlet, EscapeSequence, InvestigationTimed, SurvivalWave, Mixed |
|
||||||
|
| `TimeLimitSeconds` | Float | 0 = no time limit |
|
||||||
|
| `bFailOnPlayerDeath` | Bool | Player death = scenario failure |
|
||||||
|
| `SpawnTags` | Array of GameplayTag | Enemy / actor tags to spawn |
|
||||||
|
| `SpawnPointTags` | Array of GameplayTag | Where to spawn them |
|
||||||
|
| `Objectives` | Array of FScenarioObjectiveDef | Scenario objectives |
|
||||||
|
| `FailureConditions` | Array of FScenarioFailureCondition | What triggers failure |
|
||||||
|
| `LockedDoorTags` | Array of GameplayTag | Doors to lock during scenario |
|
||||||
|
| `UnlockOnSuccessDoorTags` | Array of GameplayTag | Doors to unlock on success |
|
||||||
|
| `SuccessFlags` | Array of GameplayTag | Flags set on success |
|
||||||
|
| `FailureFlags` | Array of GameplayTag | Flags set on failure |
|
||||||
|
| `CutsceneOnStart` | TSoftObjectPtr<DA_CutsceneData> | Cutscene before scenario |
|
||||||
|
| `CutsceneOnSuccess` | TSoftObjectPtr<DA_CutsceneData> | Cutscene on success |
|
||||||
|
| `CutsceneOnFailure` | TSoftObjectPtr<DA_CutsceneData> | Cutscene on failure |
|
||||||
|
| `bCreateCheckpointOnStart` | Bool | Auto-save before scenario |
|
||||||
|
|
||||||
|
#### Enums
|
||||||
|
|
||||||
|
| Enum | Values | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| `EScenarioType` | CombatGauntlet, EscapeSequence, InvestigationTimed, SurvivalWave, Mixed | Scenario gameplay type |
|
||||||
|
|
||||||
|
#### Struct: FScenarioObjectiveDef
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `ObjectiveTag` | GameplayTag | Reference to objective system |
|
||||||
|
| `Description` | FText | Objective description |
|
||||||
|
| `RequiredCount` | Integer | How many to complete |
|
||||||
|
| `bIsOptional` | Bool | Not required for success |
|
||||||
|
|
||||||
|
#### Struct: FScenarioFailureCondition
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `ConditionType` | EFailureConditionType | TimeExpired / PlayerDeath / TargetEscaped / ObjectiveFailed |
|
||||||
|
| `TargetTag` | GameplayTag | Specific target reference |
|
||||||
|
| `FloatParam` | Float | Threshold (e.g., max deaths) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 48.6 — DA_CutsceneData
|
||||||
|
|
||||||
|
**Parent Class:** `PrimaryDataAsset`
|
||||||
|
|
||||||
|
#### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `CutsceneTag` | GameplayTag | Unique identifier |
|
||||||
|
| `CutsceneName` | FText | Editor display name |
|
||||||
|
| `SequenceSoftReference` | TSoftObjectPtr<ALevelSequenceActor> | The Level Sequence to play |
|
||||||
|
| `TransitionType` | ECutsceneTransitionType | FadeInOut, HardCut, LetterBox, BlackBars |
|
||||||
|
| `TransitionDuration` | Float | Fade duration in seconds |
|
||||||
|
| `bCanSkip` | Bool | Player can skip |
|
||||||
|
| `TimeBeforeSkipAvailable` | Float | Seconds before skip is enabled |
|
||||||
|
| `SkipHoldDuration` | Float | Hold seconds to skip |
|
||||||
|
| `Milestones` | Array of FCutsceneMilestone | Narrative flags at specific times |
|
||||||
|
| `bAutoSaveBefore` | Bool | Create checkpoint before cutscene |
|
||||||
|
| `bRestorePlayerState` | Bool | Save and restore player stats after |
|
||||||
|
| `DialogueSequences` | Array of TSoftObjectPtr<DA_DialogueSequence> | Subtitled dialogue during cutscene |
|
||||||
|
| `OnCompleteFlags` | Array of GameplayTag | Flags set when cutscene ends |
|
||||||
|
| `OnSkippedFlags` | Array of GameplayTag | Flags set if player skips |
|
||||||
|
| `bHideHUD` | Bool | Auto-hide HUD during cutscene |
|
||||||
|
| `bSuppressPlayerInput` | Bool | Disable player input during cutscene |
|
||||||
|
|
||||||
|
#### Struct: FCutsceneMilestone
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `TimeSeconds` | Float | Sequence timecode |
|
||||||
|
| `FlagTag` | GameplayTag | Flag to set |
|
||||||
|
| `bFireOnce` | Bool | Only fire if not already set |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Asset Registration Pattern
|
||||||
|
|
||||||
|
All narrative data assets should be registered in the **Primary Asset Registry** (Project Settings > Asset Manager) with the appropriate scan paths:
|
||||||
|
|
||||||
|
| Data Asset | Directory Convention | Asset Prefix |
|
||||||
|
|-----------|---------------------|--------------|
|
||||||
|
| DA_DialogueSequence | `/Game/Dialogue/` | `SEQ_` |
|
||||||
|
| DA_ConsequenceRule | `/Game/Narrative/Consequences/` | `CON_` |
|
||||||
|
| DA_EndingData | `/Game/Narrative/Endings/` | `END_` |
|
||||||
|
| DA_LoreEntryData | `/Game/Narrative/Lore/` | `LORE_` |
|
||||||
|
| DA_ScenarioData | `/Game/Narrative/Scenarios/` | `SCEN_` |
|
||||||
|
| DA_CutsceneData | `/Game/Cinematics/CutsceneData/` | `CUT_` |
|
||||||
|
|
||||||
|
### Save System Integration
|
||||||
|
|
||||||
|
Each data asset is identified by its `Tag` field (GameplayTag). The narrative state system saves and loads only the tag references and flag states — the data assets themselves are loaded from content references and never serialized into save data. This means:
|
||||||
|
- Flags persisted as `Set<GameplayTag>` in save file
|
||||||
|
- Lore unlocked/read persisted as `Set<GameplayTag>`
|
||||||
|
- Data assets can be modified between saves without breaking save compatibility
|
||||||
159
docs/blueprints/07-narrative/67_BP_NarrativeTriggerVolume.md
Normal file
159
docs/blueprints/07-narrative/67_BP_NarrativeTriggerVolume.md
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
# BP_NarrativeTriggerVolume — Actor (with BoxComponent as root)
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`Actor` (with `BoxComponent` as root)
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`BPC_NarrativeStateSystem`](58_BPC_NarrativeStateSystem.md)
|
||||||
|
- [`BPC_DialoguePlaybackSystem`](40_BPC_DialoguePlaybackSystem.md)
|
||||||
|
- [`BPC_CutsceneBridge`](44_BPC_CutsceneBridge.md)
|
||||||
|
- [`BPC_TrialScenarioSystem`](43_BPC_TrialScenarioSystem.md)
|
||||||
|
- [`BPC_LoreUnlockSystem`](46_BPC_LoreUnlockSystem.md)
|
||||||
|
- [`I_NarrativeActor`](../01-core/03_I_NarrativeActor.md)
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
A level-placed trigger volume that detects player overlap and fires narrative actions. Supports one-shot / reusable / conditional modes, with optional prerequisite flags and cooldown timers. The primary way for level designers to trigger narrative events without blueprint logic.
|
||||||
|
|
||||||
|
### Responsibilities
|
||||||
|
- Detect player BeginOverlap / EndOverlap
|
||||||
|
- Check prerequisite flags (required and exclusive)
|
||||||
|
- Execute configured actions on trigger (fire-and-forget)
|
||||||
|
- Support trigger types: Once, Reusable, Conditional, TimedAutoReset
|
||||||
|
- Broadcast onset / offset variants if configured
|
||||||
|
- Prevent re-trigger during cooldown
|
||||||
|
|
||||||
|
### Does NOT Handle
|
||||||
|
- Playing dialogue or cutscenes directly (calls the relevant system)
|
||||||
|
- Setting flags on the NarrativeState (that is what the actions do)
|
||||||
|
- UI display (that is UI layer)
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `TriggerTag` | GameplayTag | Unique identifier for this trigger |
|
||||||
|
| `TriggerType` | ETriggerType | How the trigger behaves |
|
||||||
|
| `TriggerActions` | Array of FTriggerActionEntry | Actions to fire when triggered |
|
||||||
|
| `bAlsoFireOnExit` | Bool | Fire separate actions when player exits |
|
||||||
|
| `ExitActions` | Array of FTriggerActionEntry | Actions fired on exit (if enabled) |
|
||||||
|
| `RequiredFlags` | Array of GameplayTag | All must be set to enable |
|
||||||
|
| `ExclusiveFlags` | Array of GameplayTag | If any are set, trigger is disabled |
|
||||||
|
| `CooldownSeconds` | Float | Time before reusable triggers can re-fire |
|
||||||
|
| `bHasTriggered` | Bool | Has fired at least once |
|
||||||
|
| `bIsEnabled` | Bool | Can be toggled on/off by external systems |
|
||||||
|
| `LastTriggerTime` | Float | World time of last trigger |
|
||||||
|
| `CollisionBox` | BoxComponent | The overlap volume |
|
||||||
|
| `TriggerDisplayName` | FText | Editor-friendly name |
|
||||||
|
| `bDebugMode` | Bool | Print trigger events to screen |
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
|
||||||
|
| Enum | Values | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| `ETriggerType` | Once, Reusable, ConditionalOnce, ConditionalReusable, TimedAutoReset | Once: fires first time only. Reusable: fires every time with cooldown. Conditional*: checks flags each time. TimedAutoReset: resets after cooldown. |
|
||||||
|
| `ENarrativeActionType` | SetFlag, ClearFlag, PlayDialogue, StartCutscene, BeginScenario, UnlockLore, UpdateObjective, PlaySound, ExecuteConsoleCommand, ToggleActorEnabled, CustomEvent | Types of actions a trigger can perform |
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
| Struct | Fields | Description |
|
||||||
|
|--------|--------|-------------|
|
||||||
|
| `FTriggerActionEntry` | ActionType: ENarrativeActionType, TargetTag: GameplayTag, FloatParam: Float (delay), StringParam: FString (for console commands), bBlocking: Bool (wait for completion), DelayBeforeFire: Float | One action entry in the trigger action list |
|
||||||
|
| `FTriggerConditionState` | bMetRequirements: Bool, bBlockedByExclusive: Bool, bOnCooldown: Bool, bAlreadyTriggered: Bool | Debug state of trigger conditions |
|
||||||
|
|
||||||
|
### Functions / Events
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `OnPlayerEntered` | OverlappedComp: PrimitiveComponent, OtherActor: Actor | — | BeginOverlap event |
|
||||||
|
| `OnPlayerExited` | OverlappedComp: PrimitiveComponent, OtherActor: Actor | — | EndOverlap event (if bAlsoFireOnExit) |
|
||||||
|
| `EvaluateTriggerConditions` | — | Bool (can fire) | Check all conditions |
|
||||||
|
| `FireTrigger` | — | — | Execute all trigger actions |
|
||||||
|
| `ExecuteActionEntry` | Entry: FTriggerActionEntry | — | Perform one action |
|
||||||
|
| `ResetTrigger` | — | — | Reset bHasTriggered (for TimedAutoReset) |
|
||||||
|
| `SetTriggerEnabled` | bEnabled: Bool | — | External toggle |
|
||||||
|
| `GetTriggerState` | — | FTriggerConditionState | Debug info |
|
||||||
|
| `ManualTrigger` | — | — | Force trigger from external BP |
|
||||||
|
| `PreviewActions` | — | — | Editor preview of what this trigger does |
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired When |
|
||||||
|
|------|-----------|-----------|
|
||||||
|
| `OnTriggerPrepared` | TriggerTag: GameplayTag | Conditions met, about to fire |
|
||||||
|
| `OnTriggerFired` | TriggerTag: GameplayTag, ActionCount: Integer | All actions executed |
|
||||||
|
| `OnTriggerBlocked` | TriggerTag: GameplayTag, Reason: FName | Conditions not met |
|
||||||
|
| `OnTriggerCooldown` | TriggerTag: GameplayTag, RemainingCooldown: Float | Cooldown active |
|
||||||
|
| `OnTriggerReset` | TriggerTag: GameplayTag | Trigger reset for reuse |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[OnPlayerEntered]
|
||||||
|
└─► If !bIsEnabled → return
|
||||||
|
└─► If EvaluateTriggerConditions == false → broadcast OnTriggerBlocked → return
|
||||||
|
└─► FireTrigger
|
||||||
|
|
||||||
|
[EvaluateTriggerConditions]
|
||||||
|
└─► If TriggerType == Once && bHasTriggered → return false (already fired)
|
||||||
|
└─► If TriggerType == ConditionalOnce && bHasTriggered → return false
|
||||||
|
└─► If TriggerType == TimedAutoReset && !bHasTriggered → skip (but can fire if reset)
|
||||||
|
└─► Check RequiredFlags: all must be set in BPC_NarrativeStateSystem
|
||||||
|
└─► Check ExclusiveFlags: none must be set
|
||||||
|
└─► If TriggerType == Reusable || ConditionalReusable:
|
||||||
|
└─► Check LastTriggerTime + CooldownSeconds < CurrentTime
|
||||||
|
└─► If on cooldown → broadcast OnTriggerCooldown → return false
|
||||||
|
└─► Return true (all conditions pass)
|
||||||
|
|
||||||
|
[FireTrigger]
|
||||||
|
└─► Broadcast OnTriggerPrepared
|
||||||
|
└─► Set bHasTriggered = true
|
||||||
|
└─► Set LastTriggerTime = CurrentTime
|
||||||
|
└─► For each FTriggerActionEntry in TriggerActions (in order):
|
||||||
|
└─► If entry.DelayBeforeFire > 0 → delay via timer
|
||||||
|
└─► ExecuteActionEntry(Entry)
|
||||||
|
└─► If bAlsoFireOnExit → bind OnPlayerExited to execute ExitActions
|
||||||
|
└─► Broadcast OnTriggerFired
|
||||||
|
└─► If TriggerType == TimedAutoReset → start timer for ResetTrigger after CooldownSeconds
|
||||||
|
|
||||||
|
[ExecuteActionEntry: Entry]
|
||||||
|
└─► Switch on Entry.ActionType:
|
||||||
|
SetFlag → BPC_NarrativeStateSystem.SetFlag(Entry.TargetTag)
|
||||||
|
ClearFlag → BPC_NarrativeStateSystem.ClearFlag(Entry.TargetTag)
|
||||||
|
PlayDialogue → Get BPC_DialoguePlaybackSystem → PlaySequence(Entry.TargetTag)
|
||||||
|
StartCutscene → Get BPC_CutsceneBridge → PlayCutscene(Entry.TargetTag as DA_CutsceneData)
|
||||||
|
BeginScenario → Get BPC_TrialScenarioSystem → BeginScenario(Entry.TargetTag as DA_ScenarioData)
|
||||||
|
UnlockLore → Get BPC_LoreUnlockSystem → UnlockLoreByTag(Entry.TargetTag)
|
||||||
|
UpdateObjective → Get BPC_ObjectiveSystem → AddOrUpdateObjective(...)
|
||||||
|
PlaySound → Play sound at volume location
|
||||||
|
ExecuteConsoleCommand → Exec(Entry.StringParam)
|
||||||
|
ToggleActorEnabled → Find actor by tag, toggle set actor hidden / disable collision
|
||||||
|
CustomEvent → Dispatch custom gameplay event tag (listeners in level blueprint)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|---------------|--------|-----|
|
||||||
|
| [`BPC_NarrativeStateSystem`](58_BPC_NarrativeStateSystem.md) | Direct | Check/set flags for conditions and actions |
|
||||||
|
| [`BPC_DialoguePlaybackSystem`](40_BPC_DialoguePlaybackSystem.md) | Direct | Trigger dialogue sequences |
|
||||||
|
| [`BPC_CutsceneBridge`](44_BPC_CutsceneBridge.md) | Direct | Start cutscenes |
|
||||||
|
| [`BPC_TrialScenarioSystem`](43_BPC_TrialScenarioSystem.md) | Direct | Begin trial scenarios |
|
||||||
|
| [`BPC_LoreUnlockSystem`](46_BPC_LoreUnlockSystem.md) | Direct | Unlock lore |
|
||||||
|
| [`BPC_ObjectiveSystem`](39_BPC_ObjectiveSystem.md) | Direct | Update objectives |
|
||||||
|
| Level Blueprint | Dispatcher (CustomEvent) | Layer for designer-specific logic |
|
||||||
|
|
||||||
|
### Placement in Level
|
||||||
|
- Drag into level from Content Browser
|
||||||
|
- Scale BoxComponent to desired trigger area
|
||||||
|
- Configure TriggerActions array in Details panel
|
||||||
|
- Tag with TriggerTag for save/load identification
|
||||||
|
- Optional: set PrerequisiteFlags to gate the trigger
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
This is the primary level-design tool for narrative gating. Designers place volumes, configure action lists, and set prerequisite flags — no blueprint editing required. Supports all narrative action types. The `CustomEvent` action type allows designers to bind custom level blueprint logic when no predefined action fits.
|
||||||
|
|
||||||
|
- Renamed from `47_BPC_NarrativeTriggerVolume` to `BP_NarrativeTriggerVolume` per Master naming convention.
|
||||||
136
docs/blueprints/07-narrative/68_BPC_EndingAccumulator.md
Normal file
136
docs/blueprints/07-narrative/68_BPC_EndingAccumulator.md
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
# BPC_EndingAccumulator — Actor Component
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`BPC_NarrativeStateSystem`](58_BPC_NarrativeStateSystem.md)
|
||||||
|
- [`DA_NarrativeDataAssets`](48_DA_NarrativeDataAssets.md)
|
||||||
|
- [`BPC_CheckpointSystem`](../05-saveload/BP_Checkpoint.md)
|
||||||
|
- [`BPC_CutsceneBridge`](44_BPC_CutsceneBridge.md)
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Evaluates all narrative flags accumulated throughout gameplay and determines which ending the player qualifies for. Supports weighted scoring systems, required flag checks, and ending priority ranking.
|
||||||
|
|
||||||
|
### Responsibilities
|
||||||
|
- Maintain an ending score table (flags → points)
|
||||||
|
- Listen for narrative flag changes and accumulate score
|
||||||
|
- Evaluate ending eligibility on demand or at game-end trigger
|
||||||
|
- Rank endings by priority: specific requirements win over general
|
||||||
|
- Provide preview data to UI for ending readiness indicator
|
||||||
|
- Trigger the winning ending cutscene via `BPC_CutsceneBridge`
|
||||||
|
|
||||||
|
### Does NOT Handle
|
||||||
|
- Setting individual narrative flags (that is `BPC_NarrativeStateSystem`)
|
||||||
|
- Playing ending cutscene sequence (that is `BPC_CutsceneBridge`)
|
||||||
|
- UI display of ending readiness (that is UI layer)
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `EndingDefinitions` | Array of DA_EndingData | All possible endings loaded at game start |
|
||||||
|
| `EndingScores` | Map: GameplayTag → Float | Current accumulated scores per category |
|
||||||
|
| `bEndingReady` | Bool | At least one ending is achievable |
|
||||||
|
| `QualifiedEndings` | Array of DA_EndingData | Endings that meet requirements |
|
||||||
|
| `bGameEndTriggered` | Bool | Prevents re-evaluation after game end |
|
||||||
|
| `HighestPriorityEnding` | DA_EndingData | The selected ending |
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
|
||||||
|
| Enum | Values | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| `EEndingEvaluationMode` | OnDemand, Continuous, OnGameEndTrigger | When to evaluate ending eligibility |
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
| Struct | Fields | Description |
|
||||||
|
|--------|--------|-------------|
|
||||||
|
| `FEndingScoreEntry` | FlagTag: GameplayTag, ScoreValue: Float, Category: FName | Scores accumulate when flag is set |
|
||||||
|
| `FEndingQualification` | Ending: DA_EndingData, Score: Float, bRequiredFlagsMet: Bool, bExclusiveFlagsMet: Bool, Priority: Integer, bIsQualified: Bool | Full qualification result |
|
||||||
|
|
||||||
|
### Functions / Events
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `InitializeEndings` | — | — | Load all DA_EndingData from content registry |
|
||||||
|
| `OnNarrativeFlagChanged` | FlagTag: GameplayTag, bIsSet: Bool | — | Accumulate score when flag is set |
|
||||||
|
| `EvaluateEndings` | — | DA_EndingData | Evaluate all endings, return highest priority |
|
||||||
|
| `GetEndingQualification` | Ending: DA_EndingData | FEndingQualification | Check specific ending qualification |
|
||||||
|
| `GetQualifiedEndings` | — | Array of FEndingQualification | All endings player qualifies for |
|
||||||
|
| `GetEndingReadinessPercentage` | — | Float | 0-1 how close player is to any ending |
|
||||||
|
| `GetTotalScoreForEnding` | Ending: DA_EndingData | Float | Current accumulated score |
|
||||||
|
| `TriggerGameEnding` | OverrideEndingTag: GameplayTag (optional) | — | Force game end with specific or best ending |
|
||||||
|
| `ResetEndingState` | — | — | Clear scores (for new game+) |
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired When |
|
||||||
|
|------|-----------|-----------|
|
||||||
|
| `OnEndingScoresUpdated` | Scores: Map of GameplayTag → Float | Any score changes |
|
||||||
|
| `OnEndingQualificationChanged` | QualifiedEndings: Array of DA_EndingData | Qualification set changed |
|
||||||
|
| `OnGameEndingTriggered` | SelectedEnding: DA_EndingData | Game ending sequence begins |
|
||||||
|
| `OnEndingReadyChanged` | bReady: Bool | Player became eligible / lost eligibility |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[InitializeEndings]
|
||||||
|
└─► Load all DA_EndingData from primary asset registry (tag-based lookup)
|
||||||
|
└─► Sort by Priority (descending)
|
||||||
|
└─► Bind to BPC_NarrativeStateSystem.OnFlagChanged dispatcher
|
||||||
|
|
||||||
|
[OnNarrativeFlagChanged: FlagTag, bIsSet]
|
||||||
|
└─► If !bIsSet → return (only care about flags being set, not cleared)
|
||||||
|
└─► For each DA_EndingData:
|
||||||
|
└─► Lookup FlagTag in Ending.ScoreEntries
|
||||||
|
└─► If found → EndingScores[FlagTag] += ScoreValue
|
||||||
|
└─► If EEvaluationMode == Continuous → EvaluateEndings
|
||||||
|
└─► Broadcast OnEndingScoresUpdated
|
||||||
|
|
||||||
|
[EvaluateEndings]
|
||||||
|
└─► Clear QualifiedEndings array
|
||||||
|
└─► For each DA_EndingData (sorted by priority descending):
|
||||||
|
└─► Check RequiredFlagTags: all must be set
|
||||||
|
└─► Check ExclusiveFlagTags: none must be set
|
||||||
|
└─► Check score threshold: CurrentScore >= MinScore
|
||||||
|
└─► If all pass → add to QualifiedEndings
|
||||||
|
└─► If QualifiedEndings is empty:
|
||||||
|
└─► HighestPriorityEnding = null
|
||||||
|
└─► bEndingReady = false
|
||||||
|
└─► Else:
|
||||||
|
└─► HighestPriorityEnding = QualifiedEndings[0] (highest priority)
|
||||||
|
└─► bEndingReady = true
|
||||||
|
└─► Broadcast OnEndingQualificationChanged
|
||||||
|
|
||||||
|
[TriggerGameEnding: OverrideEndingTag]
|
||||||
|
└─► If bGameEndTriggered → return (prevent double trigger)
|
||||||
|
└─► Set bGameEndTriggered = true
|
||||||
|
└─► If OverrideEndingTag is valid:
|
||||||
|
└─► Find DA_EndingData by tag → SelectedEnding
|
||||||
|
└─► Else:
|
||||||
|
└─► EvaluateEndings → SelectedEnding = HighestPriorityEnding
|
||||||
|
└─► If SelectedEnding is null → fallback ending (always exists)
|
||||||
|
└─► Broadcast OnGameEndingTriggered with SelectedEnding
|
||||||
|
└─► Create checkpoint if configured
|
||||||
|
└─► Trigger BPC_CutsceneBridge.PlayCutscene(SelectedEnding.CutsceneData)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|---------------|--------|-----|
|
||||||
|
| [`BPC_NarrativeStateSystem`](58_BPC_NarrativeStateSystem.md) | Dispatcher (receives) | Listen for flag changes to accumulate score |
|
||||||
|
| [`BPC_CutsceneBridge`](44_BPC_CutsceneBridge.md) | Direct | Trigger ending cutscene |
|
||||||
|
| [`BP_Checkpoint`](../05-saveload/BP_Checkpoint.md) | Direct | Save before ending sequence |
|
||||||
|
| UI Layer | Dispatcher | Update ending readiness indicator |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
Completely data-driven. Designers create DA_EndingData assets with required flags, exclusive flags, score thresholds, and score entries. The priority system ensures specific endings (e.g., "True Ending") override general ones. The system supports both linear and score-based ending evaluation simultaneously.
|
||||||
|
|
||||||
|
- Renamed from `45_BPC_EndingAccumulatorSystem` to `BPC_EndingAccumulator` per Master naming convention.
|
||||||
|
- Cross-references updated: `BPC_CheckpointSystem` → `BP_Checkpoint`.
|
||||||
149
docs/blueprints/08-weapons/69_BP_WeaponBase.md
Normal file
149
docs/blueprints/08-weapons/69_BP_WeaponBase.md
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
# 49 — BP_WeaponBase
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`Actor` (attached to character via `AttachToComponent`)
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`BPC_InventoryComponent`](../04-inventory/25_BPC_InventoryComponent.md)
|
||||||
|
- [`BPC_EquipmentSystem`](../04-inventory/28_BPC_EquipmentSystem.md)
|
||||||
|
- [`BPC_PlayerCameraManager`](../02-player/14_BPC_PlayerCameraManager.md)
|
||||||
|
- [`BPC_DamageHandler`](53_BPC_DamageHandlerComponent.md)
|
||||||
|
- [`DA_WeaponData`](../12-content/60_DA_WeaponDataAsset.md)
|
||||||
|
- [`I_Damageable`](../01-core/03_I_Damageable.md)
|
||||||
|
- [`BPC_CombatFeedbackComponent`](54_BPC_CombatFeedbackComponent.md)
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Base class for all weapon actors (ranged and melee). Provides common weapon lifecycle: equip, unequip, fire input, ammo tracking integration, aim-down-sights, and weapon state machine. Designed to be extended by [`BP_RangedWeapon`](50_BP_RangedWeapon.md) and [`BP_MeleeWeapon`](51_BP_MeleeWeapon.md).
|
||||||
|
|
||||||
|
### Responsibilities
|
||||||
|
- Attach/detach from character socket on equip/unequip
|
||||||
|
- Manage weapon state (holstered / equipping / ready / firing / reloading)
|
||||||
|
- Handle fire input (start / stop) — base delegates to subclass
|
||||||
|
- Track aim-down-sights state and apply camera FOV transition
|
||||||
|
- Fire event dispatchers for UI state changes
|
||||||
|
- Communicate with [`BPC_EquipmentSystem`](../04-inventory/28_BPC_EquipmentSystem.md) for weapon selection
|
||||||
|
- Communicate with inventory for ammo consumption
|
||||||
|
|
||||||
|
### Does NOT Handle
|
||||||
|
- Specific fire logic (raycast / projectile / melee hitbox) — that is subclass
|
||||||
|
- Ammo math (that is [`BPC_AmmoComponent`](52_BPC_AmmoComponent.md))
|
||||||
|
- Damage calculation (that is [`BPC_DamageHandler`](53_BPC_DamageHandlerComponent.md))
|
||||||
|
- Visual / audio feedback (that is [`BPC_CombatFeedbackComponent`](54_BPC_CombatFeedbackComponent.md))
|
||||||
|
- UI display (that is HUD widget)
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `WeaponData` | DA_WeaponData | Weapon definition asset |
|
||||||
|
| `WeaponState` | EWeaponState | Current state machine |
|
||||||
|
| `SkeletalMesh` | USkeletalMeshComponent | Weapon visual |
|
||||||
|
| `WeaponRoot` | USceneComponent | Attachment root |
|
||||||
|
| `HolsterSocket` | FName | Socket name for holstered |
|
||||||
|
| `EquipSocket` | FName | Socket name for ready |
|
||||||
|
| `AimDownSightsFOV` | Float | FOV when ADS (e.g. 60.0) |
|
||||||
|
| `DefaultFOV` | Float | Normal FOV |
|
||||||
|
| `ADSInterpSpeed` | Float | Camera blend speed |
|
||||||
|
| `bIsAiming` | Bool | Currently ADS |
|
||||||
|
| `FireRateTimer` | FTimerHandle | Fire rate control |
|
||||||
|
| `bCanFire` | Bool | Not on cooldown |
|
||||||
|
| `EquipDuration` | Float | Seconds for equip animation |
|
||||||
|
| `HolsterDuration` | Float | Seconds for holster animation |
|
||||||
|
| `RecoilPitch` | Float | Camera pitch per shot |
|
||||||
|
| `RecoilYaw` | Float | Camera yaw per shot |
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
|
||||||
|
| Enum | Values | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| `EWeaponState` | Holstered, Equipping, Ready, Firing, Reloading, Holstering, Broken | Weapon lifecycle |
|
||||||
|
| `EWeaponFireMode` | SemiAutomatic, FullAutomatic, BurstFire, ChargeAndRelease, MeleeSwing | Fire type |
|
||||||
|
|
||||||
|
### Functions / Events
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `Equip` | — | — | Play equip anim, attach to EquipSocket, set state to Ready |
|
||||||
|
| `Holster` | — | — | Play holster anim, attach to HolsterSocket, set state to Holstered |
|
||||||
|
| `StartFire` | — | — | Begin fire loop (if auto) or single fire (semi) |
|
||||||
|
| `StopFire` | — | — | End fire loop |
|
||||||
|
| `CanFire` | — | Bool | Check state, ammo, cooldown |
|
||||||
|
| `OnFire` | — | — | Virtual: subclass implements actual fire logic |
|
||||||
|
| `StartReload` | — | — | Begin reload sequence |
|
||||||
|
| `FinishReload` | — | — | Called after reload anim |
|
||||||
|
| `StartAim` | — | — | Blend camera to ADS FOV |
|
||||||
|
| `StopAim` | — | — | Blend camera back to DefaultFOV |
|
||||||
|
| `SetWeaponState` | NewState: EWeaponState | — | Change state and broadcast |
|
||||||
|
| `GetWeaponTag` | — | GameplayTag | Return weapon identifier |
|
||||||
|
| `OnWeaponBroken` | — | — | Weapon disabled (durability zero) |
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired When |
|
||||||
|
|------|-----------|-----------|
|
||||||
|
| `OnWeaponStateChanged` | NewState: EWeaponState, OldState: EWeaponState | State machine change |
|
||||||
|
| `OnWeaponEquipped` | WeaponRef: BP_WeaponBase | Ready to use |
|
||||||
|
| `OnWeaponHolstered` | WeaponRef: BP_WeaponBase | Stored away |
|
||||||
|
| `OnFireStarted` | — | Fire input received and valid |
|
||||||
|
| `OnFireStopped` | — | Fire input released |
|
||||||
|
| `OnAimStarted` | — | ADS enter |
|
||||||
|
| `OnAimStopped` | — | ADS exit |
|
||||||
|
| `OnReloadStarted` | — | Reload begin |
|
||||||
|
| `OnReloadCompleted` | — | Reload finish |
|
||||||
|
| `OnWeaponBroken` | — | Durability depleted |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[StartFire]
|
||||||
|
└─► If !CanFire → return
|
||||||
|
└─► Set WeaponState = Firing
|
||||||
|
└─► Broadcast OnFireStarted
|
||||||
|
└─► Call OnFire (subclass override)
|
||||||
|
└─► If FireMode == FullAutomatic → start FireRateTimer loop
|
||||||
|
└─► If FireMode == SemiAutomatic → block until timer clears
|
||||||
|
|
||||||
|
[StopFire]
|
||||||
|
└─► Clear FireRateTimer
|
||||||
|
└─► If FireMode == BurstFire → let burst complete, then stop
|
||||||
|
└─► Set WeaponState = Ready
|
||||||
|
└─► Broadcast OnFireStopped
|
||||||
|
|
||||||
|
[StartReload]
|
||||||
|
└─► If WeaponState != Ready → return
|
||||||
|
└─► Set WeaponState = Reloading
|
||||||
|
└─► Broadcast OnReloadStarted
|
||||||
|
└─► Start animation or timer for reload duration
|
||||||
|
└─► On completion → FinishReload
|
||||||
|
|
||||||
|
[FinishReload]
|
||||||
|
└─► BPC_AmmoComponent.RefillAmmo(WeaponData.MagazineSize)
|
||||||
|
└─► Set WeaponState = Ready
|
||||||
|
└─► Broadcast OnReloadCompleted
|
||||||
|
|
||||||
|
[StartAim / StopAim]
|
||||||
|
└─► Set bIsAiming
|
||||||
|
└─► Get BPC_PlayerCameraManager from owner
|
||||||
|
└─► Set FOV with interp speed
|
||||||
|
└─► Broadcast OnAimStarted / OnAimStopped
|
||||||
|
```
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|---------------|--------|-----|
|
||||||
|
| [`BPC_EquipmentSystem`](../04-inventory/28_BPC_EquipmentSystem.md) | Direct | Get/return weapon socket assignment |
|
||||||
|
| [`BPC_AmmoComponent`](52_BPC_AmmoComponent.md) | Get Component | Check / consume / refill ammo |
|
||||||
|
| [`BPC_DamageHandler`](53_BPC_DamageHandlerComponent.md) | Get Component | Delegate damage processing |
|
||||||
|
| [`BPC_CombatFeedbackComponent`](54_BPC_CombatFeedbackComponent.md) | Get Component | Play muzzle flash, hit FX, sounds |
|
||||||
|
| [`BPC_PlayerCameraManager`](../02-player/14_BPC_PlayerCameraManager.md) | Get from Owner | ADS FOV blend |
|
||||||
|
| [`BPC_InventoryComponent`](../04-inventory/25_BPC_InventoryComponent.md) | Get from Owner | Check ammo inventory |
|
||||||
|
| HUD Widget | Dispatcher (via GM) | Update crosshair, ammo display |
|
||||||
|
| Owner (Player Character) | Direct | Animation blueprint queries weapon state |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
Subclass for ranged or melee specific fire implementations. The state machine prevents firing during reload, equip, or holster sequences. Weapons are data-driven via `DA_WeaponData` — design changes never require blueprint edits.
|
||||||
127
docs/blueprints/08-weapons/70_BPC_AmmoComponent.md
Normal file
127
docs/blueprints/08-weapons/70_BPC_AmmoComponent.md
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
# 52 — BPC_AmmoComponent
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`BP_WeaponBase`](49_BP_WeaponBase.md) — Owner weapon
|
||||||
|
- [`BPC_InventoryComponent`](../04-inventory/25_BPC_InventoryComponent.md) — Consumable ammo items
|
||||||
|
- [`DA_WeaponData`](../12-content/60_DA_WeaponDataAsset.md) — Magazine size, ammo type
|
||||||
|
- [`DA_AmmoType`](../12-content/61_DA_AmmoTypeDataAsset.md) — Ammo definition
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Manages ammunition state for a weapon: current magazine count, total reserve, reload logic, ammo type validation, and UI updates. Attached to each weapon actor as a child component.
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `WeaponDataRef` | DA_WeaponData | Weapon data asset (set at construction) |
|
||||||
|
| `CurrentMagazineAmmo` | Int | Bullets currently in magazine |
|
||||||
|
| `ReserveAmmo` | Int | Total carried ammo of matching type |
|
||||||
|
| `MaxMagazineSize` | Int | From weapon data |
|
||||||
|
| `AmmoTypeTag` | FGameplayTag | e.g. Tag.Ammo.9mm |
|
||||||
|
| `bInfiniteAmmo` | Bool | Debug / cheat toggle |
|
||||||
|
| `ReloadType` | EReloadType | Full, Partial, SingleBullet |
|
||||||
|
| `ReloadTimeMultiplier` | Float | Per-weapon reload speed scaling |
|
||||||
|
| `AmmoHistory` | Array<FAmmoEntry> | Save/load stack |
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
| Struct | Fields | Description |
|
||||||
|
|--------|--------|-------------|
|
||||||
|
| `FAmmoEntry` | AmmoTypeTag: FGameplayTag, MagazineCount: Int, ReserveCount: Int | State for save/load |
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
|
||||||
|
| Enum | Values | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| `EReloadType` | FullMagazine, PartialFill, SingleBulletLoad | How reload animation works |
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `InitializeAmmo` | WeaponData: DA_WeaponData | — | Set magazine size, count from reserve |
|
||||||
|
| `CanFire` | — | Bool | MagazineAmmo > 0 |
|
||||||
|
| `ConsumeAmmo` | Count: Int (default 1) | Int (remaining) | Remove from magazine |
|
||||||
|
| `StartReload` | — | Bool | Check reserve; if > 0 begin reload |
|
||||||
|
| `FinishReload` | — | — | Refill magazine from reserve; update UI |
|
||||||
|
| `GetCurrentMagazineAmmo` | — | Int | UI binding |
|
||||||
|
| `GetReserveAmmo` | — | Int | UI binding |
|
||||||
|
| `AddAmmo` | Count: Int, AmmoTag: FGameplayTag | — | From pickup / reward |
|
||||||
|
| `RemoveAmmoFromReserve` | Count: Int | — | Manufacturing / crafting |
|
||||||
|
| `GetAmmoPercent` | — | Float | 0.0–1.0 for progress bar |
|
||||||
|
| `IsAmmoEmpty` | — | Bool | Magazine + Reserve == 0 |
|
||||||
|
| `OnAmmoChanged` | — | — | Trigger dispatcher |
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired When |
|
||||||
|
|------|-----------|-----------|
|
||||||
|
| `OnAmmoUpdated` | CurrentMagazine: Int, Reserve: Int | Any ammo change |
|
||||||
|
| `OnReloadStarted` | ReloadTime: Float | Reload begin |
|
||||||
|
| `OnReloadCompleted` | — | Reload end |
|
||||||
|
| `OnOutOfAmmo` | — | Magazine + Reserve == 0 |
|
||||||
|
| `OnAmmoPickup` | AmmoTypeTag: FGameplayTag, Count: Int | Ammo added |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[InitializeAmmo]
|
||||||
|
└─► MaxMagazineSize = WeaponData.MagazineSize
|
||||||
|
└─► AmmoTypeTag = WeaponData.AmmoType
|
||||||
|
└─► If start with full: CurrentMagazineAmmo = MaxMagazineSize
|
||||||
|
└─► Else CurrentMagazineAmmo = 0
|
||||||
|
|
||||||
|
[ConsumeAmmo(Count)]
|
||||||
|
└─► If bInfiniteAmmo → return
|
||||||
|
└─► CurrentMagazineAmmo = FMath::Max(0, CurrentMagazineAmmo - Count)
|
||||||
|
└─► OnAmmoChanged
|
||||||
|
|
||||||
|
[StartReload]
|
||||||
|
└─► If ReserveAmmo <= 0 → broadcast OnOutOfAmmo → return false
|
||||||
|
└─► If ReloadType == FullMagazine:
|
||||||
|
AmountNeeded = MaxMagazineSize - CurrentMagazineAmmo
|
||||||
|
Taken = FMath::Min(AmountNeeded, ReserveAmmo)
|
||||||
|
└─► If ReloadType == PartialFill:
|
||||||
|
Taken = FMath::Min(ReloadAmount, ReserveAmmo) // per stage
|
||||||
|
└─► If ReloadType == SingleBulletLoad:
|
||||||
|
Taken = 1
|
||||||
|
└─► ReserveAmmo -= Taken
|
||||||
|
└─► Wait for ReloadTime (configured in WeaponData)
|
||||||
|
└─► CurrentMagazineAmmo += Taken
|
||||||
|
└─► OnAmmoChanged
|
||||||
|
└─► Return true
|
||||||
|
|
||||||
|
[FinishReload — called by animation notify or timer]
|
||||||
|
└─► Fill magazine based on remaining reserve
|
||||||
|
└─► Broadcast OnReloadCompleted
|
||||||
|
|
||||||
|
[AddAmmo(Count, AmmoTag)]
|
||||||
|
└─► If AmmoTag matches AmmoTypeTag:
|
||||||
|
ReserveAmmo += Count
|
||||||
|
Broadcast OnAmmoPickup(AmmoTag, Count)
|
||||||
|
OnAmmoChanged
|
||||||
|
```
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target | Method | Why |
|
||||||
|
|--------|--------|-----|
|
||||||
|
| [`BP_WeaponBase`](49_BP_WeaponBase.md) | Owner | Ammo check before fire, reload notification |
|
||||||
|
| [`BPC_InventoryComponent`](../04-inventory/25_BPC_InventoryComponent.md) | Get from Player | Add/remove ammo items from inventory |
|
||||||
|
| HUD Widget | Dispatcher (via Owner) | Ammo counter and low ammo warning |
|
||||||
|
| Save/Load System | GetSaveData / LoadFromData | Persist ammo state |
|
||||||
|
| Owner Animation BP | Direct | Reload animation play |
|
||||||
|
| [`BPC_CombatFeedbackComponent`](54_BPC_CombatFeedbackComponent.md) | Get Component | Click on empty fire attempt |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
- Designed for magazine-fed and chamber-fed weapons
|
||||||
|
- SingleBulletLoad used for shotguns / break-action weapons
|
||||||
|
- Reload can be interrupted by StartFire if loaded magazine > 0
|
||||||
|
- Ammo pickup broadcasts to allow UI, audio, VFX reactions
|
||||||
149
docs/blueprints/08-weapons/71_BPC_CombatFeedbackComponent.md
Normal file
149
docs/blueprints/08-weapons/71_BPC_CombatFeedbackComponent.md
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
# 54 — BPC_CombatFeedbackComponent
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`BP_WeaponBase`](49_BP_WeaponBase.md) — Owner weapon
|
||||||
|
- [`BPC_DamageHandlerComponent`](53_BPC_DamageHandlerComponent.md) — Damage result data
|
||||||
|
- [`DA_WeaponData`](../12-content/60_DA_WeaponDataAsset.md) — FX/sound references
|
||||||
|
- [`DA_ImpactFXConfig`](../12-content/62_DA_ImpactFXConfigDataAsset.md) — Surface impact map
|
||||||
|
- [`AudioManager`](../09-atmosphere/69_BPC_AudioManager.md) — Spatial audio requests
|
||||||
|
- [`VFXManager`](../09-atmosphere/70_BPC_VFXManager.md) — Particle system pooling
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Centralized visual and audio feedback for combat actions. Decouples weapon and damage logic from presentation. Manages muzzle flash, tracer trajectories, impact decals and particles, bullet holes, hit confirmation UI numbers, weapon sounds (fire, reload, empty click, melee whoosh), and screen effects (camera shake, hit directional indicator). Implemented as a reusable component attached to weapon actors.
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `WeaponMesh` | USkeletalMeshComponent | Reference to owner weapon mesh |
|
||||||
|
| `MuzzleFlashPS` | UParticleSystem | Muzzle flash effect |
|
||||||
|
| `TracerPS` | UParticleSystem | Tracer trail effect |
|
||||||
|
| `TracerSpawnChance` | Float | 0.0–1.0 probability per shot |
|
||||||
|
| `MuzzleSocketName` | FName | Spawn point name |
|
||||||
|
| `ImpactDecalMap` | TMap<PhysicalMaterial, UMaterialInterface> | Surface material → decal |
|
||||||
|
| `ImpactParticleMap` | TMap<PhysicalMaterial, UParticleSystem> | Surface → hit particle |
|
||||||
|
| `ImpactDecalLifeSpan` | Float | Seconds before decal fades |
|
||||||
|
| `ImpactDecalSize` | FVector | Decal projection scale |
|
||||||
|
| `FireSound` | USoundBase | Fire sound cue |
|
||||||
|
| `FireSoundTail` | USoundBase | Tail loop (full auto) |
|
||||||
|
| `ReloadSound` | USoundBase | Reload start |
|
||||||
|
| `ReloadFinishSound` | USoundBase | Reload complete |
|
||||||
|
| `EmptyClickSound` | USoundBase | Empty magazine fire attempt |
|
||||||
|
| `MeleeWhooshSound` | USoundBase | Melee swing whoosh |
|
||||||
|
| `MeleeHitSound` | USoundBase | Melee impact |
|
||||||
|
| `BlockSound` | USoundBase | Block/parry impact |
|
||||||
|
| `CameraShakeClass` | TSubclassOf<UCameraShakeBase> | Per-weapon shake profile |
|
||||||
|
| `ScreenHitIndicatorClass` | TSubclassOf<UUserWidget> | Hit direction HUD overlay |
|
||||||
|
| `HitNumberWidgetClass` | TSubclassOf<UUserWidget> | Floating damage number |
|
||||||
|
| `BulletHolePool` | Array<ABulletHoleActor> | Reusable pooled actors |
|
||||||
|
| `MaxBulletHoles` | Int | Pool limit (e.g. 50) |
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
| Struct | Fields | Description |
|
||||||
|
|--------|--------|-------------|
|
||||||
|
| `FImpactContext` | HitLocation: FVector, HitNormal: FVector, HitMaterial: UPhysicalMaterial, bIsCrit: Bool, Damage: Float, bIsKill: Bool | Full hit data for FX |
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `PlayMuzzleFlash` | — | — | Spawn muzzle particle at socket |
|
||||||
|
| `SpawnTracer` | EndPoint: FVector | — | Line trail particle from muzzle to impact |
|
||||||
|
| `SpawnImpactFX` | Impact: FImpactContext | — | Decal + particle at hit location |
|
||||||
|
| `SpawnBulletHole` | Location: FVector, Rotation: FRotator | AActor | Pooled bullet hole actor |
|
||||||
|
| `PlayFireSound` | — | — | Fire sound with optional tail |
|
||||||
|
| `StopFireSound` | — | — | End tail loop |
|
||||||
|
| `PlayReloadSound` | — | — | Reload start |
|
||||||
|
| `PlayReloadFinishSound` | — | — | Reload complete |
|
||||||
|
| `PlayEmptyClickSound` | — | — | Dry fire |
|
||||||
|
| `PlayMeleeWhoosh` | — | — | Melee attack sound |
|
||||||
|
| `PlayMeleeHitSound` | Impact: FImpactContext | — | Melee impact sound |
|
||||||
|
| `PlayBlockSound` | — | — | Block/parry impact |
|
||||||
|
| `PlayCameraShake` | — | — | Fire camera shake |
|
||||||
|
| `ShowHitNumber` | Damage: Float, Location: FVector, IsCrit: Bool | — | Floating damage widget |
|
||||||
|
| `ShowHitIndicator` | DamageDirection: FVector | — | Screen edge indicator |
|
||||||
|
| `GetImpactContextFromHit` | HitResult: FHitResult | FImpactContext | Extract material, location etc. |
|
||||||
|
| `PlayHitConfirmationSound` | bIsKill: Bool | — | Hit marker or kill sound |
|
||||||
|
| `InitializePool` | — | — | Spawn bullet hole pool |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[OnFire → called by WeaponBase]
|
||||||
|
└─► PlayMuzzleFlash
|
||||||
|
└─► SpawnTracer(EndPoint)
|
||||||
|
└─► PlayFireSound (if full auto: start tail loop)
|
||||||
|
└─► PlayCameraShake
|
||||||
|
|
||||||
|
[OnHit → called by DamageHandler dispatcher]
|
||||||
|
└─► FX Pipeline:
|
||||||
|
ImpactCtx = GetImpactContextFromHit(Hit)
|
||||||
|
If ImpactCtx.bIsCrit: PlayCritImpactFX
|
||||||
|
Else: SpawnImpactFX(ImpactCtx)
|
||||||
|
SpawnBulletHole(ImpactCtx.HitLocation, RotationFromNormal)
|
||||||
|
PlayMeleeHitSound or generic hit sound
|
||||||
|
ShowHitNumber(ImpactCtx.Damage, ImpactCtx.HitLocation, ImpactCtx.bIsCrit)
|
||||||
|
ShowHitIndicator(GetDirectionFromInstigator)
|
||||||
|
If ImpactCtx.bIsKill: PlayKillConfirmation
|
||||||
|
|
||||||
|
[OnAmmoEmpty → called by AmmoComponent dispatcher]
|
||||||
|
└─► PlayEmptyClickSound
|
||||||
|
|
||||||
|
[OnReloadStarted/Completed → called by AmmoComponent dispatcher]
|
||||||
|
└─► PlayReloadSound / PlayReloadFinishSound
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pooled Bullet Hole System
|
||||||
|
|
||||||
|
```
|
||||||
|
[InitializePool]
|
||||||
|
└─► For i = 1 .. MaxBulletHoles:
|
||||||
|
Spawn ABulletHoleActor deactivated
|
||||||
|
Add to BulletHolePool
|
||||||
|
|
||||||
|
[SpawnBulletHole]
|
||||||
|
└─► Find first inactive actor in pool
|
||||||
|
└─► If none → recycle oldest active
|
||||||
|
└─► Set transform, set decal material based on surface
|
||||||
|
└─► Activate, start life timer
|
||||||
|
```
|
||||||
|
|
||||||
|
### Impact Decal Material Mapping
|
||||||
|
|
||||||
|
| Physical Material | Decal Material | Particle System |
|
||||||
|
|-------------------|---------------|-----------------|
|
||||||
|
| Concrete | T_ConcreteDecal | P_ConcreteHit |
|
||||||
|
| Metal | T_MetalDecal | P_MetalSparks |
|
||||||
|
| Wood | T_WoodDecal | P_WoodSplinters |
|
||||||
|
| Flesh | T_FleshDecal | P_BloodSplash |
|
||||||
|
| Glass | T_GlassDecal | P_GlassShards |
|
||||||
|
| Water | T_WaterRipple | P_WaterSplash |
|
||||||
|
| Default | T_BulletHole | P_GenericHit |
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target | Method | Why |
|
||||||
|
|--------|--------|-----|
|
||||||
|
| [`BP_WeaponBase`](49_BP_WeaponBase.md) | Owner | Fire/reload/empty event hooks |
|
||||||
|
| [`BPC_AmmoComponent`](52_BPC_AmmoComponent.md) | Dispatcher listener | Empty click, reload sounds |
|
||||||
|
| [`BPC_DamageHandlerComponent`](53_BPC_DamageHandlerComponent.md) | Dispatcher listener | Hit FX on damage dealt |
|
||||||
|
| [`BPC_PlayerCameraManager`](../02-player/14_BPC_PlayerCameraManager.md) | Get from Owner | Camera shake |
|
||||||
|
| [`AudioManager`](../09-atmosphere/69_BPC_AudioManager.md) | Get Game Instance | Spatial audio for distant sounds |
|
||||||
|
| [`VFXManager`](../09-atmosphere/70_BPC_VFXManager.md) | Get Game Instance | Particle pooling |
|
||||||
|
| HUD Widget | Direct or Dispatcher | Hit marker and damage numbers |
|
||||||
|
| Bullet Hole Pool | Direct | Spawn / recycle |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
- Pure presentation component: swap entire feedback profile by replacing DA_WeaponData FX references
|
||||||
|
- Impact material map driven by physical materials, no hardcoded surface checks
|
||||||
|
- Pooled bullet holes prevent spawn/despawn overhead
|
||||||
|
- Camera shake per weapon allows distinct feel (pistol vs shotgun)
|
||||||
|
- All sounds use distance-based attenuation curves from the audio manager
|
||||||
123
docs/blueprints/08-weapons/72_BPC_DamageReceptionSystem.md
Normal file
123
docs/blueprints/08-weapons/72_BPC_DamageReceptionSystem.md
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
# BPC_DamageReceptionSystem — Damage Reception System
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`I_Damageable`](../01-core/03_I_InterfaceLibrary.md) — Interface on damage targets
|
||||||
|
- [`BP_WeaponBase`](49_BP_WeaponBase.md) — Owner weapon (optional)
|
||||||
|
- [`BPC_HealthSystem`](../02-player/08_BPC_HealthSystem.md) — Damage recipient
|
||||||
|
- [`BPC_CombatFeedbackComponent`](54_BPC_CombatFeedbackComponent.md) — Damage FX forwarding
|
||||||
|
- [`DA_WeaponData`](../14-data-assets/) — Damage/penetration values
|
||||||
|
- `Engine.DamageType` — UE5 damage type system
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Centralized damage calculation and application service. Decouples weapon fire logic from damage math. Handles damage types, armor penetration, critical hit rolls, damage falloff over distance, and damage event logging. Can be attached to weapons, projectiles, or trap actors.
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `DamageMultiplier` | Float | Per-instance modifier (e.g. weak spot) |
|
||||||
|
| `bUseDamageFalloff` | Bool | Enable distance-based damage reduction |
|
||||||
|
| `FalloffStartDistance` | Float | Distance before falloff begins (cm) |
|
||||||
|
| `FalloffEndDistance` | Float | Distance where damage reaches minimum |
|
||||||
|
| `MinDamageFactor` | Float | 0.0–1.0, minimum damage at max range |
|
||||||
|
| `DamageType` | TSubclassOf<UDamageType> | UE5 damage type class |
|
||||||
|
| `LastDamageDealt` | float | Most recent damage output (debug/logging) |
|
||||||
|
| `LastHitActor` | AActor | Most recent recipient |
|
||||||
|
| `bShowDamageNumbers` | Bool | Debug toggle |
|
||||||
|
| `CritChanceOverride` | Float | If > 0, overrides weapon default |
|
||||||
|
| `CritMultiplierOverride` | Float | If > 0, overrides weapon default |
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
| Struct | Fields | Description |
|
||||||
|
|--------|--------|-------------|
|
||||||
|
| `FDamageResult` | DamageDealt: Float, bWasCrit: Bool, bWasBlocked: Bool, DamageTypeTag: FGameplayTag, HitLocation: FVector, HitNormal: FVector | Damage calculation output |
|
||||||
|
| `FDamageRequest` | BaseDamage: Float, Instigator: AActor, DamageCauser: AActor, HitInfo: FHitResult, DamageType: TSubclassOf<UDamageType>, bIsMelee: Bool | Incoming damage data |
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `ProcessDamageRequest` | Request: FDamageRequest | FDamageResult | Main calculation entry |
|
||||||
|
| `ApplyDamageToActor` | Target: AActor, Request: FDamageRequest | Bool | Apply via interface or engine ApplyDamage |
|
||||||
|
| `CalculateDamageFalloff` | BaseDamage: Float, Distance: Float | Float | Linear interpolation between start/end |
|
||||||
|
| `RollForCriticalHit` | CritChance: Float | Bool | Random roll against chance |
|
||||||
|
| `CalculateArmorPenetration` | BaseDamage: Float, ArmorValue: Float | Float | Armor reduction math |
|
||||||
|
| `ApplyWeakSpotMultiplier` | Damage: Float, WeakSpotTag: FGameplayTag | Float | From character weak spot config |
|
||||||
|
| `OnDamageApplied` | Result: FDamageResult | — | Broadcast event |
|
||||||
|
| `GetDistanceToTarget` | Target: AActor | Float | Distance from weapon muzzle to hit |
|
||||||
|
| `LogDamageEvent` | Result: FDamageResult | — | For analytics / combat log |
|
||||||
|
| `SetDamageMultiplier` | Multiplier: Float | — | Runtime override (e.g. buff) |
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired When |
|
||||||
|
|------|-----------|-----------|
|
||||||
|
| `OnDamageDealt` | Target: AActor, Damage: Float, IsCrit: Bool, DamageTypeTag: FGameplayTag | Damage applied successfully |
|
||||||
|
| `OnDamageBlocked` | Target: AActor, BlockedDamage: Float | Damage reduced by armor / block |
|
||||||
|
| `OnCriticalHit` | Target: AActor, Damage: Float | Critical hit confirmed |
|
||||||
|
| `OnWeakSpotHit` | Target: AActor, WeakSpotTag: FGameplayTag | Weak spot damage |
|
||||||
|
| `OnKillConfirmed` | Target: AActor | Target health <= 0 |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[ProcessDamageRequest]
|
||||||
|
└─► CurrentDamage = Request.BaseDamage
|
||||||
|
└─► If bUseDamageFalloff:
|
||||||
|
Distance = GetDistanceToTarget(Request.Instigator)
|
||||||
|
CurrentDamage = CalculateDamageFalloff(CurrentDamage, Distance)
|
||||||
|
└─► If Request.bIsMelee:
|
||||||
|
CurrentDamage *= DamageMultiplier
|
||||||
|
└─► // Crit roll
|
||||||
|
bCrit = RollForCriticalHit(CritChanceOverride > 0 ? CritChanceOverride : WeaponData.CritChance)
|
||||||
|
If bCrit: CurrentDamage *= CritMultiplierOverride > 0 ? CritMultiplierOverride : WeaponData.CritMultiplier
|
||||||
|
└─► // Armor penetration (if target has armor system)
|
||||||
|
If target has component:
|
||||||
|
ArmorValue = target.GetArmorValue()
|
||||||
|
CurrentDamage = CalculateArmorPenetration(CurrentDamage, ArmorValue)
|
||||||
|
Else: Armor reduction not applied
|
||||||
|
└─► // Weak spot
|
||||||
|
If hit bone is weak spot:
|
||||||
|
CurrentDamage = ApplyWeakSpotMultiplier(CurrentDamage, WeakSpotTag)
|
||||||
|
└─► Populate FDamageResult
|
||||||
|
└─► ApplyDamageToActor(Request.Instigator, Result)
|
||||||
|
└─► OnDamageApplied(Result)
|
||||||
|
└─► If target health <= 0: OnKillConfirmed(target)
|
||||||
|
└─► Return Result
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
[ApplyDamageToActor]
|
||||||
|
└─► If target implements I_Damageable:
|
||||||
|
target.I_Damageable.ApplyDamage(Result)
|
||||||
|
└─► Else:
|
||||||
|
target.TakeDamage(Result.DamageDealt, DamageEvent, ...)
|
||||||
|
└─► Return true if successfully applied
|
||||||
|
```
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target | Method | Why |
|
||||||
|
|--------|--------|-----|
|
||||||
|
| [`I_Damageable`](../01-core/03_I_InterfaceLibrary.md) | Interface call | Primary damage path |
|
||||||
|
| [`BPC_HealthSystem`](../02-player/08_BPC_HealthSystem.md) | Get on Target | Direct health modify |
|
||||||
|
| [`BPC_CombatFeedbackComponent`](54_BPC_CombatFeedbackComponent.md) | Get from Weapon Owner | Damage numbers, hit confirmation |
|
||||||
|
| [`DA_WeaponData`](../14-data-assets/) | Direct read | Base damage, crit, falloff params |
|
||||||
|
| Analytics / Stats Tracker | Dispatcher | Damage tracking for stats |
|
||||||
|
| HUD | Dispatcher (via GM) | Damage numbers and death feed |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
- Pure calculation component — no visual or audio feedback
|
||||||
|
- Damage falloff is linear between start and end distance
|
||||||
|
- Armor penetration formula: `FinalDamage = Damage * (100 / (100 + ArmorValue))` (standard UE style)
|
||||||
|
- Weak spots are identified by bone name or gameplay tag on the target actor
|
||||||
|
- Dispatchers allow UI and feedback to react without coupling
|
||||||
|
- Renamed from `BPC_DamageHandlerComponent` to `BPC_DamageReceptionSystem` per Master naming convention.
|
||||||
200
docs/blueprints/08-weapons/73_BPC_DeathCauseTracker.md
Normal file
200
docs/blueprints/08-weapons/73_BPC_DeathCauseTracker.md
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
# BPC_DeathCauseTracker — Death Cause Tracker
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- **Requires:** [`BPC_HealthSystem`](../02-player/08_BPC_HealthSystem.md) — Receives OnDeath dispatcher for recording cause
|
||||||
|
- **Required By:** [`BPC_DeathHandlingSystem`](../05-saveload/BPC_DeathHandlingSystem.md) — Reads LastDeathCause and DeathHistory
|
||||||
|
- **Required By:** [`BPC_RunHistoryTracker`](../05-saveload/BPC_RunHistoryTracker.md) — Reads death history for summary
|
||||||
|
- **Required By:** [`BPC_AdaptiveEnvironmentDirector`](../10-adaptive/BPC_AdaptiveEnvironmentDirector.md) — Reads death patterns for adaptation
|
||||||
|
- **Engine/Plugin Requirements:** GameplayTags (for ChapterTag in DeathRecord)
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Records the cause and context of the player's death for use by the death handling system, adaptive systems, and run summary. Tracks full session death history with cause type, location, instigator, timestamp, and chapter context.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Enums
|
||||||
|
|
||||||
|
```text
|
||||||
|
Enum Name: E_DeathCause
|
||||||
|
(DisplayName = "Death Cause")
|
||||||
|
|
||||||
|
Values:
|
||||||
|
Melee = 0 // Killed by melee attack
|
||||||
|
Projectile = 1 // Killed by projectile/firearm
|
||||||
|
Environmental = 2 // Killed by environment (fall, trap, fire zone)
|
||||||
|
Scripted = 3 // Scripted/cinematic kill
|
||||||
|
Psychic = 4 // Killed by psychic/supernatural force
|
||||||
|
InstantKill = 5 // Instant-kill mechanic
|
||||||
|
Unknown = 6 // Cause cannot be determined
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Structs
|
||||||
|
|
||||||
|
### `S_DeathRecord`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Cause` | E_DeathCause | How the player died |
|
||||||
|
| `Location` | Vector | World position of death |
|
||||||
|
| `Instigator` | Actor | What/who killed the player (nullable) |
|
||||||
|
| `Timestamp` | Float | Game time of death in seconds |
|
||||||
|
| `ChapterTag` | GameplayTag | Which chapter the death occurred in |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
|
||||||
|
### Configuration (Instance Editable, Expose On Spawn)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `MaxDeathHistorySize` | Integer | 50 | DeathTracking | Maximum number of death records to retain |
|
||||||
|
|
||||||
|
### Internal (Private / Protected, No Expose)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `LastDeathCause` | E_DeathCause | Unknown | Internal | Cause of the most recent death |
|
||||||
|
| `LastDeathLocation` | Vector | (0,0,0) | Internal | World position of last death |
|
||||||
|
| `LastDeathInstigator` | Actor | None | Internal | Actor that caused last death |
|
||||||
|
| `DeathHistory` | Array of S_DeathRecord | Empty | Internal | Full session death log |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
### Public Functions
|
||||||
|
|
||||||
|
#### `RecordDeath` → `void`
|
||||||
|
- **Description:** Called when player dies. Stores cause, location, instigator, timestamp, and chapter tag into a new S_DeathRecord.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Cause` | E_DeathCause | How the player died |
|
||||||
|
| `Location` | Vector | World position of death |
|
||||||
|
| `Instigator` | Actor | Killing actor (optional) |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
- **Flow:** Create S_DeathRecord → populate fields → add to DeathHistory → update LastDeath* variables → trim history if over MaxDeathHistorySize → broadcast OnDeathRecorded
|
||||||
|
|
||||||
|
#### `GetLastDeathCause` → `E_DeathCause`
|
||||||
|
- **Description:** Returns the cause of the most recent death.
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `GetLastDeathLocation` → `Vector`
|
||||||
|
- **Description:** Returns the world location of the most recent death.
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `GetLastDeathInstigator` → `Actor`
|
||||||
|
- **Description:** Returns the actor that caused the most recent death (may be None).
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `GetDeathHistory` → `Array of S_DeathRecord`
|
||||||
|
- **Description:** Returns the full death history for this session.
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `GetDeathCount` → `Integer`
|
||||||
|
- **Description:** Returns the total number of deaths this session.
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `ClearDeathHistory` → `void`
|
||||||
|
- **Description:** Empties the death history and resets last death variables. Called on new game.
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Blueprint Authority:** Server only (if multiplayer)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Parameters | Bind Access | Description |
|
||||||
|
|------------|-----------|-------------|-------------|
|
||||||
|
| `OnDeathRecorded` | DeathRecord: S_DeathRecord | Public | New death entry added to history |
|
||||||
|
| `OnFirstDeath` | Cause: E_DeathCause | Public | First death of the session (for tutorials/achievements) |
|
||||||
|
| `OnDeathCountThreshold` | Count: Integer | Public | Death count reaches a configured threshold (e.g., 10, 25, 50) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Overridden Events / Custom Events
|
||||||
|
|
||||||
|
### Event: `OnHealthSystemDeath`
|
||||||
|
- **Description:** Subscribes to [`BPC_HealthSystem`](../02-player/08_BPC_HealthSystem.md)`OnDeath` dispatcher on BeginPlay. Routes damage event data to `RecordDeath`.
|
||||||
|
- **Flow:**
|
||||||
|
1. Extract DeathCause from S_DamageEvent
|
||||||
|
2. Map DamageType to E_DeathCause (Physical→Melee or Projectile, Fire→Environmental, etc.)
|
||||||
|
3. Get player world location
|
||||||
|
4. Call RecordDeath(Cause, Location, Instigator)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blueprint Graph Logic Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[BeginPlay] --> B[Subscribe to BPC_HealthSystem.OnDeath]
|
||||||
|
B --> C[Idle — waiting for death]
|
||||||
|
C --> D{OnDeath dispatcher fires}
|
||||||
|
D --> E[Map DamageType → E_DeathCause]
|
||||||
|
E --> F[Get player world location]
|
||||||
|
F --> G[Get GS_CoreGameState.ActiveChapterTag]
|
||||||
|
G --> H[Create S_DeathRecord]
|
||||||
|
H --> I[Add to DeathHistory array]
|
||||||
|
I --> J[Update LastDeathCause / Location / Instigator]
|
||||||
|
J --> K{DeathHistory count == 1?}
|
||||||
|
K -->|Yes| L[Broadcast OnFirstDeath]
|
||||||
|
K -->|No| M[Check threshold milestones]
|
||||||
|
L --> N[Broadcast OnDeathRecorded]
|
||||||
|
M --> N
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Communication Matrix
|
||||||
|
|
||||||
|
| Who Talks | How | What Is Sent |
|
||||||
|
|-----------|-----|-------------|
|
||||||
|
| [`BPC_HealthSystem`](../02-player/08_BPC_HealthSystem.md) | Dispatcher (`OnDeath`) | `S_DamageEvent` (read Type, Instigator, HitLocation) |
|
||||||
|
| [`BPC_DeathHandlingSystem`](../05-saveload/BPC_DeathHandlingSystem.md) | Direct read | `LastDeathCause`, `LastDeathLocation`, `LastDeathInstigator` |
|
||||||
|
| [`BPC_RunHistoryTracker`](../05-saveload/BPC_RunHistoryTracker.md) | Direct read | `DeathHistory` array |
|
||||||
|
| [`BPC_AdaptiveEnvironmentDirector`](../10-adaptive/BPC_AdaptiveEnvironmentDirector.md) | Direct read | `DeathHistory` for death pattern analysis |
|
||||||
|
| [`SS_AchievementSystem`](../11-meta/SS_AchievementSystem.md) | Dispatcher (`OnDeathRecorded`) | Death count and cause for achievement checks |
|
||||||
|
| [`GS_CoreGameState`](../01-core/06_GS_CoreGameState.md) | Direct read | `ActiveChapterTag` for death record context |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Validation / Testing Checklist
|
||||||
|
|
||||||
|
- [ ] E_DeathCause enum has all 7 values defined
|
||||||
|
- [ ] S_DeathRecord struct contains all 5 fields
|
||||||
|
- [ ] RecordDeath correctly appends to DeathHistory and updates LastDeath* variables
|
||||||
|
- [ ] DeathHistory trimmed when exceeding MaxDeathHistorySize
|
||||||
|
- [ ] OnFirstDeath fires only on the first death of the session
|
||||||
|
- [ ] ClearDeathHistory resets all state for new game
|
||||||
|
- [ ] Edge case: Instigator can be None (environmental/fall deaths)
|
||||||
|
- [ ] Edge case: rapid successive deaths all recorded independently
|
||||||
|
- [ ] Edge case: death during cutscene with no damage event maps to Scripted or Unknown
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Reuse Notes
|
||||||
|
|
||||||
|
- E_DeathCause can be extended per project (add new enum values)
|
||||||
|
- S_DeathRecord fields are generic enough for any genre
|
||||||
|
- MaxDeathHistorySize prevents unbounded memory growth in long sessions
|
||||||
|
- Death history is not persisted to save file by default — wire to I_Persistable if cross-session tracking needed
|
||||||
|
- ChapterTag in death record enables chapter-based analytics and adaptive tuning
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Specification based on Master Section 5.8, line 1836.*
|
||||||
105
docs/blueprints/08-weapons/74_BPC_FirearmSystem.md
Normal file
105
docs/blueprints/08-weapons/74_BPC_FirearmSystem.md
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
# BPC_FirearmSystem — Firearm Component
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`BPC_AmmoComponent`](52_BPC_AmmoComponent.md) — Ammo management
|
||||||
|
- [`BPC_DamageReceptionSystem`](BPC_DamageReceptionSystem.md) — Damage application
|
||||||
|
- [`BPC_CombatFeedbackComponent`](54_BPC_CombatFeedbackComponent.md) — Visual/audio feedback
|
||||||
|
- [`BPC_CameraStateLayer`](../02-player/14_BPC_CameraStateLayer.md) — Recoil & ADS
|
||||||
|
- [`I_Damageable`](../01-core/03_I_InterfaceLibrary.md) — Interface for hit targets
|
||||||
|
- [`DA_WeaponData`](../14-data-assets/) — Weapon definition
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Ranged weapon specialization. Handles line trace / projectile fire, weapon spread, bullet drop simulation, and multi-hit logic. Used by firearms (pistols, rifles, shotguns) and charge weapons.
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `FireType` | EFireType | Hitscan, Projectile, Hybrid |
|
||||||
|
| `ShootEffectType` | EShotEffect | Single, Spread, Charge |
|
||||||
|
| `Range` | Float | Max effective range (cm) |
|
||||||
|
| `SpreadAngle` | Float | Base spread in degrees |
|
||||||
|
| `SpreadRecoveryRate` | Float | Per-second recovery from spread |
|
||||||
|
| `CurrentSpread` | Float | Running spread (fired recently) |
|
||||||
|
| `MaxSpread` | Float | Spread cap |
|
||||||
|
| `BulletsPerShot` | Int | e.g. shotgun = 8 |
|
||||||
|
| `ProjectileClass` | Actor (Blueprint) | Only for Projectile / Hybrid fire types |
|
||||||
|
| `FireSocket` | FName | Muzzle attachment point |
|
||||||
|
| `MuzzleLocation` | USceneComponent | Muzzle transform reference |
|
||||||
|
| `HitScanImpacts` | Array<FHitResult> | Notify component for decals |
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
|
||||||
|
| Enum | Values | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| `EFireType` | Hitscan, Projectile, Hybrid | Ray → Instant or Bullet actor |
|
||||||
|
| `EShotEffect` | SingleShot, SpreadShot, ChargeShot | Fire pattern |
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `PerformHitscanFire` | EWeaponFireMode | Array<FHitResult> | Line trace from camera to crosshair; no bullet |
|
||||||
|
| `SpawnProjectile` | Direction: FVector | Actor | Spawn projectile actor with initial velocity |
|
||||||
|
| `CalculateSpread` | — | FVector | Apply random offset within CurrentSpread cone |
|
||||||
|
| `OnFire_Implementation` | — | — | Override of WeaponBase::OnFire; runs hit scan or spawns projectile |
|
||||||
|
| `ApplyRecoil` | — | — | Add camera rotation via CameraStateLayer |
|
||||||
|
| `ConsumeAmmo` | — | — | Delegate to BPC_AmmoComponent |
|
||||||
|
| `GetEffectiveRange` | — | Float | Return weapon range from data asset |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[OnFire]
|
||||||
|
└─► If FireType == Hitscan
|
||||||
|
For i = 1 .. BulletsPerShot:
|
||||||
|
AimDir = OwnerCamera.ForwardVector + CalculateSpread
|
||||||
|
PerformHitscanFire returns HitResults
|
||||||
|
For each hit:
|
||||||
|
if actor implements I_Damageable:
|
||||||
|
BPC_DamageReceptionSystem.ApplyDamage(WeaponData.Damage, Hit)
|
||||||
|
BPC_CombatFeedbackComponent.SpawnImpactFX(Hit)
|
||||||
|
└─► If FireType == Projectile
|
||||||
|
For i = 1 .. BulletsPerShot:
|
||||||
|
AimDir = OwnerCamera.ForwardVector + CalculateSpread
|
||||||
|
SpawnProjectile(AimDir * WeaponData.ProjectileSpeed)
|
||||||
|
└─► If FireType == Hybrid
|
||||||
|
Perform hitscan + spawn projectile for missed overlap area
|
||||||
|
└─► ApplyRecoil
|
||||||
|
└─► ConsumeAmmo
|
||||||
|
└─► BPC_CombatFeedbackComponent.PlayMuzzleFlash
|
||||||
|
└─► BPC_CombatFeedbackComponent.PlayFireSound
|
||||||
|
|
||||||
|
[CalculateSpread]
|
||||||
|
└─► CurrentSpread = FMath::Min(CurrentSpread + WeaponData.SpreadPerShot, MaxSpread)
|
||||||
|
└─► Return random vector in cone of CurrentSpread degrees
|
||||||
|
|
||||||
|
[OnFireStopped — super]
|
||||||
|
└─► Begin spread recovery: CurrentSpread → 0 over SpreadRecoveryRate seconds
|
||||||
|
└─► BPC_CombatFeedbackComponent.StopFireSound
|
||||||
|
```
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target | Method | Why |
|
||||||
|
|--------|--------|-----|
|
||||||
|
| [`BPC_AmmoComponent`](52_BPC_AmmoComponent.md) | Get Component | Ammo check / consume |
|
||||||
|
| [`BPC_DamageReceptionSystem`](BPC_DamageReceptionSystem.md) | Get Component | Apply damage to hit targets |
|
||||||
|
| [`BPC_CombatFeedbackComponent`](54_BPC_CombatFeedbackComponent.md) | Get Component | Muzzle flash, tracers, impact FX |
|
||||||
|
| [`BPC_CameraStateLayer`](../02-player/14_BPC_CameraStateLayer.md) | Get from Owner | Recoil animation |
|
||||||
|
| Owner WeaponAnimBP | Animation Instance | Fire / Reload animations |
|
||||||
|
| HUD Widget | Dispatcher | Ammo count update |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
- Hit scan fire uses camera forward, not muzzle forward, for accuracy. Muzzle is for FX only.
|
||||||
|
- Spread is cumulative while firing, resets on recovery timer.
|
||||||
|
- Projectile type requires a child BP implementing `AP_BaseProjectile` with velocity and lifetime.
|
||||||
|
- Renamed from `BP_RangedWeapon` to `BPC_FirearmSystem` per Master naming convention.
|
||||||
|
- Cross-references updated: `BPC_DamageHandlerComponent` → `BPC_DamageReceptionSystem`, `BPC_PlayerCameraManager` → `BPC_CameraStateLayer`, `DA_WeaponData` → `14-data-assets/`.
|
||||||
192
docs/blueprints/08-weapons/75_BPC_HitReactionSystem.md
Normal file
192
docs/blueprints/08-weapons/75_BPC_HitReactionSystem.md
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
# BPC_HitReactionSystem — Hit Reaction System
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- **Requires:** [`BPC_HealthSystem`](../02-player/08_BPC_HealthSystem.md) — Receives damage events
|
||||||
|
- **Requires:** [`BPC_CameraStateLayer`](../02-player/14_BPC_CameraStateLayer.md) — Camera trauma effects
|
||||||
|
- **Requires:** [`S_DamageEvent`](../02-player/08_BPC_HealthSystem.md) — Damage event struct (E_DamageType, Amount, HitLocation, HitNormal)
|
||||||
|
- **Requires:** [`E_DamageType`](../01-core/03_I_InterfaceLibrary.md) — Damage type enum for montage selection
|
||||||
|
- **Required By:** [`BPC_AdaptiveEnvironmentDirector`](../10-adaptive/BPC_AdaptiveEnvironmentDirector.md) — Reads trauma intensity for atmosphere
|
||||||
|
- **Engine/Plugin Requirements:** Animation Blueprint (ABP_PlayerBody), GASP animation hooks
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Drives physical hit reaction behaviour: flinch animations, ragdoll blends, and camera trauma. Processes incoming damage events and triggers appropriate reaction animations based on damage magnitude, direction, and type.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Enums
|
||||||
|
|
||||||
|
*Uses `E_DamageType` from [`BPC_HealthSystem`](../02-player/08_BPC_HealthSystem.md) — Physical, Fire, Psychic, Environmental, InstantKill, Scripted.*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Structs
|
||||||
|
|
||||||
|
*No new structs defined. Uses `S_DamageEvent` from [`BPC_HealthSystem`](../02-player/08_BPC_HealthSystem.md).*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
|
||||||
|
### Configuration (Instance Editable, Expose On Spawn)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `FlinchThreshold` | Float | 5.0 | HitReaction | Minimum damage to trigger flinch animation |
|
||||||
|
| `RagdollThreshold` | Float | 50.0 | HitReaction | Damage amount that triggers ragdoll blend |
|
||||||
|
| `TraumaDecayRate` | Float | 2.0 | HitReaction | Camera trauma points recovered per second |
|
||||||
|
| `HitReactionMontages` | Map (E_DamageType → AnimMontage) | Empty | HitReaction | Per-damage-type hit reaction animations |
|
||||||
|
|
||||||
|
### Internal (Private / Protected, No Expose)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `CurrentTrauma` | Float | 0.0 | Internal | Accumulated camera trauma level |
|
||||||
|
| `bIsReacting` | Bool | False | Internal | Reaction animation currently playing |
|
||||||
|
| `LastDamageDirection` | Vector | (0,0,0) | Internal | Direction of most recent damage for animation blend |
|
||||||
|
| `NextReactionIndex` | Integer | 0 | Internal | Cycles through available montages per type for variety |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
### Public Functions
|
||||||
|
|
||||||
|
#### `ProcessHitReaction` → `void`
|
||||||
|
- **Description:** Main entry point. Called when player receives damage. Determines reaction type based on damage amount vs thresholds.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `DamageEvent` | S_DamageEvent | Full damage context from HealthSystem |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
- **Flow:** Checks thresholds → selects montage → plays animation → applies trauma → broadcasts dispatchers
|
||||||
|
|
||||||
|
#### `ApplyTrauma` → `void`
|
||||||
|
- **Description:** Adds camera trauma based on damage amount.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Amount` | Float | Trauma points to add |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `GetCurrentTrauma` → `Float`
|
||||||
|
- **Description:** Returns current accumulated trauma level (0–100).
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `ResetTrauma` → `void`
|
||||||
|
- **Description:** Clears all accumulated camera trauma to zero.
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
### Protected / Private Functions
|
||||||
|
|
||||||
|
#### `SelectHitReactionMontage` → `AnimMontage`
|
||||||
|
- **Description:** Picks a hit reaction montage based on damage type and cycles through available options for variety.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `DamageType` | E_DamageType | Type of damage received |
|
||||||
|
| `DamageAmount` | Float | Magnitude of damage |
|
||||||
|
- **Flow:** Look up HitReactionMontages map by damage type → cycle through entries → return selected montage
|
||||||
|
|
||||||
|
#### `TickTraumaDecay` → `void`
|
||||||
|
- **Description:** Called on tick to decay camera trauma toward zero at TraumaDecayRate.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `DeltaTime` | Float | Frame delta time |
|
||||||
|
- **Flow:** `CurrentTrauma = FMath::FInterpTo(CurrentTrauma, 0.0, DeltaTime, TraumaDecayRate)`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Parameters | Bind Access | Description |
|
||||||
|
|------------|-----------|-------------|-------------|
|
||||||
|
| `OnFlinchTriggered` | DamageType: E_DamageType, Amount: Float | Public | Flinch animation fired (damage below ragdoll threshold) |
|
||||||
|
| `OnRagdollBlend` | Amount: Float, Direction: Vector | Public | Ragdoll blend triggered (damage exceeded ragdoll threshold) |
|
||||||
|
| `OnTraumaChanged` | CurrentTrauma: Float | Public | Trauma level changed (for camera/screen effects) |
|
||||||
|
| `OnHitReactionComplete` | DamageType: E_DamageType | Public | Reaction animation finished playing |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Overridden Events / Custom Events
|
||||||
|
|
||||||
|
### Event: `ReceiveDamage`
|
||||||
|
- **Description:** Called by [`BPC_DamageReceptionSystem`](BPC_DamageReceptionSystem.md) when player takes damage. Routes to `ProcessHitReaction`.
|
||||||
|
- **Flow:**
|
||||||
|
1. Extract damage amount and type from DamageEvent
|
||||||
|
2. If DamageAmount >= FlinchThreshold → ProcessHitReaction
|
||||||
|
3. If DamageAmount >= RagdollThreshold → trigger ragdoll blend instead
|
||||||
|
4. Calculate LastDamageDirection from HitLocation relative to player
|
||||||
|
5. ApplyTrauma proportional to damage amount
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blueprint Graph Logic Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[ReceiveDamage called] --> B{Damage >= RagdollThreshold?}
|
||||||
|
B -->|Yes| C[Disable input temporarily]
|
||||||
|
B -->|No| D{Damage >= FlinchThreshold?}
|
||||||
|
C --> E[Play ragdoll physics blend]
|
||||||
|
D -->|Yes| F[SelectHitReactionMontage by DamageType]
|
||||||
|
D -->|No| G[Skip reaction, only apply trauma]
|
||||||
|
F --> H[Play montage with directional blend]
|
||||||
|
H --> I[Broadcast OnFlinchTriggered]
|
||||||
|
I --> J[Wait for montage end]
|
||||||
|
J --> K[Broadcast OnHitReactionComplete]
|
||||||
|
E --> L[Set bIsReacting = true]
|
||||||
|
L --> M[Wait for ragdoll timer]
|
||||||
|
M --> N[Recover from ragdoll]
|
||||||
|
N --> K
|
||||||
|
G --> O[ApplyTrauma proportional to damage]
|
||||||
|
K --> O
|
||||||
|
O --> P[Broadcast OnTraumaChanged]
|
||||||
|
P --> Q[Camera system reads trauma]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Communication Matrix
|
||||||
|
|
||||||
|
| Who Talks | How | What Is Sent |
|
||||||
|
|-----------|-----|-------------|
|
||||||
|
| [`BPC_DamageReceptionSystem`](BPC_DamageReceptionSystem.md) | Direct call | `S_DamageEvent` |
|
||||||
|
| [`BPC_CameraStateLayer`](../02-player/14_BPC_CameraStateLayer.md) | Dispatcher (`OnTraumaChanged`) | `CurrentTrauma: Float` |
|
||||||
|
| [`WBP_ScreenEffectController`](../06-ui/WBP_ScreenEffectController.md) | Dispatcher (`OnTraumaChanged`) | `CurrentTrauma: Float` |
|
||||||
|
| [`ABP_PlayerBody`](../02-player/13_BPC_EmbodimentSystem.md) | Direct (anim var write) | `LastDamageDirection`, reaction montage reference |
|
||||||
|
| [`BPC_AdaptiveEnvironmentDirector`](../10-adaptive/BPC_AdaptiveEnvironmentDirector.md) | Direct read | `CurrentTrauma` via getter |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Validation / Testing Checklist
|
||||||
|
|
||||||
|
- [ ] FlinchThreshold: damage at 5.0+ triggers flinch animation
|
||||||
|
- [ ] RagdollThreshold: damage at 50.0+ triggers ragdoll blend
|
||||||
|
- [ ] TraumaDecayRate: trauma reduces to zero over time when no damage received
|
||||||
|
- [ ] HitReactionMontages map: correct montage plays per E_DamageType
|
||||||
|
- [ ] Edge case: zero damage events do not trigger any reaction
|
||||||
|
- [ ] Edge case: rapid succession hits cycle through montage variations
|
||||||
|
- [ ] Edge case: death event (HealthSystem OnDeath) skips flinch, goes directly to ragdoll
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Reuse Notes
|
||||||
|
|
||||||
|
- FlinchThreshold and RagdollThreshold can be tuned per project for desired combat feel
|
||||||
|
- HitReactionMontages map uses E_DamageType as key — add new damage types without modifying core logic
|
||||||
|
- Trauma system is separate from camera shake — trauma drives persistent wobble, shake is transient
|
||||||
|
- For non-combat projects, set FlinchThreshold very high (e.g., 9999) to disable reactions entirely
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Specification based on Master Section 5.7, line 1821.*
|
||||||
134
docs/blueprints/08-weapons/76_BPC_MeleeSystem.md
Normal file
134
docs/blueprints/08-weapons/76_BPC_MeleeSystem.md
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
# BPC_MeleeSystem — Melee Weapon Component
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`BPC_DamageReceptionSystem`](BPC_DamageReceptionSystem.md)
|
||||||
|
- [`BPC_CombatFeedbackComponent`](54_BPC_CombatFeedbackComponent.md)
|
||||||
|
- [`I_Damageable`](../01-core/03_I_InterfaceLibrary.md)
|
||||||
|
- [`DA_WeaponData`](../14-data-assets/)
|
||||||
|
- Owner Character Animation Blueprint
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Melee weapon specialization. Handles swing detection, hitbox overlap checking during windup / active frames, and combo chain sequencing. Supports light attacks, heavy attacks, charge attacks, and block/parry.
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `ComboSection` | FName | Active montage section for combo |
|
||||||
|
| `ComboStep` | Int | Current combo step index |
|
||||||
|
| `bCanCombo` | Bool | True during window after hit or end of swing |
|
||||||
|
| `ComboWindowDuration` | Float | Seconds to chain next attack |
|
||||||
|
| `SwingMontage` | UAnimMontage | Full melee animation set |
|
||||||
|
| `HitDetectionCollision` | UCapsuleComponent | Overlap collision for hit detection |
|
||||||
|
| `bHitRegistered` | Bool | Prevents multi-hit on same swing |
|
||||||
|
| `HitActors` | Array<AActor> | Already hit this swing |
|
||||||
|
| `SwingPhase` | ESwingPhase | Windup / Active / Recovery / Idle |
|
||||||
|
| `ChargeDuration` | Float | Hold time for charged heavy attack |
|
||||||
|
| `bIsBlocking` | Bool | Currently blocking stance |
|
||||||
|
| `ParryWindow` | Float | Active parry frames in seconds |
|
||||||
|
| `bParryActive` | Bool | Within parry active window |
|
||||||
|
| `StaggerDuration` | Float | Hit reaction stun (self or target) |
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
|
||||||
|
| Enum | Values | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| `ESwingPhase` | Idle, WindUp, Active, Recovery, ParryWindow | Attack animation phase |
|
||||||
|
| `EAttackType` | LightAttack, HeavyAttack, ChargeAttack, SprintAttack, ParryRiposte | Swing variant |
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `StartSwing` | AttackType: EAttackType | — | Play montage section, set SwingPhase |
|
||||||
|
| `OnSwingWindup` | — | — | Begin hit detection |
|
||||||
|
| `OnSwingActive` | — | — | Enable HitDetectionCollision overlap |
|
||||||
|
| `OnSwingHit` | HitResult: FHitResult | — | Single hit registration |
|
||||||
|
| `OnSwingRecovery` | — | — | Disable hit collision, check combo window |
|
||||||
|
| `OnSwingComplete` | — | — | Return to Idle |
|
||||||
|
| `StartBlock` | — | — | Play blocking animation |
|
||||||
|
| `EndBlock` | — | — | Release block stance |
|
||||||
|
| `ParryCheck` | IncomingAttack | Bool | If parry window active → counter |
|
||||||
|
| `GetComboStep` | — | Int | Return current combo index |
|
||||||
|
| `ResetCombo` | — | — | Clear combo step counter |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[Combo Chain Logic]
|
||||||
|
LightAttackInput →
|
||||||
|
If SwingPhase == Idle:
|
||||||
|
ComboStep = 0
|
||||||
|
StartSwing(LightAttack)
|
||||||
|
ElseIf SwingPhase == Active || SwingPhase == Recovery:
|
||||||
|
If bCanCombo && ComboStep < MaxComboLength:
|
||||||
|
ComboStep++
|
||||||
|
StartSwing(LightAttack) — next section of montage
|
||||||
|
|
||||||
|
[OnSwingActive — called by Animation Notify]
|
||||||
|
└─► HitDetectionCollision.SetCollisionEnabled(QueryOnly)
|
||||||
|
└─► On overlap → OnSwingHit
|
||||||
|
|
||||||
|
[OnSwingHit]
|
||||||
|
└─► If HitActor already in HitActors → return
|
||||||
|
└─► Add HitActor to HitActors
|
||||||
|
└─► If HitActor implements I_Damageable:
|
||||||
|
BPC_DamageReceptionSystem.ApplyMeleeDamage(
|
||||||
|
Damage = WeaponData.Damage * AttackTypeMultiplier,
|
||||||
|
HitActor,
|
||||||
|
HitLocation,
|
||||||
|
ImpulseDirection
|
||||||
|
)
|
||||||
|
└─► BPC_CombatFeedbackComponent.PlayHitFX(HitResult)
|
||||||
|
└─► ApplyStagger(HitActor)
|
||||||
|
|
||||||
|
[OnSwingRecovery — called by Animation Notify]
|
||||||
|
└─► HitDetectionCollision.SetCollisionEnabled(NoCollision)
|
||||||
|
└─► HitActors.Empty
|
||||||
|
└─► Set bCanCombo = true
|
||||||
|
└─► Start timer for ComboWindowDuration
|
||||||
|
└─► If no combo input received → OnSwingComplete
|
||||||
|
|
||||||
|
[OnSwingComplete]
|
||||||
|
└─► SwingPhase = Idle
|
||||||
|
└─► bCanCombo = false
|
||||||
|
└─► ComboStep = 0
|
||||||
|
|
||||||
|
[StartBlock]
|
||||||
|
└─► bIsBlocking = true
|
||||||
|
└─► Play block montage loop section
|
||||||
|
└─► Owner movement speed reduced
|
||||||
|
|
||||||
|
[ParryCheck]
|
||||||
|
└─► If bParryActive && incoming swing phase == Active:
|
||||||
|
Play parry riposte animation
|
||||||
|
Stagger incoming attacker
|
||||||
|
Return true
|
||||||
|
└─► Return false
|
||||||
|
```
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target | Method | Why |
|
||||||
|
|--------|--------|-----|
|
||||||
|
| [`BPC_DamageReceptionSystem`](BPC_DamageReceptionSystem.md) | Get Component | Apply melee damage and stagger |
|
||||||
|
| [`BPC_CombatFeedbackComponent`](54_BPC_CombatFeedbackComponent.md) | Get Component | Swing whoosh, hit impact, block sparks |
|
||||||
|
| Owner Animation Blueprint | Direct | Query SwingPhase for animation state |
|
||||||
|
| Owner Character Movement | Direct | Reduce speed during block |
|
||||||
|
| Owner Character Input | Direct | Light / Heavy / Block input mapping |
|
||||||
|
| [`I_Damageable`](../01-core/03_I_InterfaceLibrary.md) | Interface | Damage application on hit actors |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
- Uses animation notifies for phase transitions (Notify_Windup, Notify_Active, Notify_Recovery, Notify_CanCombo, Notify_ParryWindow).
|
||||||
|
- Combo system uses montage section names "Combo_1", "Combo_2", etc.
|
||||||
|
- Hit collision is a simple box or capsule that exists only during Active phase.
|
||||||
|
- Block reduces incoming damage via DamageReception; parry reflects stagger.
|
||||||
|
- Renamed from `BP_MeleeWeapon` to `BPC_MeleeSystem` per Master naming convention.
|
||||||
|
- Cross-references updated: `BPC_DamageHandlerComponent` → `BPC_DamageReceptionSystem`.
|
||||||
52
docs/blueprints/08-weapons/77_BPC_RecoilSystem.md
Normal file
52
docs/blueprints/08-weapons/77_BPC_RecoilSystem.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# BPC_RecoilSystem — Actor Component
|
||||||
|
|
||||||
|
**File:** [`Content/Framework/Weapons/BPC_RecoilSystem`](Content/Framework/Weapons/BPC_RecoilSystem.uasset)
|
||||||
|
**Parent Class:** `UActorComponent`
|
||||||
|
**Dependencies:** [`DA_WeaponData`](49_BP_WeaponBase.md), [`BPC_FirearmSystem`](BPC_FirearmSystem.md), [`BPC_CameraStateLayer`](../02-player/14_BPC_CameraStateLayer.md)
|
||||||
|
|
||||||
|
**Purpose:** Handles procedural recoil: camera kick, recovery curve, and ADS recoil reduction. Works with `BPC_FirearmSystem` to apply per-weapon recoil patterns.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `RecoilPattern` | CurveVector | Per-weapon kick pattern (X=pitch, Y=yaw) |
|
||||||
|
| `RecoverySpeed` | Float | How fast camera returns to rest |
|
||||||
|
| `ADSRecoilMultiplier` | Float | Recoil reduction while aiming (0.3-0.7) |
|
||||||
|
| `CurrentRecoilOffset` | Vector2D | Accumulated recoil offset |
|
||||||
|
| `bIsRecovering` | Bool | Recovery in progress |
|
||||||
|
|
||||||
|
## Functions / Events
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | What it does |
|
||||||
|
|------|--------|---------|--------------|
|
||||||
|
| `ApplyRecoil` | ShotCount: Integer, bADS: Bool | — | Applies recoil kick based on weapon pattern |
|
||||||
|
| `ResetRecoil` | — | — | Clears accumulated offset |
|
||||||
|
| `TickRecovery` | DeltaTime: Float | — | Lerps CurrentRecoilOffset back toward zero |
|
||||||
|
| `SetRecoilPattern` | Pattern: CurveVector | — | Loads per-weapon recoil data from DA_WeaponData |
|
||||||
|
|
||||||
|
## Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[FirearmSystem fires] -> ApplyRecoil(shotCount, bADS)
|
||||||
|
-> Look up RecoilPattern value at shotCount index
|
||||||
|
-> Apply ADSRecoilMultiplier if bADS
|
||||||
|
-> Add to CurrentRecoilOffset
|
||||||
|
-> Push offset to BPC_CameraStateLayer
|
||||||
|
-> Start recovery timer on tick
|
||||||
|
```
|
||||||
|
|
||||||
|
## Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|---------------|--------|-----|
|
||||||
|
| `BPC_FirearmSystem` | Direct call from ApplyRecoilRequest | Receives fire events |
|
||||||
|
| `BPC_CameraStateLayer` | Direct ApplyFOVModifier | Camera kick application |
|
||||||
|
|
||||||
|
## Reuse Notes
|
||||||
|
|
||||||
|
- RecoilPattern is a CurveVector from DA_WeaponData — swap per weapon
|
||||||
|
- For hitscan weapons, call ApplyRecoil on every shot. For projectile, call on fire release
|
||||||
|
- Recovery runs on tick when bIsRecovering; disable tick at rest for performance
|
||||||
62
docs/blueprints/08-weapons/78_BPC_ReloadSystem.md
Normal file
62
docs/blueprints/08-weapons/78_BPC_ReloadSystem.md
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
# BPC_ReloadSystem — Actor Component
|
||||||
|
|
||||||
|
**File:** [`Content/Framework/Weapons/BPC_ReloadSystem`](Content/Framework/Weapons/BPC_ReloadSystem.uasset)
|
||||||
|
**Parent Class:** `UActorComponent`
|
||||||
|
**Dependencies:** [`DA_WeaponData`](49_BP_WeaponBase.md), [`BPC_FirearmSystem`](BPC_FirearmSystem.md), [`BPC_AmmoResourceSystem`](../04-inventory/BPC_KeyItemSystem.md)
|
||||||
|
|
||||||
|
**Purpose:** Manages reload animations, timing, and ammo transfer from pool to weapon magazine. Supports tactical reload, partial reload, and reload interruption.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `bIsReloading` | Bool | Reload in progress |
|
||||||
|
| `ReloadMontage` | AnimMontage | Per-weapon reload animation |
|
||||||
|
| `AmmoTypeTag` | GameplayTag | Which ammo pool to draw from |
|
||||||
|
| `bPartialReload` | Bool | Whether partial reload is supported |
|
||||||
|
| `RoundsToAdd` | Integer | How many rounds to transfer on reload complete |
|
||||||
|
|
||||||
|
## Functions / Events
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | What it does |
|
||||||
|
|------|--------|---------|--------------|
|
||||||
|
| `RequestReload` | — | — | Player presses reload; checks if reload needed |
|
||||||
|
| `BeginReload` | — | — | Plays reload montage, locks fire input |
|
||||||
|
| `CompleteReload` | — | — | Transfers ammo from pool to magazine |
|
||||||
|
| `InterruptReload` | — | — | Cancels reload, retains partial rounds if configured |
|
||||||
|
| `CanReload` | — | Bool | Checks magazine not full AND ammo pool has rounds |
|
||||||
|
|
||||||
|
## Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired when |
|
||||||
|
|------|-----------|-----------|
|
||||||
|
| `OnReloadStarted` | — | Reload begins |
|
||||||
|
| `OnReloadCompleted` | RoundsAdded: Integer | Reload finishes successfully |
|
||||||
|
| `OnReloadInterrupted` | RoundsAdded: Integer | Reload cancelled mid-way |
|
||||||
|
| `OnReloadFailed` | — | Cannot reload (full mag or no ammo) |
|
||||||
|
|
||||||
|
## Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[Player presses Reload] -> CanReload?
|
||||||
|
-> Yes: BeginReload -> Play ReloadMontage -> bIsReloading = true -> Lock fire input
|
||||||
|
-> Animation Notify "TransferRounds" -> CompleteReload -> Consume ammo from pool
|
||||||
|
-> Unlock fire input -> Broadcast OnReloadCompleted
|
||||||
|
-> No: Broadcast OnReloadFailed
|
||||||
|
```
|
||||||
|
|
||||||
|
## Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|---------------|--------|-----|
|
||||||
|
| `BPC_FirearmSystem` | Direct LockFire / UnlockFire | Prevent firing during reload |
|
||||||
|
| `BPC_AmmoResourceSystem` | Direct ConsumeAmmo | Draw rounds from ammo pool |
|
||||||
|
| `ABP_PlayerBody` | Animation Notify events | Reload animation timing |
|
||||||
|
|
||||||
|
## Reuse Notes
|
||||||
|
|
||||||
|
- ReloadMontage from DA_WeaponData per weapon; swap for each weapon type
|
||||||
|
- Partial reload enabled per weapon (e.g. shotguns use individual shell reloads)
|
||||||
|
- AmmoTypeTag links to correct ammo pool in BPC_AmmoResourceSystem
|
||||||
59
docs/blueprints/08-weapons/79_BPC_ShieldDefenseSystem.md
Normal file
59
docs/blueprints/08-weapons/79_BPC_ShieldDefenseSystem.md
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# BPC_ShieldDefenseSystem — Actor Component
|
||||||
|
|
||||||
|
**File:** [`Content/Framework/Weapons/BPC_ShieldDefenseSystem`](Content/Framework/Weapons/BPC_ShieldDefenseSystem.uasset)
|
||||||
|
**Parent Class:** `UActorComponent`
|
||||||
|
**Dependencies:** [`BPC_DamageReceptionSystem`](BPC_DamageReceptionSystem.md), [`BPC_StaminaSystem`](../02-player/09_BPC_StaminaSystem.md)
|
||||||
|
|
||||||
|
**Purpose:** Manages defensive tools: shields, blocking, parrying. Works in both melee and ranged defense contexts.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `bIsBlocking` | Bool | Block active |
|
||||||
|
| `BlockDamageReduction` | Float | % reduction while blocking (0.0–1.0) |
|
||||||
|
| `ParryWindow` | Float | Perfect parry timing window in seconds |
|
||||||
|
| `ShieldDurability` | Float | Current shield durability (0 = broken) |
|
||||||
|
| `MaxShieldDurability` | Float | Maximum durability |
|
||||||
|
| `BlockAngle` | Float | Max angle from forward vector for block to cover |
|
||||||
|
| `StaminaCostPerBlock` | Float | Stamina drain per blocked hit |
|
||||||
|
| `bCanParry` | Bool | Whether parry mechanic is enabled |
|
||||||
|
|
||||||
|
## Functions / Events
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | What it does |
|
||||||
|
|------|--------|---------|--------------|
|
||||||
|
| `BeginBlock` | — | — | Raises shield, starts blocking |
|
||||||
|
| `EndBlock` | — | — | Lowers shield, ends blocking |
|
||||||
|
| `ProcessHit` | Damage: S_DamageEvent | Bool (blocked) | Evaluates if hit was blocked or parried |
|
||||||
|
| `TryParry` | — | Bool | Checks if parry window is active |
|
||||||
|
| `BreakShield` | — | — | Reduces durability to 0, triggers break anim |
|
||||||
|
|
||||||
|
## Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired when |
|
||||||
|
|------|-----------|-----------|
|
||||||
|
| `OnBlockStarted` | — | Shield raised |
|
||||||
|
| `OnBlockEnded` | — | Shield lowered |
|
||||||
|
| `OnHitBlocked` | Damage: S_DamageEvent | Incoming hit blocked |
|
||||||
|
| `OnHitParried` | Instigator: Actor | Perfect parry executed |
|
||||||
|
| `OnShieldBroken` | — | Durability reaches 0 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Communications With
|
||||||
|
|
||||||
|
| Target System | Method | Why |
|
||||||
|
|---------------|--------|-----|
|
||||||
|
| `BPC_DamageReceptionSystem` | Interface hook | Intercepts incoming damage |
|
||||||
|
| `BPC_StaminaSystem` | Direct `ConsumeStamina` | Stamina cost per block |
|
||||||
|
| `BPC_HitReactionSystem` | Direct | Triggers parry follow-up window |
|
||||||
|
| `ABP_PlayerBody` | Animation Notify | Block/parry animation states |
|
||||||
|
|
||||||
|
## Reuse Notes
|
||||||
|
|
||||||
|
- ParryWindow drives a perfect-parry mechanic; set to 0 to disable parries
|
||||||
|
- BlockDamageReduction is per-shield from DA_WeaponData or DA_EquipmentConfig
|
||||||
|
- Shield durability adds wear mechanic; set MaxShieldDurability to 0 for indestructible shields
|
||||||
139
docs/blueprints/09-ai/80_BP_EnemyBase.md
Normal file
139
docs/blueprints/09-ai/80_BP_EnemyBase.md
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
# 58 — BP_EnemyBase
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`Character`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`BPC_AIControllerBase`](55_BPC_AIControllerBase.md) — AI brain
|
||||||
|
- [`BPC_HealthComponent`](../02-player/08_BPC_HealthComponent.md) — Health & damage
|
||||||
|
- [`BPC_StaminaComponent`](../02-player/09_BPC_StaminaComponent.md) — Sprint/action energy
|
||||||
|
- [`BPC_DamageHandlerComponent`](../08-weapons/53_BPC_DamageHandlerComponent.md) — Damage reception
|
||||||
|
- [`I_Damageable`](../01-core/03_I_Damageable.md) — Interface
|
||||||
|
- [`I_Persistable`](../05-save/29_I_Persistable.md) — Save state
|
||||||
|
- [`DA_AIProfile`](../12-content/63_DA_AIProfileDataAsset.md) — Configuration
|
||||||
|
- [`BP_PatrolPath`](59_BP_PatrolPath.md) — Waypoint navigation
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Base enemy character blueprint. Provides movement, animation, damage reaction, ragdoll, and interaction with the AI controller system. Designed to be extended for specific enemy types (humanoid, creature, drone). All AI logic is in the controller; the pawn provides physical representation and animation state.
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `AIProfile` | DA_AIProfile | Enemy configuration asset |
|
||||||
|
| `HealthComponent` | BPC_HealthComponent | Health pool |
|
||||||
|
| `StaminaComponent` | BPC_StaminaComponent | Stamina for actions |
|
||||||
|
| `PatrolPathRef` | BP_PatrolPath | Assigned patrol route |
|
||||||
|
| `SkeletalMesh` | USkeletalMeshComponent | Visual mesh |
|
||||||
|
| `AnimationBP` | UAnimInstance | Animation blueprint instance |
|
||||||
|
| `WeakSpotComponents` | Array<UChildActorComponent> | Weak point references |
|
||||||
|
| `bIsStaggered` | Bool | Hit reaction state |
|
||||||
|
| `bIsRagdoll` | Bool | Death ragdoll state |
|
||||||
|
| `RagdollLifetime` | Float | Seconds before ragdoll cleanup |
|
||||||
|
| `LootTable` | TArray<FLootEntry> | Dropped items on death |
|
||||||
|
| `AlertColor` | FLinearColor | VFX color matching alert level |
|
||||||
|
| `FootstepSounds` | TMap<UPhysicalMaterial, USoundBase> | Surface-based footsteps |
|
||||||
|
| `EnemyTypeTag` | FGameplayTag | e.g. Tag.Enemy.Humanoid |
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
| Struct | Fields | Description |
|
||||||
|
|--------|--------|-------------|
|
||||||
|
| `FLootEntry` | ItemTag: FGameplayTag, DropChance: Float, MinCount: Int, MaxCount: Int | Loot definition |
|
||||||
|
| `FWeakSpot` | BoneName: FName, DamageMultiplier: Float, bDestroyed: Bool, HitVFX: UParticleSystem | Weak point data |
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `InitializeEnemy` | Profile: DA_AIProfile | — | Set stats, components, attachment |
|
||||||
|
| `OnTakeDamage` | DamageResult: FDamageResult | — | React to damage (stagger, VFX) |
|
||||||
|
| `OnDeath` | — | — | Play death anim or ragdoll |
|
||||||
|
| `StartRagdoll` | HitLocation: FVector, Impulse: FVector | — | Physics death |
|
||||||
|
| `EndRagdoll` | — | — | Cleanup and destroy |
|
||||||
|
| `SpawnLoot` | — | — | Drop items at death location |
|
||||||
|
| `GetWeakSpotMultiplier` | BoneName: FName | Float | Return damage multiplier |
|
||||||
|
| `SetEnemyState` | NewState: EAIState | — | Update animation state |
|
||||||
|
| `PlayFootstep` | Surface: UPhysicalMaterial | — | Footstep sound |
|
||||||
|
| `GetEyeLocation` | — | FVector | Perception reference point |
|
||||||
|
| `CanBeStaggered` | — | Bool | Stagger immunity check |
|
||||||
|
| `PlayStaggerAnimation` | — | — | Hit reaction montage |
|
||||||
|
| `OnStaggerEnd` | — | — | Return to normal state |
|
||||||
|
| `SetVisibilityBasedAnimTickOption` | Option: EVisibilityBasedAnimTickOption | — | Performance culling |
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
|
||||||
|
| Enum | Values | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| `EEnemyArchetype` | Humanoid, Creature, Drone, Turret, Boss | Enemy structural type |
|
||||||
|
| `EHitReactionType` | None, Flinch, Stagger, Knockback, Launch | Reaction severity |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[OnTakeDamage]
|
||||||
|
└─► ApplyDamage to HealthComponent
|
||||||
|
└─► If HealthComponent.Health <= 0 → OnDeath
|
||||||
|
└─► Else:
|
||||||
|
HitReaction = DetermineReaction(DamageResult)
|
||||||
|
If HitReaction >= Stagger && CanBeStaggered:
|
||||||
|
bIsStaggered = true
|
||||||
|
PlayStaggerAnimation(DamageResult.HitDirection)
|
||||||
|
SpawnHitVFX(DamageResult.HitLocation)
|
||||||
|
Notify AIController of damage via dispatcher
|
||||||
|
|
||||||
|
[OnDeath]
|
||||||
|
└─► Set AIState = Disabled on controller
|
||||||
|
└─► Stop all AI logic
|
||||||
|
└─► If AIProfile.bUseRagdoll:
|
||||||
|
StartRagdoll(DamageResult.HitLocation, DamageResult.Impulse)
|
||||||
|
SetLifeSpan(RagdollLifetime)
|
||||||
|
└─► Else:
|
||||||
|
PlayDeathAnimation()
|
||||||
|
SetLifeSpan(DeathAnimTime)
|
||||||
|
└─► SpawnLoot()
|
||||||
|
└─► Broadcast OnEnemyKilled
|
||||||
|
└─► Unregister from AlertSystem
|
||||||
|
|
||||||
|
[SpawnLoot]
|
||||||
|
└─► For each FLootEntry:
|
||||||
|
If Random < DropChance:
|
||||||
|
Count = RandomRange(MinCount, MaxCount)
|
||||||
|
Spawn pickup actor with ItemTag and Count
|
||||||
|
```
|
||||||
|
|
||||||
|
### Animation State Mapping
|
||||||
|
|
||||||
|
| EAIState | Animation State | Notes |
|
||||||
|
|----------|----------------|-------|
|
||||||
|
| Idle | Idle | Standing, breathing |
|
||||||
|
| Patrol | Walk | Speed from profile |
|
||||||
|
| Suspicious | Walk (slower) | Looking around |
|
||||||
|
| Alerted | Walk (fast) | Ready stance |
|
||||||
|
| Combat | Run / Jog | Sprint toward target |
|
||||||
|
| Searching | Walk (scanning) | Head-turning |
|
||||||
|
| Fleeing | Sprint | Panic movement |
|
||||||
|
| Disabled | Stagger / Stun | Hit reaction or downed |
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target | Method | Why |
|
||||||
|
|--------|--------|-----|
|
||||||
|
| [`BPC_AIControllerBase`](55_BPC_AIControllerBase.md) | Possessing controller | AI state and behavior |
|
||||||
|
| [`BPC_HealthComponent`](../02-player/08_BPC_HealthComponent.md) | Get Component | Health modify and death |
|
||||||
|
| [`BPC_DamageHandlerComponent`](../08-weapons/53_BPC_DamageHandlerComponent.md) | Get Component | Process incoming damage |
|
||||||
|
| [`BPC_CombatFeedbackComponent`](../08-weapons/54_BPC_CombatFeedbackComponent.md) | Get Component | Blood, hit FX |
|
||||||
|
| Animation Blueprint | Direct | State machine query |
|
||||||
|
| [`BP_PatrolPath`](59_BP_PatrolPath.md) | Direct | Waypoint navigation |
|
||||||
|
| World Spawning System | Direct | Loot spawning |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
- Subclass for different enemy visuals, hitbox setups, and animation sets
|
||||||
|
- Weak spots defined per blueprint (head, back weak point, glowing core)
|
||||||
|
- Loot table data-driven: design new enemies by swapping DA_AIProfile
|
||||||
|
- Ragdoll vs death animation toggle per enemy type
|
||||||
|
- AnimBP queries AIState for state-driven animation
|
||||||
109
docs/blueprints/09-ai/81_BP_PatrolPath.md
Normal file
109
docs/blueprints/09-ai/81_BP_PatrolPath.md
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
# 59 — BP_PatrolPath
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`Actor`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`BP_EnemyBase`](58_BP_EnemyBase.md) — AI pawn using the path
|
||||||
|
- [`BPC_AIControllerBase`](55_BPC_AIControllerBase.md) — Waypoint consumer
|
||||||
|
- [`DA_AIProfile`](../12-content/63_DA_AIProfileDataAsset.md) — Patrol behavior config
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Waypoint-based patrol route for AI characters. Defines a sequence of points (spline or actor array) that AI follows during patrol state. Supports looping, ping-pong, random selection, and conditional stops. Visualized in-editor with spline or point indicators for level designers.
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `PatrolPoints` | Array<FVector> | World-space waypoints |
|
||||||
|
| `PatrolMode` | EPatrolMode | Sequence mode |
|
||||||
|
| `bIsLooping` | Bool | Restart from first after last |
|
||||||
|
| `bIsPingPong` | Bool | Reverse direction at end |
|
||||||
|
| `PointRadius` | Float | Acceptance radius for reaching a point |
|
||||||
|
| `WaitTimeAtPoint` | Float | Pause at each waypoint (seconds) |
|
||||||
|
| `bRandomizeOrder` | Bool | Shuffle points on each loop |
|
||||||
|
| `bUseSpline` | Bool | Draw spline between points |
|
||||||
|
| `SplineComponent` | USplineComponent | Optional spline path |
|
||||||
|
| `bDrawDebug` | Bool | Editor visualization toggle |
|
||||||
|
| `DebugPointColor` | FColor | Point color in editor |
|
||||||
|
| `ConnectedAI` | Array<BPC_AIControllerBase> | AI using this path |
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
|
||||||
|
| Enum | Values | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| `EPatrolMode` | Sequential, Random, PingPong, Reverse | Point selection order |
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `GetNextPatrolPoint` | CurrentIndex: Int | FVector, NewIndex: Int | Next waypoint based on mode |
|
||||||
|
| `GetPatrolStartPoint` | — | FVector, StartIndex: Int | First point |
|
||||||
|
| `GetPatrolPointCount` | — | Int | Total points |
|
||||||
|
| `GetPatrolPointByIndex` | Index: Int | FVector | Specific point |
|
||||||
|
| `AddPatrolPoint` | Location: FVector | — | Add waypoint |
|
||||||
|
| `RemovePatrolPoint` | Index: Int | — | Remove waypoint |
|
||||||
|
| `ClearPatrolPoints` | — | — | Reset path |
|
||||||
|
| `SetPatrolMode` | Mode: EPatrolMode | — | Change movement pattern |
|
||||||
|
| `IsPatrolComplete` | CurrentIndex: Int | Bool | End of path |
|
||||||
|
| `RegisterAIUser` | AIController: BPC_AIControllerBase | — | Track who uses this path |
|
||||||
|
| `UnregisterAIUser` | AIController: BPC_AIControllerBase | — | Remove usage |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[GetNextPatrolPoint(CurrentIndex)]
|
||||||
|
└─► If PatrolMode == Sequential:
|
||||||
|
NewIndex = CurrentIndex + 1
|
||||||
|
If NewIndex >= PatrolPoints.Num():
|
||||||
|
If bIsLooping: NewIndex = 0
|
||||||
|
Else: return Last point and IsPatrolComplete = true
|
||||||
|
└─► If PatrolMode == Random:
|
||||||
|
NewIndex = Random integer in range [0, Points.Num - 1]
|
||||||
|
Exclude CurrentIndex to avoid standing still
|
||||||
|
└─► If PatrolMode == PingPong:
|
||||||
|
If going forward:
|
||||||
|
NewIndex = CurrentIndex + 1
|
||||||
|
If NewIndex >= PatrolPoints.Num(): reverse direction
|
||||||
|
If going backward:
|
||||||
|
NewIndex = CurrentIndex - 1
|
||||||
|
If NewIndex < 0: reverse direction
|
||||||
|
└─► If PatrolMode == Reverse:
|
||||||
|
NewIndex = CurrentIndex - 1
|
||||||
|
If NewIndex < 0:
|
||||||
|
If bIsLooping: NewIndex = Points.Num - 1
|
||||||
|
Else: IsPatrolComplete = true
|
||||||
|
└─► Return PatrolPoints[NewIndex], NewIndex
|
||||||
|
```
|
||||||
|
|
||||||
|
### Editor Visualization
|
||||||
|
|
||||||
|
```
|
||||||
|
[OnConstruction]
|
||||||
|
└─► If bDrawDebug:
|
||||||
|
For each point:
|
||||||
|
DrawDebugSphere(Point, PointRadius, 12, DebugPointColor)
|
||||||
|
If bUseSpline:
|
||||||
|
SplineComponent.SetSplinePoints(PatrolPoints, ESplineCoordinateSpace::World)
|
||||||
|
Else:
|
||||||
|
Draw lines between consecutive points
|
||||||
|
```
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target | Method | Why |
|
||||||
|
|--------|--------|-----|
|
||||||
|
| [`BPC_AIControllerBase`](55_BPC_AIControllerBase.md) | Direct call | AI requests next waypoint |
|
||||||
|
| [`BP_EnemyBase`](58_BP_EnemyBase.md) | Direct (via controller) | Move-to destination |
|
||||||
|
| Level Designer | Editor visualization | Place and configure in world |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
- World-placed actor, assignable to any AI via DA_AIProfile or direct reference
|
||||||
|
- Multiple AI can share one patrol path (squad patrol)
|
||||||
|
- Spline mode allows smooth curved paths (for flying/gliding enemies)
|
||||||
|
- Works with any AI that reads patrol index from blackboard
|
||||||
147
docs/blueprints/09-ai/82_BPC_AlertSystem.md
Normal file
147
docs/blueprints/09-ai/82_BPC_AlertSystem.md
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
# 60 — BPC_AlertSystem
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`BPC_AIControllerBase`](55_BPC_AIControllerBase.md) — Owner controller
|
||||||
|
- [`BP_EnemyBase`](58_BP_EnemyBase.md) — Pawn that receives commands
|
||||||
|
- [`DA_AIProfile`](../12-content/63_DA_AIProfileDataAsset.md) — Alert thresholds and response times
|
||||||
|
- [`BPC_PerceptionComponent`](56_BPC_PerceptionComponent.md) — Stimulus input source
|
||||||
|
- [`BPC_AIStateMachine`](61_BPC_AIStateMachine.md) — State transition triggers
|
||||||
|
- [`I_Damageable`](../01-foundation/05_I_Damageable.md) — Damage stimulus source
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Manages AI threat levels, alert escalation, and reinforcement propagation. Receives stimuli from perception and damage systems, evaluates threat severity, escalates through alert levels (Unaware → Suspicious → Alert → Combat), and notifies nearby AI units. Acts as the central threat-response coordinator for each AI.
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
|
||||||
|
| Enum | Values | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| `EAlertLevel` | Unaware, Suspicious, Alert, Combat | Current alert tier |
|
||||||
|
| `EAlertCause` | SightStimulus, HearingStimulus, DamageTaken, AllyAlert, Environmental | What triggered the alert |
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
| Struct | Fields | Description |
|
||||||
|
|--------|--------|-------------|
|
||||||
|
| `FAlertState` | AlertLevel: EAlertLevel, AlertCause: EAlertCause, LastAlertTime: Float, SourceLocation: FVector, KnownTarget: AActor, bHasValidTarget: Bool, SuspicionValue: Float, CombatTimer: Float | Snapshot of current alert status |
|
||||||
|
| `FAlertPropagationInfo` | AlertLevel: EAlertLevel, SourceLocation: FVector, Target: AActor, PropagationRadius: Float, Timestamp: Float | Data sent to nearby AI |
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `CurrentAlertState` | FAlertState | Live alert tracking |
|
||||||
|
| `AlertThresholds` | FVector2D | Suspicion threshold X, Combat threshold Y |
|
||||||
|
| `SuspicionDecayRate` | Float | Points per second lost when no stimulus |
|
||||||
|
| `SuspicionGrowthRate` | Float | Points per second gained on stimulus |
|
||||||
|
| `CombatExitDelay` | Float | Time without combat before dropping to Alert |
|
||||||
|
| `ReinforcementRadius` | Float | Max distance to notify allies |
|
||||||
|
| `bCanCallReinforcements` | Bool | Permission to propagate |
|
||||||
|
| `ReinforcementCooldown` | Float | Min seconds between calls |
|
||||||
|
| `LastReinforcementTime` | Float | Cooldown tracker |
|
||||||
|
| `bIsInCombat` | Bool | Fast check for combat state |
|
||||||
|
| `InvestigationTimer` | Float | Time spent investigating last known position |
|
||||||
|
| `MaxInvestigationTime` | Float | Give up after this long |
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `EvaluateStimulus` | StimulusType: EAlertCause, Intensity: Float, Location: FVector, Instigator: AActor | — | Process incoming stimulus |
|
||||||
|
| `CalculateThreatScore` | StimulusType: EAlertCause, Intensity: Float, Distance: Float | Float | Compute weighted threat value |
|
||||||
|
| `EscalateAlert` | NewLevel: EAlertLevel, Cause: EAlertCause | — | Raise alert level |
|
||||||
|
| `DeescalateAlert` | — | — | Lower alert level (decay timer) |
|
||||||
|
| `UpdateSuspicion` | DeltaTime: Float | — | Tick suspicion up/down |
|
||||||
|
| `CheckAlertTransition` | — | EAlertLevel | Determine if threshold crossed |
|
||||||
|
| `EnterCombat` | Target: AActor | — | Transition to combat state |
|
||||||
|
| `ExitCombat` | — | — | Transition out of combat |
|
||||||
|
| `CallReinforcements` | — | — | Broadcast alert to allies |
|
||||||
|
| `ReceiveReinforcementCall` | Info: FAlertPropagationInfo | — | Handle ally alert broadcast |
|
||||||
|
| `GetCurrentAlertLevel` | — | EAlertLevel | Getter for blackboard updates |
|
||||||
|
| `GetKnownTarget` | — | AActor | Current combat target |
|
||||||
|
| `IsInCombat` | — | Bool | Combat state check |
|
||||||
|
| `SetInvestigationPoint` | Location: FVector | — | Mark last known target position |
|
||||||
|
| `ClearInvestigationPoint` | — | — | Reset investigation |
|
||||||
|
| `ShouldGiveUpSearch` | — | Bool | Timeout check for investigation |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[EvaluateStimulus]
|
||||||
|
└─► Calculate Threat Score
|
||||||
|
└─► If Score > AlertThresholds.Y --> Escalate to Combat
|
||||||
|
└─► Else If Score > AlertThresholds.X --> Escalate to Suspicious
|
||||||
|
└─► Else --> Update SuspicionValue (accumulate)
|
||||||
|
└─► If AlertLevel >= Suspicious:
|
||||||
|
Set Investigation Point
|
||||||
|
If AlertLevel == Combat:
|
||||||
|
Set KnownTarget
|
||||||
|
EnterCombat
|
||||||
|
If bCanCallReinforcements:
|
||||||
|
CallReinforcements
|
||||||
|
|
||||||
|
[Event Tick]
|
||||||
|
└─► UpdateSuspicion(DeltaTime):
|
||||||
|
If no stimulus:
|
||||||
|
SuspicionValue -= SuspicionDecayRate * DeltaTime
|
||||||
|
If SuspicionValue <= 0:
|
||||||
|
DeescalateAlert (back to Unaware)
|
||||||
|
If AlertLevel == Combat:
|
||||||
|
CombatTimer += DeltaTime
|
||||||
|
If CombatTimer > CombatExitDelay && no damage/stimulus:
|
||||||
|
ExitCombat -> set AlertLevel = Alert
|
||||||
|
If AlertLevel == Suspicious:
|
||||||
|
InvestigationTimer += DeltaTime
|
||||||
|
If InvestigationTimer > MaxInvestigationTime:
|
||||||
|
ClearInvestigationPoint
|
||||||
|
DeescalateAlert
|
||||||
|
|
||||||
|
[CallReinforcements]
|
||||||
|
└─► If (Time - LastReinforcementTime) < ReinforcementCooldown: return
|
||||||
|
└─► Get all AI controllers within ReinforcementRadius
|
||||||
|
└─► For each:
|
||||||
|
Call ReceiveReinforcementCall with current alert info
|
||||||
|
└─► LastReinforcementTime = CurrentTime
|
||||||
|
|
||||||
|
[ReceiveReinforcementCall]
|
||||||
|
└─► If CurrentAlertLevel >= Info.AlertLevel: ignore (already higher)
|
||||||
|
└─► Else: EscalateAlert(Info.AlertLevel, Environmental)
|
||||||
|
Set InvestigationPoint = Info.SourceLocation
|
||||||
|
If Info.AlertLevel == Combat:
|
||||||
|
Set KnownTarget = Info.Target
|
||||||
|
```
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Delegate Signature | Purpose |
|
||||||
|
|------|-------------------|---------|
|
||||||
|
| `OnAlertLevelChanged` | EAlertLevel, EAlertCause | UI, sound, state machine listeners |
|
||||||
|
| `OnCombatEntered` | AActor Target | Combat music, HUD, behavior tree |
|
||||||
|
| `OnCombatExited` | — | Combat cleanup |
|
||||||
|
| `OnReinforcementCalled` | FAlertPropagationInfo | Debug logging, analytics |
|
||||||
|
| `OnInvestigationStarted` | FVector Location | Sound/investigation music |
|
||||||
|
| `OnInvestigationEnded` | — | Return to patrol |
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target | Method | Why |
|
||||||
|
|--------|--------|-----|
|
||||||
|
| [`BPC_AIStateMachine`](61_BPC_AIStateMachine.md) | Call | Request state change |
|
||||||
|
| [`BPC_AIControllerBase`](55_BPC_AIControllerBase.md) | Owner reference | Coordinate BB values |
|
||||||
|
| [`BPC_PerceptionComponent`](56_BPC_PerceptionComponent.md) | Direct call | Get stimulus data |
|
||||||
|
| [`BP_EnemyBase`](58_BP_EnemyBase.md) | Owner reference | Trigger combat animations |
|
||||||
|
| [`WBP_HUD`](../06-ui/36_WBP_HUD.md) | Event dispatcher | Show combat indicator |
|
||||||
|
| [`BPC_CombatFeedback`](../08-combat/54_BPC_CombatFeedbackComponent.md) | Event dispatcher | Combat music/vignette |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
- Attached to AIControllerBase; profiles tune thresholds per enemy type
|
||||||
|
- Works with any perception system that produces EAlertCause stimuli
|
||||||
|
- Reinforcement chain prevents infinite loops by comparing alert levels
|
||||||
|
- Investigation system handles "last seen" behavior without combat
|
||||||
|
- Decay system ensures AI eventually returns to patrol state
|
||||||
180
docs/blueprints/09-ai/83_BPC_AIStateMachine.md
Normal file
180
docs/blueprints/09-ai/83_BPC_AIStateMachine.md
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
# 61 — BPC_AIStateMachine
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`BPC_AIControllerBase`](55_BPC_AIControllerBase.md) — Owner controller, blackboard access
|
||||||
|
- [`BPC_AlertSystem`](60_BPC_AlertSystem.md) — Alert level triggers state transitions
|
||||||
|
- [`BPC_PerceptionComponent`](56_BPC_PerceptionComponent.md) — Stimulus input
|
||||||
|
- [`BPC_BehaviorTreeManager`](57_BPC_BehaviorTreeManager.md) — Tree lifecycle requests
|
||||||
|
- [`BP_EnemyBase`](58_BP_EnemyBase.md) — Pawn animation state requests
|
||||||
|
- [`DA_AIProfile`](../12-content/63_DA_AIProfileDataAsset.md) — State timings, cooldowns, transition rules
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
High-level finite state machine for AI behavior. Manages valid state transitions, cooldowns, priority overrides, and lifecycle events for each AI state (Patrol, Investigate, Search, Combat, Flee, Idle, Stunned). Interprets alert level changes from the alert system and resolves transitions with rule checking before delegating to the behavior tree manager.
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
|
||||||
|
| Enum | Values | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| `EAIState` | Idle, Patrol, Investigate, Search, Combat, Flee, Stunned, Dead | All possible AI states |
|
||||||
|
| `EStateTransitionRule` | Allow, Block (Cooldown), Block (Priority), Block (Health), Block (Narrative) | Transition permission results |
|
||||||
|
| `EStatePriority` | None, Low, Medium, High, Critical | State override priority |
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
| Struct | Fields | Description |
|
||||||
|
|--------|--------|-------------|
|
||||||
|
| `FStateConfig` | State: EAIState, bCanTransitionFrom: Array<EAIState>, MinTimeInState: Float, MaxTimeInState: Float, CooldownAfterExit: Float, Priority: EStatePriority, bAllowInterrupt: Bool, AssociatedTree: EBehaviorTreeType | Configuration per state |
|
||||||
|
| `FStateInstance` | State: EAIState, EnterTime: Float, LastExitTime: Float, bIsActive: Bool, CustomData: FName (tag), bCanInterrupt: Bool | Runtime state tracking |
|
||||||
|
| `FTransitionRequest` | RequestedState: EAIState, Priority: EStatePriority, Reason: FName, Instigator: AActor | Queued transition |
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `CurrentState` | EAIState | Active state |
|
||||||
|
| `PreviousState` | EAIState | State before last transition |
|
||||||
|
| `StateMap` | Map<EAIState, FStateConfig> | All state configs from profile |
|
||||||
|
| `StateInstances` | Map<EAIState, FStateInstance> | Runtime state trackers |
|
||||||
|
| `PendingTransitions` | Array<FTransitionRequest> | Queue of requested transitions |
|
||||||
|
| `bIsTransitioning` | Bool | Lock during transition processing |
|
||||||
|
| `MaxTransitionsPerTick` | Int | Safety limit (default 3) |
|
||||||
|
| `bLockedByNarrative` | Bool | Story script lock |
|
||||||
|
| `NarrativeLockReason` | FName | Why narrative locked AI |
|
||||||
|
| `StunnedDuration` | Float | Current stun timer remaining |
|
||||||
|
| `FleeThreshold` | Float | Health % below which AI flees |
|
||||||
|
| `bCanFlee` | Bool | Permission to flee |
|
||||||
|
|
||||||
|
### Functions — Core
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `RequestTransition` | NewState: EAIState, Priority: EStatePriority, Reason: FName, Instigator: AActor | Bool | Queue or execute state change |
|
||||||
|
| `ProcessPendingTransitions` | — | — | Evaluate queue on tick |
|
||||||
|
| `EvaluateTransitionRules` | FromState: EAIState, ToState: EAIState | EStateTransitionRule | Check if transition is allowed |
|
||||||
|
| `ForceTransition` | NewState: EAIState | — | Bypass rules (narrative/debug) |
|
||||||
|
| `GetCurrentState` | — | EAIState | BB getter |
|
||||||
|
| `GetTimeInCurrentState` | — | Float | Duration in current state |
|
||||||
|
| `CanTransitionTo` | NewState: EAIState | Bool | Public permission check |
|
||||||
|
| `IsStateActive` | State: EAIState | Bool | Check any state |
|
||||||
|
|
||||||
|
### Functions — State Lifecycle
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `EnterState` | NewState: EAIState, Instigator: AActor | — | State entry logic |
|
||||||
|
| `ExitState` | CurrentState: EAIState | — | State exit cleanup |
|
||||||
|
| `UpdateState` | DeltaTime: Float | — | Per-tick state behavior |
|
||||||
|
| `OnStateEnter_Patrol` | — | — | Setup patrol BB keys |
|
||||||
|
| `OnStateEnter_Investigate` | Location: FVector | — | Setup investigation |
|
||||||
|
| `OnStateEnter_Search` | LastKnownLocation: FVector | — | Setup search area |
|
||||||
|
| `OnStateEnter_Combat` | Target: AActor | — | Combat initialization |
|
||||||
|
| `OnStateEnter_Flee` | ThreatLocation: FVector | — | Flee setup |
|
||||||
|
| `OnStateEnter_Stunned` | Duration: Float | — | Stun timer |
|
||||||
|
| `OnStateEnter_Idle` | — | — | Idle setup |
|
||||||
|
| `OnStateEnter_Dead` | — | — | Death state |
|
||||||
|
| `OnStateExit_All` | — | — | Common exit behavior |
|
||||||
|
|
||||||
|
### Functions — Transition Rules
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `CheckCooldown` | State: EAIState | Bool | Has cooldown expired |
|
||||||
|
| `CheckHealthThreshold` | ToState: EAIState | Bool | Health gate check for flee/stun |
|
||||||
|
| `CheckNarrativeLock` | — | Bool | Is AI locked by script |
|
||||||
|
| `CheckPriorityOverride` | Request: FTransitionRequest | Bool | Higher priority can interrupt |
|
||||||
|
| `CheckMinStateTime` | — | Bool | Minimum time in current state met |
|
||||||
|
|
||||||
|
### Blueprint Flow — Transition Resolution
|
||||||
|
|
||||||
|
```
|
||||||
|
[RequestTransition]
|
||||||
|
└─► If bLockedByNarrative AND not ForceTransition: deny (return false)
|
||||||
|
└─► If bIsTransitioning: queue to PendingTransitions, return false
|
||||||
|
└─► EvaluateTransitionRules:
|
||||||
|
Call CheckNarrativeLock --> if blocked, deny
|
||||||
|
Call CheckCooldown(FromState) --> if active, deny
|
||||||
|
Call CheckMinStateTime --> if not met, queue or deny
|
||||||
|
Call CheckPriorityOverride --> if request priority >= current, allow
|
||||||
|
Else: deny or queue
|
||||||
|
└─► If Allowed:
|
||||||
|
bIsTransitioning = true
|
||||||
|
ExitState(CurrentState)
|
||||||
|
EnterState(NewState)
|
||||||
|
Update blackboard
|
||||||
|
Request BehaviorTree switch from BT Manager
|
||||||
|
bIsTransitioning = false
|
||||||
|
Return true
|
||||||
|
|
||||||
|
[ProcessPendingTransitions]
|
||||||
|
└─► Sort PendingTransitions by Priority (high first)
|
||||||
|
└─► For each (up to MaxTransitionsPerTick):
|
||||||
|
Try RequestTransition again
|
||||||
|
If success: remove from queue
|
||||||
|
If fails again: increment failure counter, remove if > 3 attempts
|
||||||
|
|
||||||
|
[Event Tick]
|
||||||
|
└─► UpdateState(DeltaTime):
|
||||||
|
Switch CurrentState:
|
||||||
|
Patrol: Check patrol timer, check patrol completion
|
||||||
|
Investigate: Update investigation timer, check timeout
|
||||||
|
Search: Update search timer, check coverage
|
||||||
|
Combat: Update combat timer, check health for flee
|
||||||
|
Flee: Update flee timer, check distance from threat
|
||||||
|
Stunned: StunnedDuration -= DeltaTime; if <= 0, transition
|
||||||
|
Idle: Update idle timer
|
||||||
|
Check external triggers:
|
||||||
|
If bCanFlee && Health < FleeThreshold: RequestTransition(Flee)
|
||||||
|
If AlertSystem.IsInCombat && CurrentState != Combat: RequestTransition(Combat)
|
||||||
|
```
|
||||||
|
|
||||||
|
### State Transition Map (Allowed)
|
||||||
|
|
||||||
|
| From \ To | Idle | Patrol | Investigate | Search | Combat | Flee | Stunned | Dead |
|
||||||
|
|-----------|------|--------|------------|--------|--------|------|---------|------|
|
||||||
|
| Idle | — | Yes | Yes | No | Yes | No | Yes | Yes |
|
||||||
|
| Patrol | Yes | — | Yes | No | Yes | No | Yes | Yes |
|
||||||
|
| Investigate | Yes | Yes | — | Yes | Yes | No | Yes | Yes |
|
||||||
|
| Search | Yes | Yes | No | — | Yes | Yes | Yes | Yes |
|
||||||
|
| Combat | No | No | No | No | — | Yes | Yes | Yes |
|
||||||
|
| Flee | Yes | Yes | No | No | No | — | Yes | Yes |
|
||||||
|
| Stunned | Yes | Yes | Yes | No | Yes | Yes | — | Yes |
|
||||||
|
| Dead | No | No | No | No | No | No | No | — |
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Delegate Signature | Purpose |
|
||||||
|
|------|-------------------|---------|
|
||||||
|
| `OnAIStateChanged` | EAIState NewState, EAIState PreviousState, AActor Instigator | For all listeners |
|
||||||
|
| `OnStateTransitionBlocked` | EAIState Requested, FStateTransitionRule Reason | Debug logging |
|
||||||
|
| `OnAIDefeated` | AActor Killer | Death sequence |
|
||||||
|
| `OnAIFled` | — | Flee started |
|
||||||
|
| `OnAIStunned` | Float Duration | Stun entered |
|
||||||
|
| `OnAIStateTimerWarning` | EAIState, Float RemainingTime | State expiry warning |
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target | Method | Why |
|
||||||
|
|--------|--------|-----|
|
||||||
|
| [`BPC_AlertSystem`](60_BPC_AlertSystem.md) | Event listener | Alert level changes trigger transitions |
|
||||||
|
| [`BPC_BehaviorTreeManager`](57_BPC_BehaviorTreeManager.md) | Direct call | Request tree switch on state enter |
|
||||||
|
| [`BPC_AIControllerBase`](55_BPC_AIControllerBase.md) | Owner reference | Update BB keys |
|
||||||
|
| [`BP_EnemyBase`](58_BP_EnemyBase.md) | Event dispatcher | Trigger state animations |
|
||||||
|
| [`DA_AIProfile`](../12-content/63_DA_AIProfileDataAsset.md) | Data lookup | Load state configs |
|
||||||
|
| [`BPC_PerceptionComponent`](56_BPC_PerceptionComponent.md) | Direct call | Get stimulus for investigate |
|
||||||
|
| Narrative system | Event dispatcher | Narrative lock/unlock |
|
||||||
|
| [`BPC_HealthComponent`](../01-player/01_BPC_HealthComponent.md) | Event listener | Health threshold checks |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
- One per AIController; state configs defined per AI type in DA_AIProfile
|
||||||
|
- Priority queue prevents infinite loops during rapid state changes
|
||||||
|
- Narrative lock allows scripted moments where AI cannot react
|
||||||
|
- Stun state can be triggered by weapons, environment, or narrative events
|
||||||
|
- Flee uses the same path system as patrol (inverted direction)
|
||||||
|
- Dead state is terminal — no transitions out (death is hard finality by design for this horror framework)
|
||||||
158
docs/blueprints/09-ai/84_AI_BaseAgentController.md
Normal file
158
docs/blueprints/09-ai/84_AI_BaseAgentController.md
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
# AI_BaseAgentController — AI Controller
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`AIController`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`BP_EnemyBase`](58_BP_EnemyBase.md) — Pawn
|
||||||
|
- [`BPC_AIPerceptionSystem`](BPC_AIPerceptionSystem.md) — Sensing
|
||||||
|
- [`BB_AgentBoard`](BB_AgentBoard.md) — Decision-making
|
||||||
|
- [`BPC_AIStateMachine`](61_BPC_AIStateMachine.md) — High-level states
|
||||||
|
- [`BPC_AlertSystem`](60_BPC_AlertSystem.md) — Threat awareness
|
||||||
|
- [`BPC_HealthSystem`](../02-player/08_BPC_HealthSystem.md) — Self health
|
||||||
|
- [`I_Damageable`](../01-core/03_I_InterfaceLibrary.md) — Damage reception
|
||||||
|
- `DA_AIProfile` — AI configuration
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Central brain for enemy AI characters. Manages perception, decision-making via Behavior Trees, high-level state machine transitions, alert level propagation, and combat coordination. Possesses `BP_EnemyBase` pawns and controls their actions through Blackboard values and Behavior Tree execution.
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `AIProfile` | DA_AIProfile | Configuration asset |
|
||||||
|
| `PossessedEnemy` | BP_EnemyBase | Current pawn reference |
|
||||||
|
| `BlackboardComp` | UBlackboardComponent | Blackboard instance |
|
||||||
|
| `BehaviorTreeComp` | UBehaviorTreeComponent | Behavior tree runner |
|
||||||
|
| `PerceptionComp` | BPC_AIPerceptionSystem | Sensory input |
|
||||||
|
| `StateMachine` | BPC_AIStateMachine | State logic |
|
||||||
|
| `AlertSystem` | BPC_AlertSystem | Threat level |
|
||||||
|
| `bIsActive` | Bool | Enabled / disabled |
|
||||||
|
| `AggressionRange` | Float | Engage distance |
|
||||||
|
| `SuspicionRange` | Float | Investigate distance |
|
||||||
|
| `CombatRange` | Float | Preferred combat distance |
|
||||||
|
| `HomeLocation` | FVector | Spawn or patrol anchor |
|
||||||
|
| `LastKnownPlayerLocation` | FVector | Blackboard updated |
|
||||||
|
| `bHasLineOfSight` | Bool | LOS status |
|
||||||
|
| `LostPlayerTimer` | Float | Seconds since last sighting |
|
||||||
|
|
||||||
|
### Blackboard Keys
|
||||||
|
|
||||||
|
| Key Name | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `SelfActor` | Object | Self reference |
|
||||||
|
| `TargetActor` | Object | Current threat |
|
||||||
|
| `TargetLocation` | Vector | Last known threat location |
|
||||||
|
| `HomeLocation` | Vector | Patrol anchor |
|
||||||
|
| `AIState` | Enum (EAIState) | Current state |
|
||||||
|
| `AlertLevel` | Float | 0.0–1.0 awareness |
|
||||||
|
| `bHasLOS` | Bool | Line of sight |
|
||||||
|
| `bIsInvestigating` | Bool | Searching location |
|
||||||
|
| `PatrolIndex` | Int | Current waypoint |
|
||||||
|
| `CombatStance` | Enum | Aggressive / Defensive |
|
||||||
|
| `StimulusLocation` | Vector | Sound/sight trigger point |
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
|
||||||
|
| Enum | Values | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| `EAIState` | Idle, Patrol, Suspicious, Alerted, Combat, Searching, Fleeing, Disabled | AI behavior state |
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `OnPossess` | Pawn: APawn | — | Bind blackboard, run BT |
|
||||||
|
| `OnUnPossess` | — | — | Cleanup |
|
||||||
|
| `InitializeAI` | Profile: DA_AIProfile | — | Set up components |
|
||||||
|
| `SetAIState` | NewState: EAIState | — | State machine transition |
|
||||||
|
| `UpdatePerception` | Stimulus: FAIStimulus | — | Process sensory input |
|
||||||
|
| `SetTarget` | Target: AActor | — | Update blackboard target |
|
||||||
|
| `ClearTarget` | — | — | Target lost |
|
||||||
|
| `StartInvestigation` | Location: FVector | — | Move to investigate |
|
||||||
|
| `RequestReinforcements` | — | — | Alert nearby allies |
|
||||||
|
| `GetCombatReadiness` | — | Float | Based on health, ammo, alert |
|
||||||
|
| `OnTakeDamage` | DamageResult: FDamageResult | — | React to being hit |
|
||||||
|
| `CanSeeTarget` | — | Bool | LOS check |
|
||||||
|
| `LostSightOfTarget` | — | — | Start search timer |
|
||||||
|
| `ReturnToPatrol` | — | — | Reset to patrol state |
|
||||||
|
| `GetHomeLocation` | — | FVector | Patrol anchor |
|
||||||
|
| `SetFocalPoint` | Location: FVector | — | Aim facing direction |
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired When |
|
||||||
|
|------|-----------|-----------|
|
||||||
|
| `OnAIStateChanged` | OldState: EAIState, NewState: EAIState | State transition |
|
||||||
|
| `OnTargetAcquired` | Target: AActor | New threat detected |
|
||||||
|
| `OnTargetLost` | — | Target out of range/perception |
|
||||||
|
| `OnAlertRaised` | AlertLevel: Float | Alert threshold crossed |
|
||||||
|
| `OnReinforcementRequested` | Location: FVector | Call for backup |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[OnPossess]
|
||||||
|
└─► PossessedEnemy = Cast<BP_EnemyBase>(Pawn)
|
||||||
|
└─► AIProfile = PossessedEnemy.AIProfile (or set from spawner)
|
||||||
|
└─► InitializeAI(AIProfile)
|
||||||
|
└─► RunBehaviorTree(AIProfile.BehaviorTree)
|
||||||
|
└─► SetHomeLocation(Pawn.GetActorLocation)
|
||||||
|
|
||||||
|
[InitializeAI]
|
||||||
|
└─► Create BPC_AIPerceptionSystem if not exists
|
||||||
|
└─► Create BPC_AIStateMachine
|
||||||
|
└─► Create BPC_AlertSystem
|
||||||
|
└─► PerceptionComp.Initialize(AIProfile.PerceptionConfig)
|
||||||
|
└─► StateMachine.Initialize(EAIState.Patrol)
|
||||||
|
└─► AlertSystem.Initialize()
|
||||||
|
└─► Set AIState = Patrol in Blackboard
|
||||||
|
|
||||||
|
[UpdatePerception — called by PerceptionComp dispatcher]
|
||||||
|
└─► If Stimulus.Type == Sight && Stimulus.bSuccess:
|
||||||
|
LastKnownPlayerLocation = Stimulus.StimulusLocation
|
||||||
|
AlertSystem.RaiseAlert(SightAlertValue)
|
||||||
|
If AlertSystem.AlertLevel >= AIProfile.CombatThreshold:
|
||||||
|
SetTarget(Stimulus.Instigator)
|
||||||
|
SetAIState(Combat)
|
||||||
|
Else:
|
||||||
|
SetAIState(Suspicious)
|
||||||
|
└─► If Stimulus.Type == Hearing && Stimulus.bSuccess:
|
||||||
|
AlertSystem.RaiseAlert(HearingAlertValue)
|
||||||
|
StartInvestigation(Stimulus.StimulusLocation)
|
||||||
|
SetAIState(Searching)
|
||||||
|
└─► If Stimulus.Type == Damage:
|
||||||
|
SetTarget(Stimulus.Instigator)
|
||||||
|
AlertSystem.RaiseAlert(DamageAlertValue)
|
||||||
|
SetAIState(Combat)
|
||||||
|
RequestReinforcements()
|
||||||
|
|
||||||
|
[OnTakeDamage]
|
||||||
|
└─► AlertSystem.RaiseAlert(MaxAlertValue)
|
||||||
|
└─► SetTarget(DamageResult.Instigator)
|
||||||
|
└─► SetAIState(Combat)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target | Method | Why |
|
||||||
|
|--------|--------|-----|
|
||||||
|
| [`BP_EnemyBase`](58_BP_EnemyBase.md) | Possessed pawn | Movement, animation, abilities |
|
||||||
|
| [`BPC_AIPerceptionSystem`](BPC_AIPerceptionSystem.md) | Get Component | Sensory stimulus processing |
|
||||||
|
| [`BB_AgentBoard`](BB_AgentBoard.md) | Get Component | Blackboard updates |
|
||||||
|
| [`BPC_AIStateMachine`](61_BPC_AIStateMachine.md) | Get Component | State transitions |
|
||||||
|
| [`BPC_AlertSystem`](60_BPC_AlertSystem.md) | Get Component | Threat level queries |
|
||||||
|
| [`BPC_HealthSystem`](../02-player/08_BPC_HealthSystem.md) | Get on Pawn | Health events |
|
||||||
|
| Nearby AI Controllers | Direct / Event | Reinforcement calls |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
- Designed as parent for all enemy types (humanoid, creature, drone)
|
||||||
|
- Perception, alerts, and state machine are swappable components
|
||||||
|
- Behavior tree is data-driven via AIProfile
|
||||||
|
- All decision values (ranges, thresholds, speeds) come from data asset
|
||||||
|
- Renamed from `BPC_AIControllerBase` to `AI_BaseAgentController` per Master naming convention.
|
||||||
|
- Cross-references updated: `BPC_PerceptionComponent` → `BPC_AIPerceptionSystem`, `BPC_BehaviorTreeManager` → `BB_AgentBoard`, `BPC_HealthComponent` → `BPC_HealthSystem`.
|
||||||
123
docs/blueprints/09-ai/85_BB_AgentBoard.md
Normal file
123
docs/blueprints/09-ai/85_BB_AgentBoard.md
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
# BB_AgentBoard — AI Agent Blackboard Asset
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`UBlackboardData` (Blackboard Asset)
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- **Required By:** [`AI_BaseAgentController`](AI_BaseAgentController.md) — Uses this blackboard for all agent state
|
||||||
|
- **Required By:** [`BPC_AIPerceptionSystem`](BPC_AIPerceptionSystem.md) — Writes perception data to blackboard keys
|
||||||
|
- **Required By:** [`BPC_AIMemorySystem`](BPC_AIMemorySystem.md) — Reads/writes investigate and search locations
|
||||||
|
- **Required By:** [`BPC_BehaviourVariantSelector`](BPC_BehaviourVariantSelector.md) — Writes PlayerPlaystyleTag key
|
||||||
|
- **Required By:** Behaviour Tree (`BT_Agent`) — All BT nodes read/write these keys
|
||||||
|
- **Engine/Plugin Requirements:** AI Module, GameplayTags (for PlayerPlaystyleTag)
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Standardised blackboard asset defining all keys used by AI agents. Acts as the shared data contract between perception, memory, behaviour variant selection, and the behaviour tree. Every AI agent using this framework must use this blackboard or a derivative.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Enums
|
||||||
|
|
||||||
|
*Uses `E_AIAlertState` from [`BPC_AIPerceptionSystem`](BPC_AIPerceptionSystem.md).*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Structs
|
||||||
|
|
||||||
|
*No structs defined. Blackboard keys are primitive types.*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables — Blackboard Keys
|
||||||
|
|
||||||
|
### Standard Keys
|
||||||
|
|
||||||
|
| Key | Type | Description |
|
||||||
|
|-----|------|-------------|
|
||||||
|
| `TargetActor` | Object | Current pursuit target (player or other threat) |
|
||||||
|
| `LastKnownTargetLocation` | Vector | Last known position of the target |
|
||||||
|
| `AlertState` | E_AIAlertState | Current alert level (Unaware, Curious, Suspicious, Alerted, Engaged, Searching) |
|
||||||
|
| `PatrolTargetIndex` | Integer | Index of current patrol point in the patrol path |
|
||||||
|
| `bIsInvestigating` | Bool | Whether the agent is currently investigating |
|
||||||
|
| `InvestigateLocation` | Vector | Where the agent should go to investigate |
|
||||||
|
| `bTargetLost` | Bool | Whether the agent has lost sight/hearing of the target |
|
||||||
|
| `PlayerPlaystyleTag` | GameplayTag | Current classified playstyle of the player (Aggressive, Cautious, etc.) |
|
||||||
|
| `LastHeardLocation` | Vector | Most recent sound event location |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
*Blackboard is data-only. No functions defined at the asset level. Keys are read/written by behaviour tree nodes and perception component.*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
*No event dispatchers. Blackboard is a passive data store.*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Overridden Events / Custom Events
|
||||||
|
|
||||||
|
*None. This is a data asset, not an executable Blueprint.*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blueprint Graph Logic Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[BPC_AIPerceptionSystem sees player] --> B[Write TargetActor = Player]
|
||||||
|
B --> C[Write LastKnownTargetLocation = Player.Location]
|
||||||
|
A --> D[EscalateAlertState to Suspicious]
|
||||||
|
D --> E[Write AlertState = Suspicious]
|
||||||
|
E --> F[Behaviour Tree reads AlertState]
|
||||||
|
F --> G[Suspicious → Start investigating]
|
||||||
|
G --> H[Write bIsInvestigating = true]
|
||||||
|
H --> I[Write InvestigateLocation = LastKnownTargetLocation]
|
||||||
|
I --> J[BT MoveTo: InvestigateLocation]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Communication Matrix
|
||||||
|
|
||||||
|
| Who Talks | How | What Is Sent |
|
||||||
|
|-----------|-----|-------------|
|
||||||
|
| [`BPC_AIPerceptionSystem`](BPC_AIPerceptionSystem.md) | Blackboard write | `TargetActor`, `LastKnownTargetLocation`, `LastHeardLocation`, `AlertState`, `bTargetLost` |
|
||||||
|
| [`BPC_AIMemorySystem`](BPC_AIMemorySystem.md) | Blackboard write | `InvestigateLocation`, `bIsInvestigating` |
|
||||||
|
| [`BPC_BehaviourVariantSelector`](BPC_BehaviourVariantSelector.md) | Blackboard write | `PlayerPlaystyleTag` |
|
||||||
|
| [`AI_BaseAgentController`](AI_BaseAgentController.md) | Blackboard ownership | Initialises and holds the blackboard component |
|
||||||
|
| Behaviour Tree (`BT_Agent`) | Blackboard read/write | All keys — drives BT flow decisions |
|
||||||
|
| Patrol System (`BP_PatrolPath`) | Blackboard read/write | `PatrolTargetIndex` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Validation / Testing Checklist
|
||||||
|
|
||||||
|
- [ ] All 9 standard keys are present in the blackboard asset
|
||||||
|
- [ ] Key types match their expected usage (Object for TargetActor, Vector for locations, etc.)
|
||||||
|
- [ ] E_AIAlertState enum is properly referenced for the AlertState key
|
||||||
|
- [ ] GameplayTag type is configured for PlayerPlaystyleTag
|
||||||
|
- [ ] AI controller correctly assigns this blackboard on init
|
||||||
|
- [ ] Edge case: keys are present before perception system tries to write to them
|
||||||
|
- [ ] Edge case: behaviour tree graceful when TargetActor is None (cleared on target lost)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Reuse Notes
|
||||||
|
|
||||||
|
- This is a project-agnostic standard blackboard. Add project-specific keys in a child blackboard that inherits from this.
|
||||||
|
- All BT nodes should check key validity (`Is Set`) before reading to avoid errors
|
||||||
|
- Use `Blackboard Key Selector` pattern in BT tasks to allow designers to select which key to operate on
|
||||||
|
- New perception types (smell, damage) can write to `LastHeardLocation` or add new keys in a child blackboard
|
||||||
|
- `PatrolTargetIndex` is incremented by patrol tasks; reset to 0 on patrol path change
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Specification based on Master Section 10.2, line 2964.*
|
||||||
217
docs/blueprints/09-ai/86_BPC_AIMemorySystem.md
Normal file
217
docs/blueprints/09-ai/86_BPC_AIMemorySystem.md
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
# BPC_AIMemorySystem — AI Memory System
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- **Requires:** [`BB_AgentBoard`](BB_AgentBoard.md) — Writes `InvestigateLocation`, `bIsInvestigating` keys
|
||||||
|
- **Requires:** [`BPC_AIPerceptionSystem`](BPC_AIPerceptionSystem.md) — Receives sight/sound/damage stimuli
|
||||||
|
- **Required By:** [`AI_BaseAgentController`](AI_BaseAgentController.md) — Uses memory for investigation decisions
|
||||||
|
- **Engine/Plugin Requirements:** AI Module, GameplayTags
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Gives AI agents persistent short-term memory: where they last saw the player, what sounds they heard, which areas they've searched. Prevents AI from "forgetting" the player immediately on breaking line of sight and avoids re-searching already investigated locations.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Enums
|
||||||
|
|
||||||
|
```text
|
||||||
|
Enum Name: E_MemoryType
|
||||||
|
(DisplayName = "Memory Type")
|
||||||
|
|
||||||
|
Values:
|
||||||
|
Sighting = 0 // Visual detection of target
|
||||||
|
Sound = 1 // Heard noise/audio event
|
||||||
|
Damage = 2 // Received damage
|
||||||
|
Smell = 3 // Olfactory detection (future/optional)
|
||||||
|
ScriptedHint = 4 // Designer-placed hint (scripted memory injection)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Structs
|
||||||
|
|
||||||
|
### `S_AIMemoryEntry`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Location` | Vector | World position of the memory |
|
||||||
|
| `MemoryType` | E_MemoryType | What triggered this memory |
|
||||||
|
| `Timestamp` | Float | Game time when memory was recorded |
|
||||||
|
| `Confidence` | Float | 0.0–1.0 confidence in this memory (sighting = 1.0, sound = variable) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
|
||||||
|
### Configuration (Instance Editable, Expose On Spawn)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `MemoryDecayTime` | Float | 30.0 | MemoryConfig | Seconds before a memory expires and is removed |
|
||||||
|
| `MaxMemoryEntries` | Integer | 20 | MemoryConfig | Maximum concurrent memory entries |
|
||||||
|
| `SearchRadius` | Float | 500.0 | MemoryConfig | Min distance between searched locations to consider them distinct |
|
||||||
|
|
||||||
|
### Internal (Private / Protected, No Expose)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `MemoryEntries` | Array of S_AIMemoryEntry | Empty | Internal | All active timestamped memory records |
|
||||||
|
| `SearchedLocations` | Array of Vector | Empty | Internal | World positions already investigated |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
### Public Functions
|
||||||
|
|
||||||
|
#### `AddMemoryEntry` → `void`
|
||||||
|
- **Description:** Records a new memory entry from perception stimulus.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Location` | Vector | World position of event |
|
||||||
|
| `MemoryType` | E_MemoryType | What kind of stimulus |
|
||||||
|
| `Confidence` | Float | How certain the memory is (0.0–1.0) |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
- **Flow:** Create S_AIMemoryEntry → populate fields → append to MemoryEntries → trim if over MaxMemoryEntries → broadcast OnMemoryAdded
|
||||||
|
|
||||||
|
#### `GetLatestMemoryOfType` → `S_AIMemoryEntry`
|
||||||
|
- **Description:** Returns the most recent memory entry of a specific type. Returns empty/invalid entry if none found.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `MemoryType` | E_MemoryType | Filter by type |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `GetAllRecentMemories` → `Array of S_AIMemoryEntry`
|
||||||
|
- **Description:** Returns all non-expired memory entries sorted by timestamp (newest first).
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `HasSearchedLocation` → `Bool`
|
||||||
|
- **Description:** Checks if a location (within SearchRadius) has already been investigated.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Location` | Vector | World position to check |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `MarkLocationSearched` → `void`
|
||||||
|
- **Description:** Records a location as having been searched.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Location` | Vector | World position searched |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
- **Flow:** Add Location to SearchedLocations → push InvestigateLocation to blackboard if more memories exist
|
||||||
|
|
||||||
|
#### `ClearMemories` → `void`
|
||||||
|
- **Description:** Wipes all memory entries and searched locations. Called on alert reset or agent death.
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `TickMemoryDecay` → `void`
|
||||||
|
- **Description:** Called on tick to remove expired memories based on MemoryDecayTime.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `DeltaTime` | Float | Frame delta time |
|
||||||
|
- **Flow:** Iterate MemoryEntries → remove those where `(CurrentTime - Timestamp) > MemoryDecayTime`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Parameters | Bind Access | Description |
|
||||||
|
|------------|-----------|-------------|-------------|
|
||||||
|
| `OnMemoryAdded` | Entry: S_AIMemoryEntry | Public | New memory recorded |
|
||||||
|
| `OnMemoryExpired` | Entry: S_AIMemoryEntry | Public | Memory decayed and removed |
|
||||||
|
| `OnLocationSearched` | Location: Vector | Public | Location marked as searched |
|
||||||
|
| `OnAllLocationsSearched` | — | Public | No more unsearched memory locations remain |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Overridden Events / Custom Events
|
||||||
|
|
||||||
|
### Event: `ReceivePerceptionStimulus`
|
||||||
|
- **Description:** Subscribes to [`BPC_AIPerceptionSystem`](BPC_AIPerceptionSystem.md) dispatchers on BeginPlay. Routes sight, sound, and damage events to memory.
|
||||||
|
- **Flow:**
|
||||||
|
1. On sighting: AddMemoryEntry(Location, Sighting, 1.0)
|
||||||
|
2. On sound: AddMemoryEntry(Location, Sound, 0.5–0.9 based on distance)
|
||||||
|
3. On damage: AddMemoryEntry(InstigatorLocation, Damage, 1.0)
|
||||||
|
4. Write InvestigateLocation to blackboard if not already investigating
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blueprint Graph Logic Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[BeginPlay] --> B[Subscribe to Perception dispatchers]
|
||||||
|
B --> C[Idle]
|
||||||
|
C --> D{Perception event received?}
|
||||||
|
D --> E[Create S_AIMemoryEntry]
|
||||||
|
E --> F[Add to MemoryEntries]
|
||||||
|
F --> G{MemoryEntries count >= Max?}
|
||||||
|
G -->|Yes| H[Remove oldest entry]
|
||||||
|
G -->|No| I[Broadcast OnMemoryAdded]
|
||||||
|
H --> I
|
||||||
|
I --> J{Agent not investigating?}
|
||||||
|
J -->|Yes| K[Write InvestigateLocation to BB]
|
||||||
|
J -->|No| L[Skip — already busy]
|
||||||
|
K --> L
|
||||||
|
L --> C
|
||||||
|
|
||||||
|
subgraph Tick
|
||||||
|
M[TickMemoryDecay] --> N{Entry expired?}
|
||||||
|
N -->|Yes| O[Broadcast OnMemoryExpired]
|
||||||
|
N -->|No| P[Keep entry]
|
||||||
|
O --> Q[Remove from MemoryEntries]
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Communication Matrix
|
||||||
|
|
||||||
|
| Who Talks | How | What Is Sent |
|
||||||
|
|-----------|-----|-------------|
|
||||||
|
| [`BPC_AIPerceptionSystem`](BPC_AIPerceptionSystem.md) | Dispatcher | Sighting: Location+Confidence, Sound: Location+Volume, Damage: InstigatorLocation |
|
||||||
|
| [`BB_AgentBoard`](BB_AgentBoard.md) | Blackboard write | `InvestigateLocation`, `bIsInvestigating` |
|
||||||
|
| [`AI_BaseAgentController`](AI_BaseAgentController.md) | Direct (owns) | Tick calls for memory decay |
|
||||||
|
| Behaviour Tree (`BT_Investigate`) | Direct read | `GetLatestMemoryOfType`, `HasSearchedLocation` |
|
||||||
|
| [`BPC_EncounterDirector`](../10-adaptive/BPC_ProceduralEncounter.md) | Direct read | Memory entries for encounter escalation decisions |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Validation / Testing Checklist
|
||||||
|
|
||||||
|
- [ ] E_MemoryType enum has all 5 values defined
|
||||||
|
- [ ] S_AIMemoryEntry struct has 4 fields (Location, MemoryType, Timestamp, Confidence)
|
||||||
|
- [ ] AddMemoryEntry correctly appends and trims to MaxMemoryEntries
|
||||||
|
- [ ] GetLatestMemoryOfType returns newest entry filtered by type
|
||||||
|
- [ ] HasSearchedLocation returns true for locations within SearchRadius of a searched point
|
||||||
|
- [ ] TickMemoryDecay correctly removes entries older than MemoryDecayTime
|
||||||
|
- [ ] ClearMemories wipes both MemoryEntries and SearchedLocations
|
||||||
|
- [ ] Edge case: no memories of requested type returns empty struct
|
||||||
|
- [ ] Edge case: memory decay on empty array does nothing
|
||||||
|
- [ ] Edge case: rapid perception events don't overflow (MaxMemoryEntries cap works)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Reuse Notes
|
||||||
|
|
||||||
|
- MemoryDecayTime can be tuned per archetype (patrol guards forget quickly, stalker remembers longer)
|
||||||
|
- SearchedLocations prevents AI from repeatedly checking the same hiding spot
|
||||||
|
- Confidence parameter supports fuzzy memory: sounds at edge of range have lower confidence, AI may not act on low-confidence memories
|
||||||
|
- Memory system is independent of behaviour tree logic — BT reads from memory API to make decisions
|
||||||
|
- For co-op/multiplayer, add a shared memory blackboard for squad-level memory
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Specification based on Master Section 10.4, line 3012.*
|
||||||
138
docs/blueprints/09-ai/87_BPC_AIPerceptionSystem.md
Normal file
138
docs/blueprints/09-ai/87_BPC_AIPerceptionSystem.md
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
# BPC_AIPerceptionSystem — AI Perception System
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`AI_BaseAgentController`](AI_BaseAgentController.md) — Owner controller
|
||||||
|
- [`BPC_AlertSystem`](60_BPC_AlertSystem.md) — Alert level processing
|
||||||
|
- `DA_AIProfile` — Perception config
|
||||||
|
- `Engine.AIPerception` — UE5's `UAIPerceptionComponent` (or custom)
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Encapsulates all sensory perception for AI: sight, hearing, smell, and damage detection. Processes stimulus data, performs line-of-sight checks, applies detection modifiers (lighting, noise, movement speed), and reports findings to the owner [`AI_BaseAgentController`](AI_BaseAgentController.md). Supports configurable sensor ranges, cone angles, and stimulus decay rates.
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `SightRange` | Float | Maximum sight distance (cm) |
|
||||||
|
| `SightHalfAngle` | Float | Cone half-angle (degrees) |
|
||||||
|
| `SightLossHalfAngle` | Float | Peripheral angle before losing target |
|
||||||
|
| `HearingRange` | Float | Maximum hearing distance (cm) |
|
||||||
|
| `bUseSight` | Bool | Enable sight perception |
|
||||||
|
| `bUseHearing` | Bool | Enable hearing perception |
|
||||||
|
| `bUseDamageSense` | Bool | React to being damaged |
|
||||||
|
| `DetectionMultiplier_Lighting` | Float | 0.5–2.0 based on ambient light |
|
||||||
|
| `DetectionMultiplier_Movement` | Float | 1.0 standing, 0.3 crouched, 0.1 still |
|
||||||
|
| `DetectionMultiplier_Noise` | Float | Noise volume factor |
|
||||||
|
| `MaxAge` | Float | Seconds before stimulus decays |
|
||||||
|
| `SightDelay` | Float | Time before initial detection |
|
||||||
|
| `bTargetInSight` | Bool | Current LOS status |
|
||||||
|
| `LastSightTime` | Float | Last successful sight check |
|
||||||
|
| `RegisteredStimuli` | Array<FStimulusInfo> | Current active stimuli |
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
| Struct | Fields | Description |
|
||||||
|
|--------|--------|-------------|
|
||||||
|
| `FStimulusInfo` | Type: EStimulusType, Source: AActor, Location: FVector, Strength: Float, Age: Float, bCurrentlyValid: Bool | A perceived event |
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
|
||||||
|
| Enum | Values | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| `EStimulusType` | Sight, Hearing, Damage, Smell, Vibration | Sensory channel |
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `InitializePerception` | Config: FPerceptionConfig | — | Set ranges, angles from profile |
|
||||||
|
| `OnSightStimulus` | Target: AActor | Bool | LOS check with modifiers |
|
||||||
|
| `OnHearingStimulus` | Location: FVector, NoiseTag: FGameplayTag, Loudness: Float | — | Sound detection |
|
||||||
|
| `OnDamageStimulus` | Instigator: AActor | — | Direct damage detection |
|
||||||
|
| `PerformLineOfSightCheck` | Target: AActor | Bool | Raycast with channel |
|
||||||
|
| `ApplyDetectionModifiers` | BaseChance: Float | Float | Apply darkness, movement, cover |
|
||||||
|
| `RegisterStimulus` | Info: FStimulusInfo | — | Add to active list |
|
||||||
|
| `ForgetStimulus` | Target: AActor | — | Clear specific source |
|
||||||
|
| `GetDominantStimulus` | — | FStimulusInfo | Highest priority active |
|
||||||
|
| `HasLineOfSightTo` | Target: AActor | Bool | Public wrapper |
|
||||||
|
| `GetSightStrength` | Target: AActor | Float | 0.0–1.0 detection confidence |
|
||||||
|
| `DecayStimuli` | DeltaTime: Float | — | Age all stimuli |
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired When |
|
||||||
|
|------|-----------|-----------|
|
||||||
|
| `OnTargetDetected` | Target: AActor, Strength: Float, Type: EStimulusType | New target detected |
|
||||||
|
| `OnTargetLost` | Target: AActor | Target out of perception |
|
||||||
|
| `OnLineOfSightChanged` | Target: AActor, bHasLOS: Bool | LOS state change |
|
||||||
|
| `OnSoundHeard` | Location: FVector, Loudness: Float | Sound detected |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[OnSightStimulus]
|
||||||
|
└─► If !bUseSight → return false
|
||||||
|
└─► If Distance > SightRange → return false
|
||||||
|
└─► If Angle > SightHalfAngle → return false (outside cone)
|
||||||
|
└─► // Modifier layer
|
||||||
|
ModChance = ApplyDetectionModifiers(1.0)
|
||||||
|
If Random < ModChance → return false (failed detection check)
|
||||||
|
└─► // LOS check
|
||||||
|
bHasLOS = PerformLineOfSightCheck(Target)
|
||||||
|
If !bHasLOS → return false
|
||||||
|
└─► // Confirmed
|
||||||
|
Strength = CalculateStrength(Distance, Angle, Modifiers)
|
||||||
|
FStimulusInfo = { Sight, Target, Location, Strength, 0, true }
|
||||||
|
RegisterStimulus(Info)
|
||||||
|
Broadcast OnTargetDetected(Target, Strength, Sight)
|
||||||
|
Return true
|
||||||
|
|
||||||
|
[ApplyDetectionModifiers]
|
||||||
|
└─► BaseChance *= DetectionMultiplier_Lighting (based on light channel)
|
||||||
|
└─► BaseChance *= DetectionMultiplier_Movement (based on target velocity)
|
||||||
|
└─► If target in cover → BaseChance *= 0.3 (partial cover) or 0.0 (full)
|
||||||
|
└─► Return FMath::Clamp(BaseChance, 0.0, 1.0)
|
||||||
|
|
||||||
|
[PerformLineOfSightCheck]
|
||||||
|
└─► Start = Owner Pawn EyeLocation
|
||||||
|
└─► End = Target.GetActorLocation + Target BaseEyeHeight
|
||||||
|
└─► Hit = LineTraceSingleByChannel(Channel = Visibility)
|
||||||
|
└─► Return Hit.Actor == Target || Hit.Actor == nullptr
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stimulus Priority Table
|
||||||
|
|
||||||
|
| Stimulus Type | Base Priority | Decay Rate | Triggers |
|
||||||
|
|---------------|--------------|-----------|----------|
|
||||||
|
| Damage | 100 | Instant forget on lost | Combat state |
|
||||||
|
| Sight (Combat) | 90 | 3 sec | Combat state |
|
||||||
|
| Sight (Suspicious) | 60 | 8 sec | Suspicious state |
|
||||||
|
| Hearing (Loud) | 50 | 5 sec | Investigate |
|
||||||
|
| Hearing (Quiet) | 20 | 10 sec | Brief suspicion |
|
||||||
|
| Vibration | 30 | 2 sec | Alert nearby |
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target | Method | Why |
|
||||||
|
|--------|--------|-----|
|
||||||
|
| [`AI_BaseAgentController`](AI_BaseAgentController.md) | Owner | Send stimulus results |
|
||||||
|
| [`BPC_AlertSystem`](60_BPC_AlertSystem.md) | Get from Owner | Raise alerts based on perception |
|
||||||
|
| [`BP_EnemyBase`](58_BP_EnemyBase.md) | Owner Pawn | Check eye location, movement speed |
|
||||||
|
| World | Direct | Line trace queries |
|
||||||
|
| Audio Manager | Event | Query noise levels |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
- Entirely data-driven via DA_AIProfile.PerceptionConfig
|
||||||
|
- Detection modifiers allow stealth mechanics (darkness, crouch, cover)
|
||||||
|
- Sight checks use UE5 AIPerception for stimulus system, but LOS logic is custom for accuracy
|
||||||
|
- Hearing range accepts any gameplay-tagged noise source (footsteps, doors, gunshots)
|
||||||
|
- Stimulus decay prevents infinite chase after target disappears
|
||||||
|
- Renamed from `BPC_PerceptionComponent` to `BPC_AIPerceptionSystem` per Master naming convention.
|
||||||
|
- Cross-references updated: `BPC_AIControllerBase` → `AI_BaseAgentController`.
|
||||||
185
docs/blueprints/09-ai/88_BPC_BehaviourVariantSelector.md
Normal file
185
docs/blueprints/09-ai/88_BPC_BehaviourVariantSelector.md
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
# BPC_BehaviourVariantSelector — Behaviour Variant Selector
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- **Requires:** [`BPC_PlaystyleClassifier`](../10-adaptive/BPC_PlaystyleClassifier.md) — Subscribes to `OnPlaystyleChanged` for variant selection
|
||||||
|
- **Requires:** [`BB_AgentBoard`](BB_AgentBoard.md) — Writes `PlayerPlaystyleTag` key
|
||||||
|
- **Requires:** [`DA_BehaviourVariant`](../14-data-assets/DA_BehaviourVariant.md) — Behaviour variant data assets
|
||||||
|
- **Required By:** [`AI_BaseAgentController`](AI_BaseAgentController.md) — Owns this component, uses variant for BT configuration
|
||||||
|
- **Required By:** Behaviour Tree nodes — Read `PlayerPlaystyleTag` from blackboard to adjust behaviour
|
||||||
|
- **Engine/Plugin Requirements:** GameplayTags
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Selects which behaviour variant the AI agent uses based on the player's classified playstyle tag. Adapts AI difficulty, aggression, patrol patterns, investigation behaviour, and reaction times to match the player's approach. Enables dynamic difficulty adjustment without designer intervention.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Enums
|
||||||
|
|
||||||
|
*Uses `E_PlaystyleTag` from [`BPC_PlaystyleClassifier`](../10-adaptive/BPC_PlaystyleClassifier.md) — Aggressive, Cautious, Explorer, Passive, Speedrunner, Completionist (used as Gameplay Tags).*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Structs
|
||||||
|
|
||||||
|
*No new structs defined. Uses `DA_BehaviourVariant` data asset rows for variant configuration.*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
|
||||||
|
### Configuration (Instance Editable, Expose On Spawn)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `VariantMap` | Map (GameplayTag → DA_BehaviourVariant) | Empty | BehaviourConfig | Playstyle tag to behaviour variant data asset lookup |
|
||||||
|
| `DefaultVariant` | DA_BehaviourVariant | None | BehaviourConfig | Fallback variant used when no mapping exists for current playstyle |
|
||||||
|
|
||||||
|
### Internal (Private / Protected, No Expose)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `CurrentVariant` | DA_BehaviourVariant | None | Internal | Currently active behaviour variant |
|
||||||
|
| `bInitialised` | Bool | False | Internal | Whether initial variant has been applied |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
### Public Functions
|
||||||
|
|
||||||
|
#### `SelectVariant` → `DA_BehaviourVariant`
|
||||||
|
- **Description:** Looks up the behaviour variant for a given playstyle tag. Returns DefaultVariant if no mapping exists.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `PlaystyleTag` | GameplayTag | Player's current playstyle classification |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
- **Flow:** Check VariantMap for PlaystyleTag → if found, return mapped DA_BehaviourVariant → else return DefaultVariant
|
||||||
|
|
||||||
|
#### `ApplyVariant` → `void`
|
||||||
|
- **Description:** Applies a behaviour variant by writing its properties to the blackboard and configuring agent parameters.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Variant` | DA_BehaviourVariant | Variant to apply |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
- **Flow:** Write variant tag to BB `PlayerPlaystyleTag` → update perception config if variant changes it → update patrol speed if variant changes it → broadcast OnVariantApplied
|
||||||
|
|
||||||
|
#### `GetCurrentVariant` → `DA_BehaviourVariant`
|
||||||
|
- **Description:** Returns the currently active behaviour variant.
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `ResetToDefault` → `void`
|
||||||
|
- **Description:** Reverts to the DefaultVariant regardless of current playstyle. Used when AI is force-reset or despawned.
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Parameters | Bind Access | Description |
|
||||||
|
|------------|-----------|-------------|-------------|
|
||||||
|
| `OnVariantChanged` | OldVariant: DA_BehaviourVariant, NewVariant: DA_BehaviourVariant | Public | Variant selection changed |
|
||||||
|
| `OnVariantApplied` | Variant: DA_BehaviourVariant | Public | Variant successfully written to blackboard and configured |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Overridden Events / Custom Events
|
||||||
|
|
||||||
|
### Event: `BeginPlay`
|
||||||
|
- **Description:** Subscribes to [`BPC_PlaystyleClassifier`](../10-adaptive/BPC_PlaystyleClassifier.md)`OnPlaystyleChanged` dispatcher. Applies the initial variant.
|
||||||
|
- **Flow:**
|
||||||
|
1. Subscribe to BPC_PlaystyleClassifier.OnPlaystyleChanged
|
||||||
|
2. If BPC_PlaystyleClassifier exists, get CurrentPlaystyleTag
|
||||||
|
3. SelectVariant(CurrentPlaystyleTag) → ApplyVariant
|
||||||
|
4. Set bInitialised = true
|
||||||
|
|
||||||
|
### Event: `OnPlaystyleChanged`
|
||||||
|
- **Description:** Called when the player's playstyle classification changes. Re-evaluates variant selection.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `NewPlaystyleTag` | GameplayTag | Updated playstyle classification |
|
||||||
|
- **Flow:**
|
||||||
|
1. NewVariant = SelectVariant(NewPlaystyleTag)
|
||||||
|
2. If NewVariant != CurrentVariant → ApplyVariant(NewVariant)
|
||||||
|
3. Broadcast OnVariantChanged
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blueprint Graph Logic Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[BeginPlay] --> B[Subscribe to BPC_PlaystyleClassifier.OnPlaystyleChanged]
|
||||||
|
B --> C{PlaystyleClassifier exists?}
|
||||||
|
C -->|Yes| D[Get CurrentPlaystyleTag]
|
||||||
|
C -->|No| E[Apply DefaultVariant]
|
||||||
|
D --> F[SelectVariant from VariantMap]
|
||||||
|
E --> G[Write PlayerPlaystyleTag to BB]
|
||||||
|
F --> H{Variant found in map?}
|
||||||
|
H -->|Yes| I[CurrentVariant = mapped DA_BehaviourVariant]
|
||||||
|
H -->|No| J[CurrentVariant = DefaultVariant]
|
||||||
|
I --> G
|
||||||
|
J --> G
|
||||||
|
G --> K[Broadcast OnVariantApplied]
|
||||||
|
K --> L[bInitialised = true]
|
||||||
|
|
||||||
|
M[OnPlaystyleChanged fires] --> N[SelectVariant new tag]
|
||||||
|
N --> O{NewVariant != CurrentVariant?}
|
||||||
|
O -->|Yes| P[ApplyVariant]
|
||||||
|
O -->|No| Q[No change needed]
|
||||||
|
P --> R[Broadcast OnVariantChanged]
|
||||||
|
R --> L
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Communication Matrix
|
||||||
|
|
||||||
|
| Who Talks | How | What Is Sent |
|
||||||
|
|-----------|-----|-------------|
|
||||||
|
| [`BPC_PlaystyleClassifier`](../10-adaptive/BPC_PlaystyleClassifier.md) | Dispatcher (`OnPlaystyleChanged`) | `NewPlaystyleTag: GameplayTag` |
|
||||||
|
| [`BB_AgentBoard`](BB_AgentBoard.md) | Blackboard write | `PlayerPlaystyleTag` |
|
||||||
|
| [`AI_BaseAgentController`](AI_BaseAgentController.md) | Direct (owns) | Tick calls, init |
|
||||||
|
| Behaviour Tree (`BT_Agent`) | Blackboard read | `PlayerPlaystyleTag` → BT switches behaviour subtree |
|
||||||
|
| [`BPC_AIPerceptionSystem`](BPC_AIPerceptionSystem.md) | Direct (on same agent) | Reconfigure perception radius, reaction time from variant data |
|
||||||
|
| [`DA_BehaviourVariant`](../14-data-assets/DA_BehaviourVariant.md) | Data asset read | Variant stats: AggressionLevel, PatrolSpeed, ReactionTime, SearchRadius, etc. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Validation / Testing Checklist
|
||||||
|
|
||||||
|
- [ ] VariantMap is populated with entries for all E_PlaystyleTag values
|
||||||
|
- [ ] DefaultVariant is assigned as fallback
|
||||||
|
- [ ] SelectVariant returns DefaultVariant when playstyle tag is not in VariantMap
|
||||||
|
- [ ] ApplyVariant correctly writes PlayerPlaystyleTag to blackboard
|
||||||
|
- [ ] OnPlaystyleChanged triggers variant re-evaluation
|
||||||
|
- [ ] OnVariantChanged fires when variant actually changes (not on same variant)
|
||||||
|
- [ ] ResetToDefault reverts to DefaultVariant correctly
|
||||||
|
- [ ] Edge case: BPC_PlaystyleClassifier not present → DefaultVariant applied immediately
|
||||||
|
- [ ] Edge case: rapid playstyle changes → only last variant wins
|
||||||
|
- [ ] Edge case: invalid DA_BehaviourVariant in map → fall back to DefaultVariant with warning
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Reuse Notes
|
||||||
|
|
||||||
|
- VariantMap is designer-configurable — each AI archetype can have different playstyle-to-variant mappings
|
||||||
|
- DefaultVariant ensures AI always has valid behaviour even without a playstyle classifier
|
||||||
|
- For non-adaptive games, set VariantMap to empty and use only DefaultVariant
|
||||||
|
- Behaviour tree uses PlayerPlaystyleTag to switch between behaviour subtrees (aggressive chase vs cautious patrol, etc.)
|
||||||
|
- Extend DA_BehaviourVariant with project-specific fields for combat style, dialogue behaviour, etc.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Specification based on Master Section 10.5, line 3039.*
|
||||||
203
docs/blueprints/10-adaptive/100_BPC_RareEventSystem.md
Normal file
203
docs/blueprints/10-adaptive/100_BPC_RareEventSystem.md
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
# BPC_RareEventSystem — Rare Event System
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- **Requires:** [`DA_RareEvent`](../14-data-assets/DA_RareEvent.md) — Rare event definitions with probability and conditions
|
||||||
|
- **Requires:** [`BPC_NarrativeStateSystem`](../07-narrative/38_BPC_NarrativeStateSystem.md) — Reads narrative flags for eligibility conditions
|
||||||
|
- **Required By:** [`BPC_AdaptiveEnvironmentDirector`](BPC_AdaptiveEnvironmentDirector.md) — Requests rare event roll
|
||||||
|
- **Engine/Plugin Requirements:** GameplayTags, Timer system, Random stream
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Controls ultra-rare, randomised world events that can occur once per session or with very low probability: hidden messages, brief hallucinations, entity sightings, environmental easter eggs. Tracks which rare events have already fired to prevent repeats and evaluates complex eligibility conditions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Enums
|
||||||
|
|
||||||
|
*No new enums defined. Uses GameplayTags for event and condition identification.*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Structs
|
||||||
|
|
||||||
|
### `S_RareEventCondition`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `RequiredFlags` | Array of GameplayTag | All must be true for eligibility |
|
||||||
|
| `ForbiddenFlags` | Array of GameplayTag | None must be true for eligibility |
|
||||||
|
| `RequiredPlaystyleTag` | GameplayTag | Player must currently have this playstyle |
|
||||||
|
| `MinSessionTime` | Float | Minimum session elapsed seconds |
|
||||||
|
| `MinDeathCount` | Integer | Minimum player deaths this session |
|
||||||
|
| `RequiredChapterTag` | GameplayTag | Current chapter must match |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
|
||||||
|
### Configuration (Instance Editable, Expose On Spawn)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `RareEventPool` | Array of DA_RareEvent | Empty | RareEventConfig | All available rare events for this session |
|
||||||
|
|
||||||
|
### Internal (Private / Protected, No Expose)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `FiredRareEvents` | Set of GameplayTag | Empty | Internal | Already triggered rare events (prevent repeat) |
|
||||||
|
| `EligibilityConditions` | Map (GameplayTag → S_RareEventCondition) | Empty | Internal | Resolved eligibility conditions per event |
|
||||||
|
| `RollTimer` | TimerHandle | — | Internal | Periodic roll timer |
|
||||||
|
| `RollInterval` | Float | 60.0 | Internal | Seconds between rare event roll checks |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
### Public Functions
|
||||||
|
|
||||||
|
#### `RollForRareEvent` → `DA_RareEvent`
|
||||||
|
- **Description:** Evaluates all eligible rare events, rolls probability, and returns a selected event (or None). Fires the event if one is selected.
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
- **Flow:** Filter pool by eligibility → exclude fired events → for each eligible, roll probability → if roll passes, select and fire → add to FiredRareEvents → broadcast OnRareEventFired → return selected event
|
||||||
|
|
||||||
|
#### `IsEventEligible` → `Bool`
|
||||||
|
- **Description:** Checks if a specific rare event's conditions are currently met.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `EventTag` | GameplayTag | Rare event identifier |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
- **Flow:** Read S_RareEventCondition from map → check RequiredFlags against narrative state → check ForbiddenFlags → check playstyle match → check session time → check death count → check chapter → return true if all pass
|
||||||
|
|
||||||
|
#### `ForceRareEvent` → `void`
|
||||||
|
- **Description:** Fires a specific rare event immediately, bypassing probability and eligibility checks.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `EventTag` | GameplayTag | Rare event to fire |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `GetFiredEvents` → `Set of GameplayTag`
|
||||||
|
- **Description:** Returns all rare events that have already fired this session.
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `GetEligibleEvents` → `Array of DA_RareEvent`
|
||||||
|
- **Description:** Returns all rare events that are currently eligible (conditions met, not yet fired).
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `ResetSession` → `void`
|
||||||
|
- **Description:** Clears fired events and resets eligibility. Called on new game.
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `SetRollInterval` → `void`
|
||||||
|
- **Description:** Adjusts the periodic roll check interval.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Interval` | Float | New interval in seconds |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Parameters | Bind Access | Description |
|
||||||
|
|------------|-----------|-------------|-------------|
|
||||||
|
| `OnRareEventFired` | EventTag: GameplayTag | Public | A rare event was triggered |
|
||||||
|
| `OnRareEventFailed` | EventTag: GameplayTag | Public | Probability roll failed for an eligible event |
|
||||||
|
| `OnAllRareEventsFired` | — | Public | All events in pool have been fired |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Overridden Events / Custom Events
|
||||||
|
|
||||||
|
### Event: `BeginPlay`
|
||||||
|
- **Description:** Initialises the rare event pool and starts the periodic roll timer.
|
||||||
|
- **Flow:**
|
||||||
|
1. Build EligibilityConditions map from RareEventPool entries
|
||||||
|
2. Start RollTimer with RollInterval
|
||||||
|
3. Perform initial roll check
|
||||||
|
|
||||||
|
### Event: `TickRoll`
|
||||||
|
- **Description:** Timer callback. Performs a rare event roll check.
|
||||||
|
- **Flow:**
|
||||||
|
1. Call RollForRareEvent
|
||||||
|
2. If event fired → broadcast OnRareEventFired
|
||||||
|
3. If all events in pool are fired → stop timer and broadcast OnAllRareEventsFired
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blueprint Graph Logic Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[BeginPlay] --> B[Build EligibilityConditions map]
|
||||||
|
B --> C[Start RollTimer at RollInterval]
|
||||||
|
C --> D[Timer fires]
|
||||||
|
D --> E[GetEligibleEvents]
|
||||||
|
E --> F{Any eligible events?}
|
||||||
|
F -->|No| G[Wait for next tick]
|
||||||
|
F -->|Yes| H[For each eligible event:]
|
||||||
|
H --> I[Roll random against DA_RareEvent.Probability]
|
||||||
|
I --> J{Roll <= Probability?}
|
||||||
|
J -->|Yes| K[Fire the rare event]
|
||||||
|
J -->|No| L[Broadcast OnRareEventFailed]
|
||||||
|
K --> M[Add to FiredRareEvents]
|
||||||
|
M --> N[Broadcast OnRareEventFired]
|
||||||
|
N --> O{All events fired?}
|
||||||
|
O -->|Yes| P[Stop RollTimer]
|
||||||
|
O -->|No| G
|
||||||
|
L --> H
|
||||||
|
P --> Q[Broadcast OnAllRareEventsFired]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Communication Matrix
|
||||||
|
|
||||||
|
| Who Talks | How | What Is Sent |
|
||||||
|
|-----------|-----|-------------|
|
||||||
|
| [`BPC_AdaptiveEnvironmentDirector`](BPC_AdaptiveEnvironmentDirector.md) | Direct call | `RollForRareEvent()` |
|
||||||
|
| [`BPC_NarrativeStateSystem`](../07-narrative/38_BPC_NarrativeStateSystem.md) | Direct read | Narrative flags for eligibility condition checks |
|
||||||
|
| [`BPC_PlaystyleClassifier`](BPC_PlaystyleClassifier.md) | Direct read | `CurrentPlaystyleTag` for playstyle-gated events |
|
||||||
|
| [`GS_CoreGameState`](../01-core/06_GS_CoreGameState.md) | Direct read | `ElapsedPlayTime`, `ActiveChapterTag` |
|
||||||
|
| [`DA_RareEvent`](../14-data-assets/DA_RareEvent.md) | Data asset read | Event def: tag, probability, conditions, actions |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Validation / Testing Checklist
|
||||||
|
|
||||||
|
- [ ] S_RareEventCondition struct has all 6 fields
|
||||||
|
- [ ] RareEventPool is populated with DA_RareEvent assets
|
||||||
|
- [ ] IsEventEligible correctly checks all condition types
|
||||||
|
- [ ] RollForRareEvent respects probability values (0.0 = never, 1.0 = always)
|
||||||
|
- [ ] FiredRareEvents prevents repeat triggering
|
||||||
|
- [ ] ForceRareEvent bypasses all checks for scripted/debug use
|
||||||
|
- [ ] RollTimer stops when all events exhausted
|
||||||
|
- [ ] Edge case: RareEventPool empty → no errors, timer still runs harmlessly
|
||||||
|
- [ ] Edge case: all events fired → OnAllRareEventsFired broadcasts once
|
||||||
|
- [ ] Edge case: rapid probability rolls don't fire same event twice (FiredRareEvents set check)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Reuse Notes
|
||||||
|
|
||||||
|
- RareEventPool is population-defined — populate per project with unique events
|
||||||
|
- Probability values in DA_RareEvent are designer-tuned per event (0.001 for ultra-rare, 0.1 for uncommon)
|
||||||
|
- Eligibility conditions support complex gating (chapter, playstyle, time, death count)
|
||||||
|
- For non-horror games, fill pool with easter eggs and hidden secrets instead of scary events
|
||||||
|
- Extend S_RareEventCondition with project-specific gates (e.g., specific item held, specific ending achieved)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Specification based on Master Section 9.9, line 2922.*
|
||||||
213
docs/blueprints/10-adaptive/101_BPC_ScareEventSystem.md
Normal file
213
docs/blueprints/10-adaptive/101_BPC_ScareEventSystem.md
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
# BPC_ScareEventSystem — Scare Event System
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- **Requires:** [`BPC_StressSystem`](../02-player/10_BPC_StressSystem.md) — Reads current stress tier to set ActiveScareTier
|
||||||
|
- **Requires:** [`DA_ScareEvent`](../14-data-assets/DA_ScareEvent.md) — Scare event definitions
|
||||||
|
- **Required By:** [`SS_AudioManager`](../10-adaptive/132_SS_AudioManager.md) — Trigger stingers (via `PlayMusicStinger()`)
|
||||||
|
- **Required By:** [`BPC_LightEventController`](BPC_LightEventController.md) — Trigger scare lighting
|
||||||
|
- **Required By:** `I_ScareTarget` actors — Broadcast scare events to world actors
|
||||||
|
- **Required By:** [`WBP_ScreenEffectController`](../06-ui/WBP_ScreenEffectController.md) — Jump scare flash
|
||||||
|
- **Engine/Plugin Requirements:** GameplayTags, Audio, Timer system
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Manages the scheduling and firing of scare events: jump scares, ambient scares, audio stingers, and environmental anomalies. Enforces scare cooldowns to prevent scare saturation, selects appropriate scare tier from player stress level, and tracks scare history to avoid repetition.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Enums
|
||||||
|
|
||||||
|
```text
|
||||||
|
Enum Name: E_ScareTier
|
||||||
|
(DisplayName = "Scare Tier")
|
||||||
|
|
||||||
|
Values:
|
||||||
|
Ambient = 0 // Background unease: creaks, whispers, distant sounds
|
||||||
|
Minor = 1 // Small startle: object falls, door creaks nearby
|
||||||
|
Moderate = 2 // Noticeable scare: shadow figure, loud noise
|
||||||
|
Major = 3 // Intense scare: entity appears, lights fail
|
||||||
|
JumpScare = 4 // Full jump scare: flash, stinger, entity lunge
|
||||||
|
Cinematic = 5 // Scripted cinematic scare sequence
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Structs
|
||||||
|
|
||||||
|
*No new structs defined. Uses [`DA_ScareEvent`](../14-data-assets/DA_ScareEvent.md) for event definitions.*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
|
||||||
|
### Configuration (Instance Editable, Expose On Spawn)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `ScareEventPool` | Array of DA_ScareEvent | Empty | ScareConfig | All available scare events for this level |
|
||||||
|
| `ScareCooldownMin` | Float | 30.0 | ScareConfig | Minimum seconds between scare events |
|
||||||
|
|
||||||
|
### Internal (Private / Protected, No Expose)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `LastScareTime` | Float | 0.0 | Internal | Game time of last scare event |
|
||||||
|
| `ActiveScareTier` | E_ScareTier | Ambient | Internal | Current scare tier matched to stress tier |
|
||||||
|
| `ScareHistory` | Array of GameplayTag | Empty | Internal | Recently fired scare event tags (avoid repeat) |
|
||||||
|
| `ScareHistoryMaxSize` | Integer | 10 | Internal | Max history entries before oldest are removed |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
### Public Functions
|
||||||
|
|
||||||
|
#### `RequestScareEvent` → `Bool`
|
||||||
|
- **Description:** Checks conditions and fires a scare event if cooldown passed and valid events available. Returns true if a scare was fired.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `TargetTier` | E_ScareTier | Desired scare intensity (None = use ActiveScareTier) |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
- **Flow:** Check cooldown → filter pool by tier → exclude history → select event → execute → add to history → update timer
|
||||||
|
|
||||||
|
#### `SetActiveScareTier` → `void`
|
||||||
|
- **Description:** Updates the active scare tier. Called when stress tier changes.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Tier` | E_ScareTier | New active scare tier |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `GetActiveScareTier` → `E_ScareTier`
|
||||||
|
- **Description:** Returns the currently active scare tier.
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `ExecuteScareEvent` → `void`
|
||||||
|
- **Description:** Executes a specific scare event immediately, bypassing cooldown and history checks.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `ScareEvent` | DA_ScareEvent | Specific event to fire |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
- **Flow:** Route to audio → route to lighting → route to screen effects → broadcast I_ScareTarget
|
||||||
|
|
||||||
|
#### `GetEligibleScares` → `Array of DA_ScareEvent`
|
||||||
|
- **Description:** Returns all scare events eligible for the current tier, excluding recently fired ones.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Tier` | E_ScareTier | Filter tier |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `ResetScareHistory` → `void`
|
||||||
|
- **Description:** Clears scare history, allowing all events to be eligible again. Called on level load.
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Parameters | Bind Access | Description |
|
||||||
|
|------------|-----------|-------------|-------------|
|
||||||
|
| `OnScareEventFired` | EventTag: GameplayTag, Tier: E_ScareTier | Public | Scare event executed |
|
||||||
|
| `OnScareTierChanged` | OldTier: E_ScareTier, NewTier: E_ScareTier | Public | Active scare tier changed |
|
||||||
|
| `OnScareCooldownExpired` | — | Public | Cooldown period elapsed, scares available again |
|
||||||
|
| `OnJumpScareTriggered` | EventTag: GameplayTag | Public | Jump scare specifically fired |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Overridden Events / Custom Events
|
||||||
|
|
||||||
|
### Event: `BeginPlay`
|
||||||
|
- **Description:** Subscribes to [`BPC_StressSystem`](../02-player/10_BPC_StressSystem.md)`OnStressTierChanged` to map stress to scare tier.
|
||||||
|
- **Flow:**
|
||||||
|
1. Find BPC_StressSystem and subscribe
|
||||||
|
2. Map initial stress tier to scare tier
|
||||||
|
3. Set ActiveScareTier
|
||||||
|
|
||||||
|
### Event: `OnStressTierChanged`
|
||||||
|
- **Description:** Called when player stress tier changes. Remaps stress tier to scare tier.
|
||||||
|
- **Flow:**
|
||||||
|
1. Map: Calm→Ambient, Low→Minor, Mid→Moderate, High→Major, Critical→JumpScare
|
||||||
|
2. SetActiveScareTier(mapped tier)
|
||||||
|
3. Broadcast OnScareTierChanged
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blueprint Graph Logic Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[BeginPlay] --> B[Subscribe to StressSystem.OnStressTierChanged]
|
||||||
|
B --> C[Set initial ActiveScareTier from stress]
|
||||||
|
C --> D[Idle]
|
||||||
|
|
||||||
|
D --> E{AdaptiveDirector requests scare?}
|
||||||
|
E --> F{ScareCooldown elapsed?}
|
||||||
|
F -->|No| G[Skip — on cooldown]
|
||||||
|
F -->|Yes| H[GetEligibleScares ActiveScareTier]
|
||||||
|
H --> I{Any eligible events?}
|
||||||
|
I -->|No| G
|
||||||
|
I -->|Yes| J[Select random from eligible]
|
||||||
|
J --> K[ExecuteScareEvent]
|
||||||
|
K --> L[Audio → SS_AudioManager.PlayMusicStinger]
|
||||||
|
K --> M[Visual → BPC_LightEventController + WBP_ScreenEffectController]
|
||||||
|
K --> N[World → Broadcast I_ScareTarget to tagged actors]
|
||||||
|
L --> O[Add to ScareHistory]
|
||||||
|
M --> O
|
||||||
|
N --> O
|
||||||
|
O --> P[Update LastScareTime]
|
||||||
|
P --> Q[Broadcast OnScareEventFired]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Communication Matrix
|
||||||
|
|
||||||
|
| Who Talks | How | What Is Sent |
|
||||||
|
|-----------|-----|-------------|
|
||||||
|
| [`BPC_StressSystem`](../02-player/10_BPC_StressSystem.md) | Dispatcher (`OnStressTierChanged`) | `NewTier: E_StressTier` — maps to E_ScareTier |
|
||||||
|
| [`BPC_AdaptiveEnvironmentDirector`](BPC_AdaptiveEnvironmentDirector.md) | Direct call | `RequestScareEvent(E_ScareTier)` |
|
||||||
|
| [`SS_AudioManager`](../10-adaptive/132_SS_AudioManager.md) | Direct call | `PlayMusicStinger(StingerAsset)` |
|
||||||
|
| [`BPC_LightEventController`](BPC_LightEventController.md) | Direct call | `TriggerLightEvent(EventTag)` |
|
||||||
|
| [`WBP_ScreenEffectController`](../06-ui/WBP_ScreenEffectController.md) | Direct call | `TriggerJumpScareFlash()` or `TriggerDamageFlash()` |
|
||||||
|
| `I_ScareTarget` actors | Interface broadcast | `OnScareEventFired(ScareTag)` |
|
||||||
|
| [`DA_ScareEvent`](../14-data-assets/DA_ScareEvent.md) | Data asset read | Event def: tier, tags, audio, visual, actor targets |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Validation / Testing Checklist
|
||||||
|
|
||||||
|
- [ ] E_ScareTier enum has all 6 values
|
||||||
|
- [ ] RequestScareEvent respects ScareCooldownMin
|
||||||
|
- [ ] ActiveScareTier correctly mapped from E_StressTier
|
||||||
|
- [ ] Eligible scares filtered by tier and excluded from history
|
||||||
|
- [ ] ExecuteScareEvent routes to audio, lighting, screen effects, and I_ScareTarget
|
||||||
|
- [ ] ScareHistory prevents repeating recent events
|
||||||
|
- [ ] OnJumpScareTriggered fires for E_ScareTier::JumpScare events
|
||||||
|
- [ ] Edge case: no eligible scares → RequestScareEvent returns false
|
||||||
|
- [ ] Edge case: all scares in pool exhausted → reset history or allow repeats
|
||||||
|
- [ ] Edge case: cooldown active during cinematic → scripted scares bypass cooldown via ExecuteScareEvent
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Reuse Notes
|
||||||
|
|
||||||
|
- ScareEventPool is populated per level — different levels have different scare content
|
||||||
|
- ScareCooldownMin prevents scare saturation; tune per project for desired horror intensity
|
||||||
|
- Stress-to-scare mapping is configurable: calm games can cap at Minor tier
|
||||||
|
- For non-horror games, leave ScareEventPool empty to disable the system
|
||||||
|
- I_ScareTarget interface lets any actor react to scares without coupling to this system
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Specification based on Master Section 9.5, line 2812.*
|
||||||
638
docs/blueprints/10-adaptive/132_SS_AudioManager.md
Normal file
638
docs/blueprints/10-adaptive/132_SS_AudioManager.md
Normal file
@@ -0,0 +1,638 @@
|
|||||||
|
# 132 — MetaSounds Audio Manager (`SS_AudioManager`)
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
Single entry point for all audio playback in the framework. Every system routes sound through this Game Instance Subsystem instead of calling `UGameplayStatics::PlaySound*` directly. Owns 4 MetaSound buses (SFX, Ambience, Music, Dialogue) → Master Bus, manages room-based reverb zones with crossfade, handles per-category volume from player settings, and accepts gameplay-driven parameter modulation (heart rate, stress, fear, music intensity).
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **Requires:** `SS_SettingsSystem` (105 — persistent volume settings), `BPC_StateManager` (130 — heart rate + vital signals), `BPC_StressSystem` (10 — stress tier), `BPC_PacingDirector` (98 — music intensity), `BPC_HealthSystem` (08 — player health for muffled audio), `BPC_FearSystem` (90 — fear audio modulation), `DA_AudioSettings` (134 — default config)
|
||||||
|
- **Required By:** ALL systems that play audio, `BP_RoomAudioZone` (133 — room preset changes), `WBP_SettingsMenu` (57 — volume sliders), `BPC_AudioAtmosphereController` (95 — DEPRECATED, replaced by this)
|
||||||
|
- **Engine/Plugin Requirements:** MetaSounds plugin (UE 5.5+ default audio engine), GameplayTags plugin
|
||||||
|
|
||||||
|
## Class Info
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Parent Class** | `GameInstanceSubsystem` |
|
||||||
|
| **Class Type** | Game Instance Subsystem |
|
||||||
|
| **Asset Path** | `Content/Framework/Audio/SS_AudioManager.uasset` |
|
||||||
|
| **Implements Interfaces** | *(none)* |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Enums
|
||||||
|
|
||||||
|
### E_AudioBusCategory — Audio Bus Categories (5 values)
|
||||||
|
|
||||||
|
```text
|
||||||
|
Enum Name: E_AudioBusCategory
|
||||||
|
(DisplayName = "Audio Bus Category")
|
||||||
|
|
||||||
|
Values:
|
||||||
|
SFX = 0 // Sound effects — spatial, occluded, surface-reactive
|
||||||
|
Ambience = 1 // Environmental background — room tones, weather
|
||||||
|
Music = 2 // Dynamic music — layers, intensity bands
|
||||||
|
Dialogue = 3 // VO and character dialogue — ducking priority
|
||||||
|
Master = 4 // Master output — overall volume
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Structs
|
||||||
|
|
||||||
|
### `S_AudioBusConfig`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `BusCategory` | `E_AudioBusCategory` | Which bus this config applies to |
|
||||||
|
| `DefaultVolume` | `Float` (0–1) | Default volume (pre-settings) |
|
||||||
|
| `MetaSoundSource` | `UMetaSoundSource` | The MetaSound patch for this bus |
|
||||||
|
| `bSpatialized` | `Boolean` | Use 3D spatialization? |
|
||||||
|
| `AttenuationSettings` | `USoundAttenuation` | Distance falloff curve |
|
||||||
|
| `MaxConcurrentSounds` | `Integer` | Voice limiting per bus |
|
||||||
|
| `DuckingPriority` | `Integer` | 0=lowest, 100=highest (dialogue=100) |
|
||||||
|
| `DuckAmount` | `Float` (0–1) | How much to duck other buses when this bus plays |
|
||||||
|
| `SettingsBindingTag` | `GameplayTag` | Tag for settings lookup (e.g., `Settings.Audio.SFXVolume`) |
|
||||||
|
|
||||||
|
### `S_RoomAcousticProfile`
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `RoomPresetName` | `FName` | Identifier for this room preset |
|
||||||
|
| `ReverbDensity` | `Float` (0–1) | How dense the reverb reflections are |
|
||||||
|
| `ReverbDecayTime` | `Float` (seconds) | RT60 decay time |
|
||||||
|
| `ReverbWetLevel` | `Float` (0–1) | Wet/dry mix for reverb |
|
||||||
|
| `ReflectionsDelay` | `Float` (ms) | Pre-delay before reflections start |
|
||||||
|
| `RoomSizeScale` | `Float` (0.5–3.0) | Perceived room size |
|
||||||
|
| `OcclusionMultiplier` | `Float` (0–1) | How much walls occlude sound (0=fully open, 1=fully blocked) |
|
||||||
|
| `LowPassCutoff` | `Float` (Hz) | Low-pass filter cutoff (simulates wall filtering) |
|
||||||
|
| `bApplyToSFX` | `Boolean` | Apply this room to SFX bus? |
|
||||||
|
| `bApplyToAmbience` | `Boolean` | Apply this room to ambience bus? |
|
||||||
|
| `bApplyToDialogue` | `Boolean` | Apply this room to dialogue bus? |
|
||||||
|
| `TransitionTime` | `Float` (seconds) | Crossfade time when entering this room |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
|
||||||
|
### Configuration (Instance Editable, Expose On Spawn)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `BusConfigs` | `Array<S_AudioBusConfig>` | `[SFX, Ambience, Music, Dialogue]` | `Audio Config` | Configuration per bus |
|
||||||
|
| `RoomPresets` | `Array<DA_RoomAcousticPreset>` | `DefaultPresets` | `Audio Config` | All room acoustic presets |
|
||||||
|
| `DefaultRoomPreset` | `DA_RoomAcousticPreset` | `Outdoor` | `Audio Config` | Fallback room preset |
|
||||||
|
| `AudioSettings` | `DA_AudioSettings` | `Defaults` | `Audio Config` | Data asset with volume defaults and bus config |
|
||||||
|
|
||||||
|
### Internal (Private / Protected)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `ActiveRoomPreset` | `DA_RoomAcousticPreset` | `None` | `Room` | Current room acoustic profile |
|
||||||
|
| `PreviousRoomPreset` | `DA_RoomAcousticPreset` | `None` | `Room` | Previous room (for crossfade) |
|
||||||
|
| `RoomTransitionProgress` | `Float` | `0.0` | `Room` | Crossfade progress 0→1 |
|
||||||
|
| `RoomTransitionDuration` | `Float` | `0.0` | `Room` | Current transition time remaining |
|
||||||
|
| `RoomZoneStack` | `Array<DA_RoomAcousticPreset>` | `Empty` | `Room` | Push/pop stack for nested room zones |
|
||||||
|
| `bIsMuted` | `Boolean` | `false` | `State` | Global mute toggle |
|
||||||
|
| `bIsPaused` | `Boolean` | `false` | `State` | Global pause state |
|
||||||
|
| `BusInstances` | `Map<E_AudioBusCategory, UAudioComponent>` | `Empty` | `Bus` | Active bus audio components |
|
||||||
|
| `ActiveSFXInstances` | `Array<UAudioComponent>` | `Empty` | `SFX` | Pooled SFX playback instances |
|
||||||
|
| `SFXPoolSize` | `Integer` | `32` | `SFX` | Max concurrent SFX instances |
|
||||||
|
| `bIsCrossfading` | `Boolean` | `false` | `Room` | Room transition in progress |
|
||||||
|
| `ActiveDialogueCount` | `Integer` | `0` | `Dialogue` | Number of active dialogue lines |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
### Public Playback Functions
|
||||||
|
|
||||||
|
#### `PlaySFX` → `UAudioComponent`
|
||||||
|
- **Description:** Play spatial SFX through SFX bus at world location. Surface tag selects material impact variant.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Sound` | `UMetaSoundSource` | The MetaSound source to play |
|
||||||
|
| `Location` | `FVector` | World position for spatialization |
|
||||||
|
| `SurfaceTag` | `GameplayTag` (optional) | Physical surface tag for material EQ variant |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
- **Flow:**
|
||||||
|
1. Get or create SFX audio component from pool
|
||||||
|
2. Set sound to `SFXBus` MetaSound source
|
||||||
|
3. Set world location
|
||||||
|
4. If SurfaceTag valid: set `SurfaceEQ` parameter on MetaSound
|
||||||
|
5. Play audio component
|
||||||
|
6. Return component reference
|
||||||
|
|
||||||
|
#### `PlaySFX2D` → `UAudioComponent`
|
||||||
|
- **Description:** Play non-spatial SFX through SFX bus (UI sounds, heartbeat).
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Sound` | `UMetaSoundSource` | The MetaSound source to play |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
- **Flow:** Same as PlaySFX but without world location — 2D playback.
|
||||||
|
|
||||||
|
#### `PlayAmbient` → `void`
|
||||||
|
- **Description:** Crossfade ambient layer on Ambience bus.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Sound` | `UMetaSoundSource` | Ambient layer MetaSound |
|
||||||
|
| `FadeIn` | `Float` (default 1.0) | Fade-in duration in seconds |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
- **Flow:**
|
||||||
|
1. Get Ambience bus audio component
|
||||||
|
2. Set new MetaSound source
|
||||||
|
3. Fade volume from 0 to target over `FadeIn` seconds
|
||||||
|
4. Route through current room reverb
|
||||||
|
|
||||||
|
#### `StopAmbient` → `void`
|
||||||
|
- **Description:** Fade out current ambient layer.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `FadeOut` | `Float` (default 2.0) | Fade-out duration in seconds |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `SetMusicLayer` → `void`
|
||||||
|
- **Description:** Set a music layer with intensity parameter.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `LayerIndex` | `Integer` | Which music layer (0-3: Calm/Tense/Action/Climax) |
|
||||||
|
| `Sound` | `UMetaSoundSource` | MetaSound source for this layer |
|
||||||
|
| `Intensity` | `Float` (default 0.5) | Layer blend intensity 0-1 |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `StopMusicLayers` → `void`
|
||||||
|
- **Description:** Fade out all music layers.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `FadeOut` | `Float` (default 2.0) | Fade-out duration in seconds |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `PlayMusicStinger` → `void`
|
||||||
|
- **Description:** One-shot music stinger on Music bus (overrides layers temporarily).
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Stinger` | `UMetaSoundSource` | Stinger MetaSound source |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `PlayDialogue` → `void`
|
||||||
|
- **Description:** Play dialogue line. Ducks other buses by configured amounts.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `DialogueSound` | `UMetaSoundSource` | Dialogue MetaSound source |
|
||||||
|
| `Priority` | `Integer` (default 0) | 0=Bark, 10=Quest, 100=Cinematic |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
- **Flow:**
|
||||||
|
1. If `ActiveDialogueCount >= MaxConcurrentDialogue`, check priority
|
||||||
|
2. If new priority > lowest active: interrupt lowest, play new
|
||||||
|
3. Apply ducking: SFX -6dB, Ambience -12dB, Music -18dB
|
||||||
|
4. Route dialogue through Dialogue bus with spatialization
|
||||||
|
|
||||||
|
#### `StopDialogue` → `void`
|
||||||
|
- **Description:** Stop current dialogue playback.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `FadeOut` | `Float` (default 0.5) | Fade-out duration in seconds |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
### Public Room Zone Functions
|
||||||
|
|
||||||
|
#### `SetRoomPreset` → `void`
|
||||||
|
- **Description:** Begin room transition with smooth crossfade of reverb/occlusion parameters.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Preset` | `DA_RoomAcousticPreset` | The new room acoustic preset |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
- **Flow:**
|
||||||
|
1. Push current preset onto `RoomZoneStack`
|
||||||
|
2. Set `PreviousRoomPreset = ActiveRoomPreset`
|
||||||
|
3. Set `ActiveRoomPreset = Preset`
|
||||||
|
4. Set `RoomTransitionDuration = Preset.TransitionTime`
|
||||||
|
5. Set `bIsCrossfading = true`
|
||||||
|
6. Fire `OnRoomPresetChanged(PreviousPreset, Preset)`
|
||||||
|
7. Begin per-tick interpolation (see `TickRoomTransition`)
|
||||||
|
|
||||||
|
#### `SetRoomPresetInstant` → `void`
|
||||||
|
- **Description:** Instant room switch — no crossfade. Used for void space entry, cutscenes.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Preset` | `DA_RoomAcousticPreset` | The new room preset to apply immediately |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `GetCurrentRoomPreset` → `DA_RoomAcousticPreset`
|
||||||
|
- **Description:** Returns the currently active room preset.
|
||||||
|
- **Parameters:** *(none)*
|
||||||
|
- **Blueprint Authority:** Any (Pure)
|
||||||
|
|
||||||
|
#### `PopRoomPreset` → `void`
|
||||||
|
- **Description:** Restores the previous room from the zone stack. Called by `BP_RoomAudioZone` on overlap end.
|
||||||
|
- **Parameters:** *(none)*
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
### Public Settings / Control Functions
|
||||||
|
|
||||||
|
#### `SetBusVolume` → `void`
|
||||||
|
- **Description:** Set volume for a specific bus category. Called from settings menu.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Bus` | `E_AudioBusCategory` | Which bus to adjust |
|
||||||
|
| `Volume` | `Float` (0–1) | Target volume |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
- **Flow:**
|
||||||
|
1. Set float parameter `Volume` on the corresponding MetaSound bus
|
||||||
|
2. Fire `OnBusVolumeChanged`
|
||||||
|
3. If `Bus == Master`, scale all buses proportionally
|
||||||
|
|
||||||
|
#### `GetBusVolume` → `Float`
|
||||||
|
- **Description:** Get current volume for a specific bus.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Bus` | `E_AudioBusCategory` | Which bus to query |
|
||||||
|
- **Blueprint Authority:** Any (Pure)
|
||||||
|
|
||||||
|
#### `SetMasterMute` → `void`
|
||||||
|
- **Description:** Mute/unmute all audio globally.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `bMuted` | `Boolean` | True to mute, false to unmute |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `SetPauseAll` → `void`
|
||||||
|
- **Description:** Pause/resume all audio (used when pause menu opens/closes).
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `bPaused` | `Boolean` | True to pause, false to resume |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
- **Flow:**
|
||||||
|
1. Set `bIsPaused = bPaused`
|
||||||
|
2. Set Paused on all bus audio components
|
||||||
|
3. Fire `OnAudioPaused(bPaused)`
|
||||||
|
|
||||||
|
#### `UpdateGameplayParameter` → `void`
|
||||||
|
- **Description:** Set a gameplay-driven MetaSound float parameter on the relevant bus.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `ParameterName` | `FName` | MetaSound parameter name (e.g., `MusicIntensity`) |
|
||||||
|
| `Value` | `Float` | Parameter value |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `UpdateHeartRateAudio` → `void`
|
||||||
|
- **Description:** Drives heartbeat SFX tempo and breathing layer intensity from StateManager data.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `BPM` | `Float` | Current heart rate in BPM (60-180) |
|
||||||
|
| `Signal` | `E_PlayerVitalSignals` | Vital signal tier |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
- **Flow:**
|
||||||
|
1. Set `HeartRateBPM` parameter on SFX bus
|
||||||
|
2. Set `VitalSignal` parameter (0-4 enum as int) on SFX bus
|
||||||
|
3. If heartbeat SFX not already playing: start looping heartbeat MetaSound
|
||||||
|
|
||||||
|
#### `UpdateFearAudio` → `void`
|
||||||
|
- **Description:** Drives fear-reactive audio modulation from StressSystem + FearSystem.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `FearLevel` | `Float` (0–1) | Current fear intensity |
|
||||||
|
| `Tier` | `E_StressTier` | Current stress tier |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
- **Flow:**
|
||||||
|
1. Set `FearIntensity` parameter on Ambience + SFX buses
|
||||||
|
2. Set `StressLevel` parameter on Ambience + Music buses
|
||||||
|
3. Apply fear effects: subtle pitch drop, reverb increase, random pan jitter
|
||||||
|
|
||||||
|
### Internal Functions (Private)
|
||||||
|
|
||||||
|
#### `InitializeBuses` → `void`
|
||||||
|
- **Description:** Create `UAudioComponent` for each bus category and attach MetaSound sources. Called during `Initialize`.
|
||||||
|
- **Flow:**
|
||||||
|
1. For each `BusConfig` in `BusConfigs`:
|
||||||
|
a. Create `UAudioComponent`
|
||||||
|
b. Set sound to `BusConfig.MetaSoundSource`
|
||||||
|
c. Configure spatialization from `BusConfig.bSpatialized`
|
||||||
|
d. Attach attenuation settings
|
||||||
|
e. Store in `BusInstances` map
|
||||||
|
2. Apply saved settings volumes to each bus
|
||||||
|
3. Set `ActiveRoomPreset = DefaultRoomPreset`
|
||||||
|
|
||||||
|
#### `TickRoomTransition` → `void`
|
||||||
|
- **Description:** Called on Tick — interpolates room reverb/occlusion/filter parameters during crossfade.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `DeltaTime` | `Float` | Frame time |
|
||||||
|
- **Flow:**
|
||||||
|
1. If `!bIsCrossfading`: return
|
||||||
|
2. `RoomTransitionProgress += DeltaTime`
|
||||||
|
3. If `RoomTransitionProgress >= RoomTransitionDuration`: clamp to 1.0, set `bIsCrossfading = false`
|
||||||
|
4. Alpha = `RoomTransitionProgress / RoomTransitionDuration`
|
||||||
|
5. Interpolate: ReverbDensity, ReverbDecayTime, ReverbWetLevel, ReflectionsDelay, RoomSizeScale, OcclusionMultiplier, LowPassCutoff between `PreviousRoomPreset` and `ActiveRoomPreset`
|
||||||
|
6. Apply to relevant buses based on `bApplyToSFX` / `bApplyToAmbience` / `bApplyToDialogue` flags
|
||||||
|
|
||||||
|
#### `GetOrCreateSFXInstance` → `UAudioComponent`
|
||||||
|
- **Description:** Pool management — reuse an idle SFX audio component or create a new one. If pool is full (32), cull the oldest instance.
|
||||||
|
- **Flow:**
|
||||||
|
1. Find first idle component in `ActiveSFXInstances`
|
||||||
|
2. If found: return it
|
||||||
|
3. If pool not full: create new, add to pool, return
|
||||||
|
4. If pool full: stop oldest active instance, reuse it
|
||||||
|
|
||||||
|
#### `ApplyRoomToBus` → `void`
|
||||||
|
- **Description:** Apply a room preset's reverb/occlusion/filter to a specific bus.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Bus` | `E_AudioBusCategory` | Target bus |
|
||||||
|
| `Preset` | `DA_RoomAcousticPreset` | Room preset to apply |
|
||||||
|
|
||||||
|
#### `ApplySettingsToBus` → `void`
|
||||||
|
- **Description:** Read saved settings volume from `SS_SettingsSystem` and apply to the bus component.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `Bus` | `E_AudioBusCategory` | Target bus |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Parameters | Bind Access | Description |
|
||||||
|
|------------|-----------|-------------|-------------|
|
||||||
|
| `OnRoomPresetChanged` | `OldPreset: FName`, `NewPreset: FName` | `Public` | Fired when room acoustic profile changes |
|
||||||
|
| `OnBusVolumeChanged` | `Bus: E_AudioBusCategory`, `Volume: Float` | `Public` | Fired when a bus volume is adjusted |
|
||||||
|
| `OnAudioPaused` | `bPaused: Boolean` | `Public` | Fired on pause/resume — for visual feedback |
|
||||||
|
| `OnMasterMuteChanged` | `bMuted: Boolean` | `Public` | Fired when global mute toggles |
|
||||||
|
| `OnDialogueStarted` | `DialogueSound: UMetaSoundSource`, `Priority: Integer` | `Public` | Fired when dialogue begins (for subtitle triggering) |
|
||||||
|
| `OnDialogueEnded` | `WasInterrupted: Boolean` | `Public` | Fired when dialogue ends |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Overridden Events / Custom Events
|
||||||
|
|
||||||
|
### Event: `Initialize`
|
||||||
|
- **Description:** Called when subsystem is created. Loads config, initializes buses, binds settings.
|
||||||
|
- **Flow:**
|
||||||
|
1. Load `DA_AudioSettings` if assigned
|
||||||
|
2. Call `InitializeBuses()`
|
||||||
|
3. Bind to `SS_SettingsSystem.OnSettingChanged` → route volume changes to `SetBusVolume`
|
||||||
|
4. Enable tick (required for room transition interpolation)
|
||||||
|
5. Apply saved settings volumes from persistent storage
|
||||||
|
|
||||||
|
### Event: `Deinitialize`
|
||||||
|
- **Description:** Cleanup all audio components and unbind delegates.
|
||||||
|
- **Flow:**
|
||||||
|
1. Stop all bus audio components
|
||||||
|
2. Clear `ActiveSFXInstances` pool
|
||||||
|
3. Unbind from `SS_SettingsSystem`
|
||||||
|
4. Set all references to null
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blueprint Graph Logic Flow
|
||||||
|
|
||||||
|
### Audio Playback Routing
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[Any System: PlaySound] --> B[SS_AudioManager.PlaySFX / PlayAmbient / PlayMusic / PlayDialogue]
|
||||||
|
B --> C{Route by Category}
|
||||||
|
C -->|SFX| D[SFX Bus MetaSound Source]
|
||||||
|
C -->|Ambience| E[Ambience Bus MetaSound Source]
|
||||||
|
C -->|Music| F[Music Bus MetaSound Source]
|
||||||
|
C -->|Dialogue| G[Dialogue Bus MetaSound Source]
|
||||||
|
D --> H[Room Zone Reverb]
|
||||||
|
E --> H
|
||||||
|
F --> H
|
||||||
|
G --> H
|
||||||
|
H --> I[Master Bus]
|
||||||
|
I --> J[Player Settings Volume Sliders]
|
||||||
|
J --> K[Audio Output]
|
||||||
|
|
||||||
|
L[BPC_StateManager.HeartRate] -->|Parameter| D
|
||||||
|
M[BPC_StressSystem.StressTier] -->|Parameter| E
|
||||||
|
N[BPC_PacingDirector.IntensityBand] -->|Parameter| F
|
||||||
|
O[BPC_AccessibilitySettings] -->|Volume Sliders| I
|
||||||
|
```
|
||||||
|
|
||||||
|
### Settings Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant SM as WBP_SettingsMenu
|
||||||
|
participant SS as SS_SettingsSystem
|
||||||
|
participant AM as SS_AudioManager
|
||||||
|
participant Bus as MetaSound Bus
|
||||||
|
|
||||||
|
SM->>SS: SetFloat(Settings.Audio.SFXVolume, 0.8)
|
||||||
|
SS->>SS: Save to persistent settings
|
||||||
|
SS->>AM: SetBusVolume(SFX, 0.8)
|
||||||
|
AM->>Bus: Set Float Parameter 'Volume' = 0.8
|
||||||
|
Bus->>Bus: Apply to output
|
||||||
|
|
||||||
|
Note over AM: On game launch:
|
||||||
|
AM->>SS: GetFloat(Settings.Audio.SFXVolume)
|
||||||
|
SS->>AM: 0.8
|
||||||
|
AM->>Bus: Set Float Parameter 'Volume' = 0.8
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Mix Bus Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────┐
|
||||||
|
│ MASTER BUS │
|
||||||
|
│ (MS_MasterBus) │
|
||||||
|
│ - Master Volume (settings) │
|
||||||
|
│ - Limiter / Compressor │
|
||||||
|
│ - Global Mute │
|
||||||
|
│ - Accessibility: Mono collapse, L/R balance │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────────────────────────────────────────┐ │
|
||||||
|
│ │ SFX BUS (MS_SFXBus) │ │
|
||||||
|
│ │ - 3D Spatialization │ │
|
||||||
|
│ │ - Per-source occlusion (line trace) │ │
|
||||||
|
│ │ - Distance attenuation │ │
|
||||||
|
│ │ - Surface material EQ (concrete/metal/wood) │ │
|
||||||
|
│ │ - Heart rate BPM → breathing blend param │ │
|
||||||
|
│ │ - Max 32 concurrent voices │ │
|
||||||
|
│ │ - Ducking: -6dB when Dialogue active │ │
|
||||||
|
│ │ Sub-inputs: │ │
|
||||||
|
│ │ • WeaponFire (limit 8 voices) │ │
|
||||||
|
│ │ • Footsteps (limit 4 voices per actor) │ │
|
||||||
|
│ │ • Impacts (limit 8 voices) │ │
|
||||||
|
│ │ • UI SFX (2D, no spatialization) │ │
|
||||||
|
│ └──────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────────────────────────────────────────┐ │
|
||||||
|
│ │ AMBIENCE BUS (MS_AmbientBus) │ │
|
||||||
|
│ │ - Stereo (no 3D) │ │
|
||||||
|
│ │ - 4-layer crossfade (room tone, wind, drone) │ │
|
||||||
|
│ │ - Stress tier → layer blend weights │ │
|
||||||
|
│ │ - Room reverb applied │ │
|
||||||
|
│ │ - Ducking: -12dB when Dialogue active │ │
|
||||||
|
│ └──────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────────────────────────────────────────┐ │
|
||||||
|
│ │ MUSIC BUS (MS_MusicBus) │ │
|
||||||
|
│ │ - Stereo (no 3D) │ │
|
||||||
|
│ │ - 4 intensity layers (Calm/Tense/Action/Climax)│ │
|
||||||
|
│ │ - PacingDirector.IntensityBand → layer blend │ │
|
||||||
|
│ │ - Stingers (one-shot overrides) │ │
|
||||||
|
│ │ - Ducking: -18dB when Dialogue active │ │
|
||||||
|
│ │ - Combat detected → crossfade to Action │ │
|
||||||
|
│ └──────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────────────────────────────────────────┐ │
|
||||||
|
│ │ DIALOGUE BUS (MS_DialogueBus) │ │
|
||||||
|
│ │ - 3D (for in-world dialogue) │ │
|
||||||
|
│ │ - Priority system (Cinematic > Quest > Bark) │ │
|
||||||
|
│ │ - Ducks all other buses │ │
|
||||||
|
│ │ - Lip-sync amplitude passthrough │ │
|
||||||
|
│ │ - Radio/phone EQ preset toggle │ │
|
||||||
|
│ │ - Max 2 concurrent dialogue voices │ │
|
||||||
|
│ │ - Settings: DialogueVolume → bus volume │ │
|
||||||
|
│ └──────────────────────────────────────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Settings Integration
|
||||||
|
|
||||||
|
### Player-Facing Volume Sliders
|
||||||
|
|
||||||
|
| Setting | GameplayTag | Bus Controlled | MetaSound Parameter |
|
||||||
|
|---------|------------|----------------|---------------------|
|
||||||
|
| Master Volume | `Settings.Audio.MasterVolume` | Master Bus | `Volume` (0–1) |
|
||||||
|
| SFX Volume | `Settings.Audio.SFXVolume` | SFX Bus | `Volume` (0–1) |
|
||||||
|
| Music Volume | `Settings.Audio.MusicVolume` | Music Bus | `Volume` (0–1) |
|
||||||
|
| Ambience Volume | `Settings.Audio.AmbientVolume` | Ambience Bus | `Volume` (0–1) |
|
||||||
|
| Dialogue Volume | `Settings.Audio.DialogueVolume` | Dialogue Bus | `Volume` (0–1) |
|
||||||
|
|
||||||
|
### Accessibility Settings
|
||||||
|
|
||||||
|
| Setting | Effect |
|
||||||
|
|---------|--------|
|
||||||
|
| Mono Audio | Collapse all buses to mono on Master Bus |
|
||||||
|
| Subtitle Background Opacity | (UI — not audio, handled by WBP_AccessibilityUI) |
|
||||||
|
| Audio Cues for UI | Play additional confirmation SFX on button press |
|
||||||
|
| Left/Right Balance | Pan parameter on Master Bus (for single-ear players) |
|
||||||
|
| Visual Heartbeat Indicator | Alternative to audio heartbeat for deaf players — UI pulse based on `BPC_StateManager.CurrentHeartRate` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Gameplay Parameter Modulation
|
||||||
|
|
||||||
|
MetaSound parameters driven by gameplay state:
|
||||||
|
|
||||||
|
| Parameter Name | Source System | Bus | Range | Effect |
|
||||||
|
|---------------|--------------|-----|-------|--------|
|
||||||
|
| `HeartRateBPM` | `BPC_StateManager` (130) | SFX | 60–180 | Controls heartbeat SFX tempo + breathing layer intensity |
|
||||||
|
| `VitalSignal` | `BPC_StateManager` (130) | SFX | 0–4 (enum) | Selects heartbeat sound variant (normal/elevated/critical/erratic) |
|
||||||
|
| `StressLevel` | `BPC_StressSystem` (10) | Ambience, Music | 0–1 | Blends tension layers, adds distortion |
|
||||||
|
| `FearIntensity` | `BPC_FearSystem` (90) | Ambience, SFX | 0–1 | Lowers pitch, increases reverb, random pan jitter |
|
||||||
|
| `MusicIntensity` | `BPC_PacingDirector` (98) | Music | 0–1 (4 bands) | Crossfades between Calm/Tense/Action/Climax layers |
|
||||||
|
| `PlayerHealth` | `BPC_HealthSystem` (08) | SFX | 0–1 | Low-pass filter on all SFX as health drops (muffled hearing) |
|
||||||
|
| `IsUnderwater` | (Physics volume) | Master | bool | Apply underwater EQ + muffled low-pass |
|
||||||
|
| `CombatActive` | `BPC_StateManager` (130) | Music | bool | Stinger trigger + immediate intensity ramp |
|
||||||
|
| `TimeOfDay` | World state | Ambience | 0–1 (dawn→dusk) | Ambient layer blend (day birds → night crickets) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Communication Matrix
|
||||||
|
|
||||||
|
| Who Talks | How | What Is Sent |
|
||||||
|
|-----------|-----|-------------|
|
||||||
|
| Any System | `SS_AudioManager.PlaySFX()` | `UMetaSoundSource`, `FVector Location`, `GameplayTag SurfaceTag` |
|
||||||
|
| Any System | `SS_AudioManager.PlaySFX2D()` | `UMetaSoundSource` (UI sounds) |
|
||||||
|
| Any System | `SS_AudioManager.PlayAmbient()` | `UMetaSoundSource`, `Float FadeIn` |
|
||||||
|
| Any System | `SS_AudioManager.SetMusicLayer()` | `Integer LayerIndex`, `UMetaSoundSource`, `Float Intensity` |
|
||||||
|
| Any System | `SS_AudioManager.PlayDialogue()` | `UMetaSoundSource`, `Integer Priority` |
|
||||||
|
| `BP_RoomAudioZone` (133) | `SS_AudioManager.SetRoomPreset()` | `DA_RoomAcousticPreset` |
|
||||||
|
| `WBP_SettingsMenu` (57) | `SS_SettingsSystem.SetFloat()` → `SS_AudioManager.SetBusVolume()` | `E_AudioBusCategory`, `Float Volume` |
|
||||||
|
| `BPC_StateManager` (130) | `SS_AudioManager.UpdateHeartRateAudio()` | `Float BPM`, `E_PlayerVitalSignals` |
|
||||||
|
| `BPC_StressSystem` (10) | `SS_AudioManager.UpdateFearAudio()` | `Float FearLevel`, `E_StressTier` |
|
||||||
|
| `BPC_PacingDirector` (98) | `SS_AudioManager.UpdateGameplayParameter("MusicIntensity")` | `Float 0–1` |
|
||||||
|
| `BPC_HealthSystem` (08) | `SS_AudioManager.UpdateGameplayParameter("PlayerHealth")` | `Float 0–1` |
|
||||||
|
| `SS_AudioManager` | `Dispatcher: OnRoomPresetChanged` | `OldPreset: FName`, `NewPreset: FName` |
|
||||||
|
| `SS_AudioManager` | `Dispatcher: OnBusVolumeChanged` | `Bus: E_AudioBusCategory`, `Volume: Float` |
|
||||||
|
| `SS_AudioManager` | `Dispatcher: OnAudioPaused` | `bPaused: Boolean` |
|
||||||
|
| `SS_AudioManager` | `Dispatcher: OnDialogueStarted` | `UMetaSoundSource`, `Integer Priority` (for subtitle system) |
|
||||||
|
| `SS_AudioManager` | `WBP_AccessibilityUI` (45) | Heart rate BPM for visual heartbeat indicator |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Migration: Replacing Deprecated BPC_AudioAtmosphereController (95)
|
||||||
|
|
||||||
|
| Old (BPC_AudioAtmosphereController) | New (SS_AudioManager) |
|
||||||
|
|-------------------------------------|----------------------|
|
||||||
|
| `PlayOneShotSFX(Sound, Location)` | `SS_AudioManager.PlaySFX(Sound, Location)` |
|
||||||
|
| `PlayAmbientSound(Sound, FadeIn)` | `SS_AudioManager.PlayAmbient(Sound, FadeIn)` |
|
||||||
|
| `SetMusicLayer(LayerIndex, Sound, Volume)` | `SS_AudioManager.SetMusicLayer(LayerIndex, Sound, Intensity)` |
|
||||||
|
| `PlayDialogue(DialogueSound)` | `SS_AudioManager.PlayDialogue(DialogueSound, Priority)` |
|
||||||
|
| `SetReverb(Reverb, WetLevel)` | `SS_AudioManager.SetRoomPreset(Preset)` via `BP_RoomAudioZone` |
|
||||||
|
| `SetOcclusion(Intensity)` | Automatic — per-source line trace in MetaSound bus |
|
||||||
|
| `FearReactiveAudioUpdate(Fear, Threshold)` | `SS_AudioManager.UpdateFearAudio(Fear, Tier)` |
|
||||||
|
| `HandleRoomChange(RoomAcousticPreset)` | `BP_RoomAudioZone.OnActorBeginOverlap` |
|
||||||
|
| `ActiveAudioPreset` / `TargetAudioPreset` | `SS_AudioManager.ActiveRoomPreset` |
|
||||||
|
| `Master/SFX/Music/Ambient/DialogueVolumeModifier` | `SS_AudioManager.SetBusVolume()` bound to `SS_SettingsSystem` |
|
||||||
|
|
||||||
|
### Phase-Out Strategy
|
||||||
|
1. **Phase 14a:** Create `SS_AudioManager` + `BP_RoomAudioZone` + all MetaSound patches (CURRENT)
|
||||||
|
2. **Phase 14b:** Update all 129 systems to call `SS_AudioManager` instead of raw `UGameplayStatics::PlaySound*`
|
||||||
|
3. **Phase 14c:** Convert all `USoundBase` references to `UMetaSoundSource` in Data Assets
|
||||||
|
4. **Phase 14d:** Remove `BPC_AudioAtmosphereController` (95) from build
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Validation / Testing Checklist
|
||||||
|
|
||||||
|
- [ ] `PlaySFX(Sound, Location)` spawns at correct world position with spatialization
|
||||||
|
- [ ] `PlaySFX2D(Sound)` plays without spatialization (UI sounds)
|
||||||
|
- [ ] `PlayAmbient(Sound)` crossfades ambient layer smoothly
|
||||||
|
- [ ] `SetMusicLayer(idx, Sound, intensity)` blends music layers correctly
|
||||||
|
- [ ] `PlayDialogue(Sound, Priority)` ducks other buses by correct amounts
|
||||||
|
- [ ] `BP_RoomAudioZone` on overlap → `SetRoomPreset` transitions reverb smoothly
|
||||||
|
- [ ] Nested room zones use stack correctly (push/pop)
|
||||||
|
- [ ] Room transition crossfade interpolates reverb parameters
|
||||||
|
- [ ] `SS_SettingsSystem` volume changes apply to correct bus
|
||||||
|
- [ ] Master Mute silences all buses
|
||||||
|
- [ ] Pause/Resume toggles all bus audio components
|
||||||
|
- [ ] `UpdateHeartRateAudio(BPM)` modulates heartbeat tempo in MetaSound
|
||||||
|
- [ ] `UpdateFearAudio(0.8, Terrified)` applies fear distortion
|
||||||
|
- [ ] SFX pooling limits to 32 concurrent (oldest culled when full)
|
||||||
|
- [ ] Dialogue priority system: higher priority interrupts lower
|
||||||
|
- [ ] Accessibility mono audio collapses all buses
|
||||||
|
- [ ] Accessibility L/R balance pans correctly on Master Bus
|
||||||
|
- [ ] Edge case: Rapid room zone enter/exit doesn't cause audio glitches
|
||||||
|
- [ ] Edge case: SS_AudioManager handles null MetaSound source gracefully
|
||||||
|
- [ ] Edge case: Settings loaded before first sound plays (Init order)
|
||||||
|
- [ ] Edge case: `SetMasterMute(true)` while crossfading — immediate mute
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. Reuse Notes
|
||||||
|
|
||||||
|
- `SS_AudioManager` is the **ONLY** entry point for audio playback. Systems must never call `UGameplayStatics::PlaySound*` directly.
|
||||||
|
- Game instance subsystem — exists for the entire game session. No per-level reinitialization needed.
|
||||||
|
- SFX pool of 32 ensures no unbounded audio component creation. Reuse is automatic.
|
||||||
|
- Room zone stack supports arbitrarily nested zones (room within building within cave).
|
||||||
|
- All gameplay parameters are soft-referenced — if a source system (e.g., `BPC_PacingDirector`) is not present, `UpdateGameplayParameter` fails gracefully (log warning, no crash).
|
||||||
|
- MetaSound sources can be hot-reloaded in-editor while the game is running for rapid sound design iteration.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Blueprint Spec: SS_AudioManager — Single entry point for all MetaSounds audio. Replaces BPC_AudioAtmosphereController (95). Architecture document: [`metasounds-audio-system.md`](../../architecture/metasounds-audio-system.md)*
|
||||||
216
docs/blueprints/10-adaptive/133_BP_RoomAudioZone.md
Normal file
216
docs/blueprints/10-adaptive/133_BP_RoomAudioZone.md
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
# 133 — Room Audio Zone (`BP_RoomAudioZone`)
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
A trigger volume placed in the world that automatically switches the audio reverb/occlusion profile when the player enters. When the player exits, it restores the previous room preset via a push/pop stack in `SS_AudioManager`. Supports nested room zones (room within building within cave) with priority-based override.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **Requires:** `SS_AudioManager` (132 — calls `SetRoomPreset` / `PopRoomPreset` on overlap), `DA_RoomAcousticPreset` (135 — assigned acoustic profile)
|
||||||
|
- **Required By:** Level Designers (placed in world)
|
||||||
|
- **Engine/Plugin Requirements:** MetaSounds plugin (for room reverb pipeline)
|
||||||
|
|
||||||
|
## Class Info
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Parent Class** | `TriggerVolume` (or `Actor` with `BoxComponent`) |
|
||||||
|
| **Class Type** | Blueprint Actor |
|
||||||
|
| **Asset Path** | `Content/Framework/Audio/BP_RoomAudioZone.uasset` |
|
||||||
|
| **Implements Interfaces** | *(none)* |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Enums
|
||||||
|
|
||||||
|
*Uses enums defined elsewhere:*
|
||||||
|
- `E_AudioBusCategory` — from `SS_AudioManager` (132)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Structs
|
||||||
|
|
||||||
|
*Uses struct defined in `SS_AudioManager` (132):*
|
||||||
|
- `S_RoomAcousticProfile` — acoustic profile fields (reverb density, decay time, wet level, etc.)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
|
||||||
|
### Configuration (Instance Editable, Expose On Spawn)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `RoomPreset` | `DA_RoomAcousticPreset` | `None` | `Room Config` | Acoustic profile for this room |
|
||||||
|
| `TransitionTime` | `Float` | `1.5` | `Room Config` | Crossfade duration on enter (seconds) |
|
||||||
|
| `ExitTransitionTime` | `Float` | `1.0` | `Room Config` | Crossfade duration on exit (seconds) |
|
||||||
|
| `bOverrideOcclusion` | `Boolean` | `false` | `Room Config` | Override occlusion settings in this zone |
|
||||||
|
| `OcclusionTraceChannel` | `ETraceTypeQuery` | `Camera` | `Room Config` | Trace channel for per-source occlusion |
|
||||||
|
| `Priority` | `Integer` | `0` | `Room Config` | Higher priority zones override lower (for nested rooms) |
|
||||||
|
| `bIsActive` | `Boolean` | `true` | `State` | Whether this room zone is actively processing |
|
||||||
|
| `ZoneDebugColor` | `LinearColor` | `Cyan` | `Debug` | Color of the trigger volume in editor for visual identification |
|
||||||
|
| `bShowDebugInfo` | `Boolean` | `false` | `Debug` | Print room zone enter/exit to log |
|
||||||
|
|
||||||
|
### Internal (Private)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `bPlayerInside` | `Boolean` | `false` | `State` | Whether the player is currently inside this zone |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
### Public Functions
|
||||||
|
|
||||||
|
#### `SetRoomAudioEnabled` → `void`
|
||||||
|
- **Description:** Toggle room audio processing. Used for narrative lock, void space override.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `bEnabled` | `Boolean` | True to enable, false to disable zone processing |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
- **Flow:**
|
||||||
|
1. Set `bIsActive = bEnabled`
|
||||||
|
2. If `bEnabled == false` and `bPlayerInside`: force `PopRoomPreset` on `SS_AudioManager`
|
||||||
|
3. If `bEnabled == true` and `bPlayerInside`: re-apply `SetRoomPreset`
|
||||||
|
|
||||||
|
#### `GetPriority` → `Integer`
|
||||||
|
- **Description:** Returns the zone priority. Used by `SS_AudioManager` for nested zone resolution.
|
||||||
|
- **Parameters:** *(none)*
|
||||||
|
- **Blueprint Authority:** Any (Pure)
|
||||||
|
|
||||||
|
#### `GetAcousticProfile` → `DA_RoomAcousticPreset`
|
||||||
|
- **Description:** Returns the assigned room acoustic preset.
|
||||||
|
- **Parameters:** *(none)*
|
||||||
|
- **Blueprint Authority:** Any (Pure)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Parameters | Bind Access | Description |
|
||||||
|
|------------|-----------|-------------|-------------|
|
||||||
|
| `OnPlayerEnteredZone` | `ZoneName: FName` | `Public` | Fired when player enters (for gameplay logic) |
|
||||||
|
| `OnPlayerExitedZone` | `ZoneName: FName` | `Public` | Fired when player exits |
|
||||||
|
| `OnZoneEnabledChanged` | `bEnabled: Boolean` | `Public` | Fired when `SetRoomAudioEnabled` is called |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Overridden Events / Custom Events
|
||||||
|
|
||||||
|
### Event: `OnActorBeginOverlap`
|
||||||
|
- **Description:** Detects player character entering the trigger volume → calls `SS_AudioManager.SetRoomPreset()`.
|
||||||
|
- **Flow:**
|
||||||
|
1. Validate `OtherActor` — is it the player character? (check tag `Player` or cast to `BP_PlayerCharacter`)
|
||||||
|
2. If not player: return
|
||||||
|
3. If `!bIsActive`: return
|
||||||
|
4. Set `bPlayerInside = true`
|
||||||
|
5. Get `SS_AudioManager` from Game Instance
|
||||||
|
6. Call `SS_AudioManager.SetRoomPreset(RoomPreset)` with `TransitionTime`
|
||||||
|
7. Fire `OnPlayerEnteredZone`
|
||||||
|
8. If `bShowDebugInfo`: log zone name to console
|
||||||
|
|
||||||
|
### Event: `OnActorEndOverlap`
|
||||||
|
- **Description:** Player exits → restore previous room preset via `SS_AudioManager.PopRoomPreset()`.
|
||||||
|
- **Flow:**
|
||||||
|
1. Validate `OtherActor` — is it the player?
|
||||||
|
2. If not player: return
|
||||||
|
3. If `!bIsActive`: return
|
||||||
|
4. Set `bPlayerInside = false`
|
||||||
|
5. Get `SS_AudioManager` from Game Instance
|
||||||
|
6. Call `SS_AudioManager.PopRoomPreset()` (restores previous room from stack)
|
||||||
|
7. Fire `OnPlayerExitedZone`
|
||||||
|
8. If `bShowDebugInfo`: log to console
|
||||||
|
|
||||||
|
### Event: `BeginPlay`
|
||||||
|
- **Description:** Register this zone with a global zone manager if one exists. Configure trigger volume collision settings.
|
||||||
|
- **Flow:**
|
||||||
|
1. Configure trigger volume: `SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap)`
|
||||||
|
2. Set `Generate Overlap Events = true`
|
||||||
|
3. Optionally set trigger volume color from `ZoneDebugColor`
|
||||||
|
4. If `RoomPreset` is null: log warning, set `bIsActive = false`
|
||||||
|
|
||||||
|
### Event: `EndPlay`
|
||||||
|
- **Description:** Cleanup — if player is inside zone on destruction, force pop the room preset.
|
||||||
|
- **Flow:**
|
||||||
|
1. If `bPlayerInside`: call `SS_AudioManager.PopRoomPreset()`
|
||||||
|
2. Set `bPlayerInside = false`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blueprint Graph Logic Flow
|
||||||
|
|
||||||
|
### Nested Room Zone Stack
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[Enter Room A - SmallRoom] --> B[SS_AudioManager.SetRoomPreset: SmallRoom]
|
||||||
|
B --> C[Enter Room B - Bathroom, Priority > A]
|
||||||
|
C --> D[SS_AudioManager.SetRoomPreset: Bathroom]
|
||||||
|
D --> E[Exit Room B]
|
||||||
|
E --> F[SS_AudioManager.PopRoomPreset → Restore SmallRoom]
|
||||||
|
F --> G[Exit Room A]
|
||||||
|
G --> H[SS_AudioManager.PopRoomPreset → Restore Default/Outdoor]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Room Preset Reference
|
||||||
|
|
||||||
|
### Included Room Presets (in `Content/Framework/Audio/RoomPresets/`)
|
||||||
|
|
||||||
|
| Preset Asset | Description | Typical Use |
|
||||||
|
|-------------|-------------|-------------|
|
||||||
|
| `DA_RoomAcousticPreset` | Base class | Parent for all presets |
|
||||||
|
| `DA_RP_SmallRoom` | Small room reverb | Offices, bedrooms, closets |
|
||||||
|
| `DA_RP_LargeHall` | Large hall reverb | Auditoriums, churches, lobbies |
|
||||||
|
| `DA_RP_Outdoor` | Outdoor (minimal reverb) | Default fallback, streets, forests |
|
||||||
|
| `DA_RP_Cave` | Cave/underground reverb | Caves, tunnels, basements |
|
||||||
|
| `DA_RP_VoidSpace` | Void/alt death space reverb | Alt death space, dream sequences |
|
||||||
|
| `DA_RP_Industrial` | Industrial/factory reverb | Factories, warehouses, hangars |
|
||||||
|
| `DA_RP_Bathroom` | Small tile reverb | Bathrooms, tiled corridors |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Communication Matrix
|
||||||
|
|
||||||
|
| Who Talks | How | What Is Sent |
|
||||||
|
|-----------|-----|-------------|
|
||||||
|
| `BP_RoomAudioZone` | `SS_AudioManager.SetRoomPreset()` | `DA_RoomAcousticPreset`, crossfade time |
|
||||||
|
| `BP_RoomAudioZone` | `SS_AudioManager.PopRoomPreset()` | *(none — restores previous from stack)* |
|
||||||
|
| `BP_RoomAudioZone` | `Dispatcher: OnPlayerEnteredZone` | `FName ZoneName` (for gameplay scripting) |
|
||||||
|
| `BP_RoomAudioZone` | `Dispatcher: OnPlayerExitedZone` | `FName ZoneName` |
|
||||||
|
| Level Designer | Direct placement in world | Adjusts `RoomPreset`, `Priority`, `TransitionTime` in Details Panel |
|
||||||
|
| Narrative System | `SetRoomAudioEnabled(false)` | Disables zone during cutscenes/void space |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Validation / Testing Checklist
|
||||||
|
|
||||||
|
- [ ] Player entering zone triggers `SetRoomPreset` with correct preset
|
||||||
|
- [ ] Reverb parameters smoothly crossfade over `TransitionTime`
|
||||||
|
- [ ] Player exiting zone triggers `PopRoomPreset` restoring previous room
|
||||||
|
- [ ] Nested zones: entering inner room, exiting restores outer room
|
||||||
|
- [ ] Priority: higher priority zone overrides lower priority when overlapping
|
||||||
|
- [ ] `SetRoomAudioEnabled(false)` during overlap — zone is disabled
|
||||||
|
- [ ] `SetRoomAudioEnabled(true)` after disable — zone reapplies
|
||||||
|
- [ ] Non-player actors do NOT trigger zone overlap
|
||||||
|
- [ ] Zone with null `RoomPreset` logs warning and remains inactive
|
||||||
|
- [ ] `bShowDebugInfo` logs enter/exit to console
|
||||||
|
- [ ] Zone destroyed while player inside — `PopRoomPreset` still called
|
||||||
|
- [ ] Edge case: Rapid enter/exit (player on boundary) — no audio glitching
|
||||||
|
- [ ] Edge case: `SS_AudioManager` not found — zone gracefully handles missing subsystem
|
||||||
|
- [ ] Edge case: Multiple zones with same `Priority` — last-entered wins
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Reuse Notes
|
||||||
|
|
||||||
|
- Place multiple `BP_RoomAudioZone` actors throughout a level to create realistic acoustic transitions.
|
||||||
|
- Use `Priority` to handle overlapping zones — higher values override. Example: a bathroom (Priority=5) inside a house (Priority=0).
|
||||||
|
- `bOverrideOcclusion` enables per-source occlusion line traces in this zone — useful for walls, doors.
|
||||||
|
- The `OnPlayerEnteredZone` / `OnPlayerExitedZone` dispatchers allow gameplay logic to react to room changes (e.g., "You've entered the forbidden wing" audio sting).
|
||||||
|
- Zone scaling: Box components can be scaled to any size. Use multiple zones for irregularly shaped rooms.
|
||||||
|
- Debug mode (`bShowDebugInfo` + `ZoneDebugColor`) helps level designers visualize acoustic boundaries.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Blueprint Spec: BP_RoomAudioZone — Trigger volume for automatic room acoustic switching. Architecture document: [`metasounds-audio-system.md`](../../architecture/metasounds-audio-system.md) Section 6.*
|
||||||
137
docs/blueprints/10-adaptive/89_BPC_DifficultyManager.md
Normal file
137
docs/blueprints/10-adaptive/89_BPC_DifficultyManager.md
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
# 62 — BPC_DifficultyManager
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`GI_GameFramework`](../01-core/04_GI_GameFramework.md) — Central game instance access
|
||||||
|
- [`BPC_PlayerMetricsTracker`](../02-player/15_BPC_PlayerMetricsTracker.md) — Input for difficulty tuning
|
||||||
|
- [`BPC_AtmosphereController`](64_BPC_AtmosphereController.md) — Output to atmosphere/scare systems
|
||||||
|
- [`BPC_HealthSystem`](../02-player/08_BPC_HealthSystem.md) — Health/revive tuning
|
||||||
|
- [`BPC_InventoryComponent`](../04-inventory/22_BPC_InventoryComponent.md) — Resource scarcity input
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Central difficulty orchestrator. Dynamically adjusts game challenge based on player performance metrics and explicit difficulty profile settings. Operates as a non-intrusive governor that tunes encounter intensity, resource availability, AI aggression, puzzle complexity, and survival pressure without direct player awareness (unless overridden by explicit difficulty mode selection).
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `CurrentDifficulty` | EGameDifficulty | Active difficulty preset |
|
||||||
|
| `bIsAdaptive` | Bool | Auto-adjust based on performance |
|
||||||
|
| `AdaptiveBias` | Float (0.0–1.0) | Current adaptive tuning bias |
|
||||||
|
| `PlayerEfficiencyScore` | Float (0.0–1.0) | Rolling efficiency metric |
|
||||||
|
| `PlayerSurvivalScore` | Float (0.0–1.0) | Rolling survival metric |
|
||||||
|
| `ExplorationThoroughness` | Float (0.0–1.0) | How much player explores |
|
||||||
|
| `ResourceConsumptionRate` | Float | Scarcity/frequency of resources |
|
||||||
|
| `bIsPaused` | Bool | Pause all difficulty adjustments |
|
||||||
|
| `AdjustmentCooldown` | Float | Seconds between recalibrations |
|
||||||
|
| `LastAdjustmentTime` | Float | Timestamp of last adjustment |
|
||||||
|
| `PerformanceWindow` | TArray\<Float\> | Sliding window of recent metric samples |
|
||||||
|
| `MaxWindowSize` | Int | Max samples before averaging |
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
|
||||||
|
| Enum | Values | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| `EGameDifficulty` | Story, Easy, Normal, Hard, Survival, Adaptive | Difficulty presets |
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
| Struct | Fields | Description |
|
||||||
|
|--------|--------|-------------|
|
||||||
|
| `FDifficultyParams` | HealthMultiplier, DamageMultiplier, AmmoScarcity, EnemyAggression, PuzzleComplexity, FearIntensity, ReviveCount, CheckpointFrequency, TimedPressure | Tuning package |
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `Initialize` | Difficulty: EGameDifficulty | — | Set initial difficulty state |
|
||||||
|
| `SetDifficulty` | Difficulty: EGameDifficulty | — | Apply explicit difficulty preset |
|
||||||
|
| `SetAdaptiveMode` | bEnabled: Bool | — | Toggle adaptive adjustment |
|
||||||
|
| `RecalculateDifficulty` | — | — | Analyze metrics and adjust |
|
||||||
|
| `GetCurrentParams` | — | FDifficultyParams | Return current tuning |
|
||||||
|
| `ApplyHealthMultiplier` | BaseValue: Float | Float | Scaled health |
|
||||||
|
| `ApplyDamageMultiplier` | BaseValue: Float | Float | Scaled damage |
|
||||||
|
| `GetAmmoScarcity` | — | Float | 0.0 = abundant, 1.0 = scarce |
|
||||||
|
| `GetEnemyAggressionModifier` | — | Float | Multiplier for AI aggression |
|
||||||
|
| `GetFearIntensityModifier` | — | Float | Scare system intensity |
|
||||||
|
| `GetReviveLimit` | — | Int | Max revives allowed |
|
||||||
|
| `GetCheckpointFrequency` | — | Float | Time between auto-saves |
|
||||||
|
| `IsTimedPressureActive` | — | Bool | Survival timer enabled |
|
||||||
|
| `PauseAdjustments` | bPaused: Bool | — | Freeze recalibration |
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired When |
|
||||||
|
|------|-----------|-----------|
|
||||||
|
| `OnDifficultyChanged` | NewDifficulty: EGameDifficulty | Difficulty preset changes |
|
||||||
|
| `OnAdaptiveShift` | ShiftAmount: Float | Adaptive tuning adjusts |
|
||||||
|
| `OnPerformanceWarning` | Metric: EPerformanceMetric, Value: Float | Player struggling/excelling |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[Initialize]
|
||||||
|
└─► Set default difficulty based on game mode / save data
|
||||||
|
└─► SetDifficulty(DefaultDifficulty)
|
||||||
|
└─► If bIsAdaptive:
|
||||||
|
Start recalibration timer (AdjustmentCooldown)
|
||||||
|
Bind to BPC_PlayerMetricsTracker dispatchers
|
||||||
|
|
||||||
|
[RecalculateDifficulty — called by timer or metric threshold]
|
||||||
|
└─► Gather metrics from BPC_PlayerMetricsTracker:
|
||||||
|
DeathCount, HealingUsed, AmmoUsed, EnemiesKilled,
|
||||||
|
TimeInCombat, TimeHidden, PuzzlesSolved
|
||||||
|
└─► Compute PlayerEfficiencyScore =
|
||||||
|
(EnemiesKilled / TimeInCombat) * ExplorationThoroughness
|
||||||
|
└─► Compute PlayerSurvivalScore =
|
||||||
|
1.0 - (DeathCount / MaxDeathsBeforeAdjust)
|
||||||
|
└─► If PlayerSurvivalScore < LowThreshold:
|
||||||
|
Reduce enemy aggression by 0.1
|
||||||
|
Increase ammo drops by 15%
|
||||||
|
Extend checkpoint frequency
|
||||||
|
Fire OnPerformanceWarning(Struggling)
|
||||||
|
└─► If PlayerEfficiencyScore > HighThreshold AND DeathCount == 0:
|
||||||
|
Increase enemy aggression by 0.1
|
||||||
|
Reduce ammo drops by 10%
|
||||||
|
Shorten checkpoint frequency
|
||||||
|
Fire OnPerformanceWarning(Excelling)
|
||||||
|
└─► If bIsAdaptive:
|
||||||
|
Lerp AdaptiveBias toward target
|
||||||
|
BPC_AtmosphereController.SetTuningBias(AdaptiveBias)
|
||||||
|
└─► Update LastAdjustmentTime
|
||||||
|
└─► Broadcast OnAdaptiveShift
|
||||||
|
|
||||||
|
[SetDifficulty — explicit override]
|
||||||
|
└─► Set CurrentDifficulty = input
|
||||||
|
└─► bIsAdaptive = false if explicit non-Adaptive difficulty
|
||||||
|
└─► Broadcast OnDifficultyChanged
|
||||||
|
└─► Apply to dependent systems:
|
||||||
|
BPC_HealthSystem.MaxRevives = GetReviveLimit()
|
||||||
|
SS_SaveManager.AutoSaveInterval = GetCheckpointFrequency()
|
||||||
|
BPC_AtmosphereController.FearIntensityBias = GetFearIntensityModifier()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target | Method | Why |
|
||||||
|
|--------|--------|-----|
|
||||||
|
| [`BPC_PlayerMetricsTracker`](../02-player/15_BPC_PlayerMetricsTracker.md) | Get Owner Component | Feed performance data |
|
||||||
|
| [`BPC_AtmosphereController`](65_BPC_AtmosphereController.md) | Direct call | Apply fear/intensity tuning |
|
||||||
|
| [`BPC_HealthSystem`](../02-player/08_BPC_HealthSystem.md) | Get Owner Component | Revive limits |
|
||||||
|
| [`SS_SaveManager`](../05-saveload/28_SS_SaveManager.md) | Game Instance | Checkpoint frequency |
|
||||||
|
| [`BPC_InventoryComponent`](../04-inventory/22_BPC_InventoryComponent.md) | Get Owner Component | Resource scarcity input |
|
||||||
|
| [`BPC_FearSystem`](90_BPC_FearSystem.md) | Direct call | Fear intensity modifier |
|
||||||
|
| [`BPC_AlertSystem`](../09-ai/60_BPC_AlertSystem.md) | Get on AI Controller | Enemy aggression modifier |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
- Single instance per game (attach to GameMode or persistent GameState)
|
||||||
|
- Designed to be optional; game works without it using static defaults
|
||||||
|
- Adaptive mode is additive; explicit difficulty modes override everything
|
||||||
|
- Performance window smooths out spikes before adjustment triggers
|
||||||
|
- All tuning values are configurable at runtime via console or debug menu
|
||||||
182
docs/blueprints/10-adaptive/90_BPC_FearSystem.md
Normal file
182
docs/blueprints/10-adaptive/90_BPC_FearSystem.md
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
# 63 — BPC_FearSystem
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`GI_GameFramework`](../01-core/04_GI_GameFramework.md) — Central game instance access
|
||||||
|
- [`BPC_PlayerMetricsTracker`](../02-player/15_BPC_PlayerMetricsTracker.md) — Player anxiety/stress metrics
|
||||||
|
- [`BPC_DifficultyManager`](89_BPC_DifficultyManager.md) — Fear intensity modifier
|
||||||
|
- [`BPC_StressSystem`](../02-player/10_BPC_StressSystem.md) — Player stress level
|
||||||
|
- [`BPC_AtmosphereController`](64_BPC_AtmosphereController.md) — Atmosphere response
|
||||||
|
- [`BPC_AudioManager`](66_BPC_AudioManager.md) — Audio feedback
|
||||||
|
- [`BPC_VFXManager`](67_BPC_VFXManager.md) — Visual feedback
|
||||||
|
- [`BPC_LightingManager`](65_BPC_LightingManager.md) — Lighting response
|
||||||
|
- [`BPC_NarrativeStateSystem`](../07-narrative/38_BPC_NarrativeStateSystem.md) — Story-aware fear triggers
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Core fear and anxiety system that manages the player's fear level, fear sources, fear decay, and fear-induced gameplay effects. Acts as the central orchestrator for all horror-related feedback loops — audio, visual, AI behavior, and UI — driven by the player's current fear state and environmental context.
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `CurrentFearLevel` | Float (0–100) | Current fear value |
|
||||||
|
| `MaxFearLevel` | Float | Cap for fear (default 100) |
|
||||||
|
| `FearDecayRate` | Float | Passive fear reduction per second |
|
||||||
|
| `FearThresholds` | TMap\<EFearThreshold, Float\> | Threshold values for each fear tier |
|
||||||
|
| `ActiveFearSources` | TArray\<FFearSource\> | Currently contributing fear sources |
|
||||||
|
| `FearMultiplier` | Float | Global multiplier (from DifficultyManager) |
|
||||||
|
| `bIsInPanicState` | Bool | Overwhelmed by fear |
|
||||||
|
| `PanicDuration` | Float | Seconds of panic state |
|
||||||
|
| `PanicTimer` | Float | Panic countdown |
|
||||||
|
| `LastIncreaseTime` | Float | When fear last increased |
|
||||||
|
| `bFearDecayPaused` | Bool | Freeze decay |
|
||||||
|
| `EnvironmentalFearModifier` | Float | Ambient fear from atmosphere |
|
||||||
|
| `DarknessFearMultiplier` | Float | Extra fear in dark areas |
|
||||||
|
| `IsolatedFearMultiplier` | Float | Extra fear when alone |
|
||||||
|
| `ProximityFearMultiplier` | Float | Extra fear from nearby threats |
|
||||||
|
| `bIsInSafeZone` | Bool | Safe zone overrides fear |
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
|
||||||
|
| Enum | Values | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| `EFearThreshold` | Calm, Uneasy, Nervous, Afraid, Terrified, Panic | Fear tiers |
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
| Struct | Fields | Description |
|
||||||
|
|--------|--------|-------------|
|
||||||
|
| `FFearSource` | SourceName: FName, BaseValue: Float, CurrentValue: Float, DecayDelay: Float, bIsDecaying: Bool, TimeSinceTrigger: Float, Tag: FGameplayTag | Active fear contributor |
|
||||||
|
| `FFearEvent` | Source: FName, Value: Float, Duration: Float, bInstant: Bool, bStack: Bool, AssociatedTag: FGameplayTag | Incoming fear impulse |
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `Initialize` | — | — | Setup thresholds, bind events |
|
||||||
|
| `AddFear` | Event: FFearEvent | — | Add fear from any source |
|
||||||
|
| `RemoveFearSource` | SourceName: FName | — | Remove specific fear source |
|
||||||
|
| `ClearAllFear` | — | — | Reset fear to baseline |
|
||||||
|
| `GetFearLevel` | — | Float | Current fear value |
|
||||||
|
| `GetFearThreshold` | — | EFearThreshold | Current fear tier |
|
||||||
|
| `IsInPanic` | — | Bool | Panic state check |
|
||||||
|
| `SetFearDecayRate` | NewRate: Float | — | Override decay speed |
|
||||||
|
| `PauseFearDecay` | bPaused: Bool | — | Freeze/unfreeze decay |
|
||||||
|
| `UpdateEnvironmentalFear` | Modifier: Float | — | Ambient fear contribution |
|
||||||
|
| `CalculateFearTier` | FearValue: Float | EFearThreshold | Determine current tier |
|
||||||
|
| `TriggerPanic` | Duration: Float | — | Force panic state |
|
||||||
|
| `ResolvePanic` | — | — | End panic state early |
|
||||||
|
| `ApplyFearEffect` | Effect: EFearEffect | — | Gameplay consequences of fear |
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired When |
|
||||||
|
|------|-----------|-----------|
|
||||||
|
| `OnFearLevelChanged` | NewFear: Float, Threshold: EFearThreshold | Any fear change |
|
||||||
|
| `OnFearThresholdReached` | Threshold: EFearThreshold | Cross a fear tier boundary |
|
||||||
|
| `OnPanicStarted` | — | Panic state entered |
|
||||||
|
| `OnPanicEnded` | — | Panic state ended |
|
||||||
|
| `OnFearSourceAdded` | Source: FFearSource | New fear source registered |
|
||||||
|
| `OnFearSourceRemoved` | SourceName: FName | Fear source expired |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[Tick — every frame]
|
||||||
|
└─► If not bFearDecayPaused AND not bIsInPanicState:
|
||||||
|
CurrentFearLevel -= FearDecayRate * DeltaTime
|
||||||
|
Clamp CurrentFearLevel to 0..MaxFearLevel
|
||||||
|
└─► Process active fear sources:
|
||||||
|
For each FFearSource in ActiveFearSources:
|
||||||
|
Source.TimeSinceTrigger += DeltaTime
|
||||||
|
If Source.TimeSinceTrigger > Source.DecayDelay:
|
||||||
|
Source.bIsDecaying = true
|
||||||
|
Source.CurrentValue -= DecayRate * DeltaTime
|
||||||
|
If Source.CurrentValue <= 0:
|
||||||
|
Remove from ActiveFearSources
|
||||||
|
Fire OnFearSourceRemoved
|
||||||
|
└─► If bIsInPanicState:
|
||||||
|
PanicTimer -= DeltaTime
|
||||||
|
If PanicTimer <= 0:
|
||||||
|
ResolvePanic()
|
||||||
|
└─► CalculateFearTier(CurrentFearLevel)
|
||||||
|
└─► If tier changed since last frame:
|
||||||
|
Fire OnFearThresholdReached(NewThreshold)
|
||||||
|
|
||||||
|
[AddFear]
|
||||||
|
└─► Apply modifiers:
|
||||||
|
EffectiveValue = Event.Value * FearMultiplier
|
||||||
|
EffectiveValue *= EnvironmentalFearModifier
|
||||||
|
EffectiveValue *= DarknessFearModifier (if in dark volume)
|
||||||
|
EffectiveValue *= IsolatedFearModifier (if no allies)
|
||||||
|
EffectiveValue *= ProximityFearModifier (if enemies nearby)
|
||||||
|
└─► If Event.bStack:
|
||||||
|
Find existing source by Event.Source
|
||||||
|
If found: Stack.Value += EffectiveValue
|
||||||
|
Else: Create new FFearSource
|
||||||
|
└─► Else:
|
||||||
|
Create new FFearSource with Event parameters
|
||||||
|
└─► CurrentFearLevel += EffectiveValue
|
||||||
|
└─► Clamp to MaxFearLevel
|
||||||
|
└─► Check if panic threshold crossed:
|
||||||
|
If CurrentFearLevel >= FearThresholds[Panic]:
|
||||||
|
TriggerPanic(PanicDuration)
|
||||||
|
└─► Fire OnFearLevelChanged(CurrentFearLevel, CurrentThreshold)
|
||||||
|
└─► Fire OnFearSourceAdded(NewSource)
|
||||||
|
|
||||||
|
[TriggerPanic]
|
||||||
|
└─► bIsInPanicState = true
|
||||||
|
└─► PanicTimer = Duration (or PanicDuration default)
|
||||||
|
└─► ApplyFearEffect(EPanicEffects)
|
||||||
|
└─► BPC_AtmosphereController.TriggerPanicMode()
|
||||||
|
└─► BPC_AudioManager.PlayPanicAudio()
|
||||||
|
└─► BPC_VFXManager.TriggerPanicVFX()
|
||||||
|
└─► UI: Show panic overlay
|
||||||
|
└─► Fire OnPanicStarted
|
||||||
|
|
||||||
|
[ResolvePanic]
|
||||||
|
└─► bIsInPanicState = false
|
||||||
|
└─► CurrentFearLevel = FearThresholds[Terrified] * 0.8
|
||||||
|
└─► BPC_AtmosphereController.EndPanicMode()
|
||||||
|
└─► BPC_AudioManager.StopPanicAudio()
|
||||||
|
└─► Fire OnPanicEnded
|
||||||
|
|
||||||
|
[ApplyFearEffect — based on current threshold]
|
||||||
|
└─► EFearThreshold.Calm: No effects
|
||||||
|
└─► EFearThreshold.Uneasy: Subtle audio cues, slight camera sway
|
||||||
|
└─► EFearThreshold.Nervous: UI vignette, breathing sounds, reduced stamina regen
|
||||||
|
└─► EFearThreshold.Afraid: Visibility distortion, movement speed penalty, aim shake
|
||||||
|
└─► EFearThreshold.Terrified: Severe vignette, stammering audio, weapon sway
|
||||||
|
└─► EFearThreshold.Panic: Tunnel vision, audio distortion, sprint penalty, AI gets buffs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target | Method | Why |
|
||||||
|
|--------|--------|-----|
|
||||||
|
| [`BPC_PlayerMetricsTracker`](../02-player/15_BPC_PlayerMetricsTracker.md) | Get Owner Component | Track fear history for metrics |
|
||||||
|
| [`BPC_StressSystem`](../02-player/10_BPC_StressSystem.md) | Get Owner Component | Stress/fear feedback loop |
|
||||||
|
| [`BPC_DifficultyManager`](89_BPC_DifficultyManager.md) | Get Owner Component | Fear intensity modifier |
|
||||||
|
| [`BPC_AtmosphereController`](64_BPC_AtmosphereController.md) | Direct call | Atmosphere panic mode |
|
||||||
|
| [`BPC_AudioManager`](66_BPC_AudioManager.md) | Direct call | Panic audio, fear ambience |
|
||||||
|
| [`BPC_VFXManager`](67_BPC_VFXManager.md) | Direct call | Visual effects per tier |
|
||||||
|
| [`BPC_LightingManager`](65_BPC_LightingManager.md) | Direct call | Dynamic lighting response |
|
||||||
|
| [`SS_UIManager`](../06-ui/32_SS_UIManager.md) | Subsystem | HUD fear indicator, panic overlay |
|
||||||
|
| [`BPC_NarrativeStateSystem`](../07-narrative/38_BPC_NarrativeStateSystem.md) | Get Owner Component | Story-aware fear modifiers |
|
||||||
|
| [`BPC_HealthSystem`](../02-player/08_BPC_HealthSystem.md) | Get Owner Component | Panic damage if health low |
|
||||||
|
| [`AI_EnemyControllers`](../09-ai/55_BPC_AIControllerBase.md) | Event Dispatcher | AI aggression rise with player fear |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
- Single instance on player character (or PlayerController)
|
||||||
|
- All fear sources are stackable and independently decayable
|
||||||
|
- Thresholds are designer-configurable at runtime
|
||||||
|
- Fear multiplier from DifficultyManager allows difficulty to scale horror
|
||||||
|
- Safe zones (checkpoints, safe rooms) pause fear accumulation and decay
|
||||||
|
- Panic state is a temporary debuff with full cleanup on resolve
|
||||||
|
- UI reads OnFearLevelChanged dispatcher, never polls
|
||||||
234
docs/blueprints/10-adaptive/91_BPC_PerformanceScaler.md
Normal file
234
docs/blueprints/10-adaptive/91_BPC_PerformanceScaler.md
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
# 69 — BPC_PerformanceScaler
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`BPC_LightingManager`](65_BPC_LightingManager.md) — Controls light quality
|
||||||
|
- [`BPC_AudioManager`](66_BPC_AudioManager.md) — Controls audio quality
|
||||||
|
- [`BPC_VFXManager`](67_BPC_VFXManager.md) — Controls particle LOD
|
||||||
|
- [`BPC_AtmosphereController`](64_BPC_AtmosphereController.md) — Preset complexity reduction
|
||||||
|
- [`SS_SaveManager`](../05-saveload/28_SS_SaveManager.md) — Save performance settings
|
||||||
|
- [`GI_GameFramework`](../01-core/04_GI_GameFramework.md) — Frame time measurement
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Automatically adjusts graphics, audio, and gameplay quality settings based on real-time performance metrics (frame time, draw calls, memory usage). Provides a unified interface for all other systems to scale their quality without direct awareness of the hardware. Supports manual user override via Settings menu and adaptive automatic mode. Maintains a performance budget that is dynamically allocated across subsystems.
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
|
||||||
|
**EPerformanceQualityLevel**
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| Low | Minimum quality, max performance |
|
||||||
|
| Medium | Balanced quality/performance |
|
||||||
|
| High | High quality |
|
||||||
|
| Ultra | Maximum quality, no compromises |
|
||||||
|
| Cinematic | Maximum visual fidelity, 30fps target |
|
||||||
|
|
||||||
|
**EScalingTarget**
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| ResolutionScale | Screen percentage |
|
||||||
|
| ShadowQuality | Shadow resolution and distance |
|
||||||
|
| TextureQuality | Texture streaming pool |
|
||||||
|
| PostProcessQuality | Post-process complexity |
|
||||||
|
| ParticleLOD | VFX particle count |
|
||||||
|
| AudioQuality | Audio layer count |
|
||||||
|
| ViewDistance | Draw distance |
|
||||||
|
| FoliageDensity | Foliage and grass density |
|
||||||
|
| AntiAliasing | Anti-aliasing method |
|
||||||
|
| GlobalIllumination | GI quality |
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `CurrentQualityLevel` | EPerformanceQualityLevel | Active quality level |
|
||||||
|
| `TargetQualityLevel` | EPerformanceQualityLevel | Quality being transitioned to |
|
||||||
|
| `bAdaptiveMode` | Bool | Auto-adjust based on performance |
|
||||||
|
| `bUserOverride` | Bool | Player manually set quality |
|
||||||
|
| `FrameTimeHistory` | TArray\<Float\> | Rolling frame time samples |
|
||||||
|
| `AverageFrameTime` | Float | Smoothed average frame time |
|
||||||
|
| `FrameTimeVariance` | Float | Frame time stability metric |
|
||||||
|
| `TargetFrameTime` | Float | Desired frame time (e.g., 16.67ms for 60fps) |
|
||||||
|
| `PerformanceBudget` | Float | Allowed ms per frame |
|
||||||
|
| `ScalerSettings` | TMap\<EScalingTarget, float\> | Current scaling multipliers |
|
||||||
|
| `bIsScalingInProgress` | Bool | Currently adjusting settings |
|
||||||
|
| `MeasurementInterval` | Float | Seconds between performance checks |
|
||||||
|
| `AdjustmentCooldown` | Float | Min time between quality changes |
|
||||||
|
| `LastAdjustmentTime` | Float | Last quality change timestamp |
|
||||||
|
| `QualityOverrideLevel` | EPerformanceQualityLevel | Manual override level |
|
||||||
|
| `PlatformProfile` | FName | Detected platform profile |
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `Initialize` | — | — | Read saved settings, detect platform |
|
||||||
|
| `SetQualityLevel` | Level: EPerformanceQualityLevel | — | Apply quality level |
|
||||||
|
| `SetAdaptiveMode` | bEnabled: Bool | — | Toggle adaptive scaling |
|
||||||
|
| `GetCurrentQualityLevel` | — | EPerformanceQualityLevel | Current level |
|
||||||
|
| `GetRecommendedQuality` | — | EPerformanceQualityLevel | System-recommended level |
|
||||||
|
| `MeasurePerformance` | — | — | Sample frame time from GI_GameFramework |
|
||||||
|
| `EvaluateScaling` | — | — | Decide if scaling is needed |
|
||||||
|
| `ApplyScalerSettings` | Level: EPerformanceQualityLevel | — | Apply settings to subsystems |
|
||||||
|
| `SetResolutionScale` | Scale: Float | — | Screen percentage |
|
||||||
|
| `SetShadowQuality` | Level: Int32 | — | Shadow resolution preset |
|
||||||
|
| `SetTexturePoolSize` | SizeMB: Int32 | — | Texture streaming pool |
|
||||||
|
| `SetViewDistance` | Distance: Float | — | Draw distance |
|
||||||
|
| `SetFoliageDensity` | Density: Float | — | Foliage cull distance |
|
||||||
|
| `SetParticleLOD` | LODLevel: Int32 | — | Particle LOD for VFXManager |
|
||||||
|
| `SetAudioQuality` | Quality: Int32 | — | Audio layer count |
|
||||||
|
| `SetGlobalIllumination` | Quality: Int32 | — | GI method |
|
||||||
|
| `NotifySubsystemScaled` | Target: EScalingTarget | — | Broadcast scaled subsystems |
|
||||||
|
| `SavePerformanceSettings` | — | — | Persist to SS_SaveManager |
|
||||||
|
| `OnFrameTimeSpike` | SpikeValue: Float | — | Handle sudden frame drop |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[BeginPlay]
|
||||||
|
└─► Initialize():
|
||||||
|
Detect platform (Console, PC Low/Med/High, Steam Deck, etc.)
|
||||||
|
Load saved settings from SS_SaveManager
|
||||||
|
If no saved settings:
|
||||||
|
GetRecommendedQuality() based on platform
|
||||||
|
Apply quality level
|
||||||
|
Start measurement timer with MeasurementInterval
|
||||||
|
|
||||||
|
[Timer - MeasurePerformance]
|
||||||
|
└─► MeasurePerformance():
|
||||||
|
Get frame time from GI_GameFramework.FrameTimeHistory
|
||||||
|
Add to local FrameTimeHistory, keep rolling window (last 60 frames)
|
||||||
|
Calculate AverageFrameTime (exponential moving average)
|
||||||
|
Calculate FrameTimeVariance (standard deviation of history)
|
||||||
|
If bAdaptiveMode and not bUserOverride:
|
||||||
|
EvaluateScaling()
|
||||||
|
|
||||||
|
[EvaluateScaling]
|
||||||
|
└─► If AverageFrameTime > TargetFrameTime * 1.2:
|
||||||
|
(Running slow, too many frames over target)
|
||||||
|
If TimeSince LastAdjustment > AdjustmentCooldown:
|
||||||
|
SetQualityLevel(CurrentQualityLevel - 1)
|
||||||
|
└─► If AverageFrameTime < TargetFrameTime * 0.7 and FrameTimeVariance < 2.0:
|
||||||
|
(Running fast, stable, can upgrade)
|
||||||
|
If TimeSince LastAdjustment > AdjustmentCooldown * 2:
|
||||||
|
SetQualityLevel(CurrentQualityLevel + 1)
|
||||||
|
└─► If FrameTimeVariance > 5.0:
|
||||||
|
(Unstable frame times, reduce quality for stability)
|
||||||
|
SetQualityLevel(CurrentQualityLevel - 1)
|
||||||
|
|
||||||
|
[SetQualityLevel]
|
||||||
|
└─► If CurrentQualityLevel == Level: return
|
||||||
|
└─► TargetQualityLevel = Level
|
||||||
|
└─► bIsScalingInProgress = true
|
||||||
|
└─► ApplyScalerSettings(Level)
|
||||||
|
└─► For each subsystem:
|
||||||
|
BPC_LightingManager: SetParticleLOD mapped to QualityLevel
|
||||||
|
BPC_AudioManager: Reduce active layers and spatial audio
|
||||||
|
BPC_VFXManager: SetParticleLOD mapped to QualityLevel
|
||||||
|
BPC_AtmosphereController: Reduce preset complexity
|
||||||
|
└─► Apply UE console variables for resolution, shadows, textures, foliage
|
||||||
|
└─► bIsScalingInProgress = false
|
||||||
|
└─► CurrentQualityLevel = TargetQualityLevel
|
||||||
|
└─► OnQualityLevelChanged.Broadcast(CurrentQualityLevel)
|
||||||
|
└─► SavePerformanceSettings()
|
||||||
|
|
||||||
|
[ApplyScalerSettings - Quality Levels]
|
||||||
|
└─► Low:
|
||||||
|
ResolutionScale = 0.7
|
||||||
|
ShadowQuality = 0 (off/low)
|
||||||
|
TexturePoolSize = 512 MB
|
||||||
|
PostProcessQuality = Low
|
||||||
|
ParticleLOD = 2 (minimum)
|
||||||
|
AudioQuality = 0 (mono, 2 layers max)
|
||||||
|
ViewDistance = Low
|
||||||
|
FoliageDensity = 0.3
|
||||||
|
AntiAliasing = TSR Low
|
||||||
|
GlobalIllumination = None
|
||||||
|
└─► Medium:
|
||||||
|
ResolutionScale = 0.85
|
||||||
|
ShadowQuality = 1 (medium)
|
||||||
|
TexturePoolSize = 1024 MB
|
||||||
|
PostProcessQuality = Medium
|
||||||
|
ParticleLOD = 1 (reduced)
|
||||||
|
AudioQuality = 1 (stereo, 4 layers)
|
||||||
|
ViewDistance = Medium
|
||||||
|
FoliageDensity = 0.6
|
||||||
|
AntiAliasing = TSR Medium
|
||||||
|
GlobalIllumination = Lumen Low
|
||||||
|
└─► High:
|
||||||
|
ResolutionScale = 1.0
|
||||||
|
ShadowQuality = 2 (high)
|
||||||
|
TexturePoolSize = 2048 MB
|
||||||
|
PostProcessQuality = High
|
||||||
|
ParticleLOD = 0 (full)
|
||||||
|
AudioQuality = 2 (surround, all layers)
|
||||||
|
ViewDistance = High
|
||||||
|
FoliageDensity = 1.0
|
||||||
|
AntiAliasing = TSR High
|
||||||
|
GlobalIllumination = Lumen High
|
||||||
|
└─► Ultra:
|
||||||
|
ResolutionScale = 1.0
|
||||||
|
ShadowQuality = 3 (ultra)
|
||||||
|
TexturePoolSize = 4096 MB
|
||||||
|
PostProcessQuality = Cinematic
|
||||||
|
ParticleLOD = 0 (full with extras)
|
||||||
|
AudioQuality = 3 (full spatial, max layers)
|
||||||
|
ViewDistance = Epic
|
||||||
|
FoliageDensity = 1.5
|
||||||
|
AntiAliasing = TSR Epic
|
||||||
|
GlobalIllumination = Lumen Ultra
|
||||||
|
└─► Cinematic:
|
||||||
|
ResolutionScale = 1.0 (or 1.25 for supersampling)
|
||||||
|
ShadowQuality = 3 (ultra with contact shadows)
|
||||||
|
TexturePoolSize = 8192 MB
|
||||||
|
PostProcessQuality = Cinematic with extra effects
|
||||||
|
ParticleLOD = 0 with extra presets
|
||||||
|
AudioQuality = 3
|
||||||
|
ViewDistance = Epic with forced high-resolution impostors
|
||||||
|
FoliageDensity = 2.0
|
||||||
|
AntiAliasing = TSR Epic with extra samples
|
||||||
|
GlobalIllumination = Lumen Ultra + Path Tracing toggle
|
||||||
|
|
||||||
|
[OnFrameTimeSpike]
|
||||||
|
└─► If spike > 50ms (sudden freeze):
|
||||||
|
SetQualityLevel(Low) temporarily
|
||||||
|
After 5 seconds of stable performance:
|
||||||
|
Gradually restore to previous level
|
||||||
|
```
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Payload | Description |
|
||||||
|
|------|---------|-------------|
|
||||||
|
| `OnQualityLevelChanged` | NewLevel: EPerformanceQualityLevel | Broadcast quality change |
|
||||||
|
| `OnScalingTargetAdjusted` | Target: EScalingTarget, NewValue: Float | Per-target adjustment |
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target | Method | Why |
|
||||||
|
|--------|--------|-----|
|
||||||
|
| [`GI_GameFramework`](../01-core/04_GI_GameFramework.md) | Direct call | Frame time data source |
|
||||||
|
| [`SS_SaveManager`](../05-saveload/28_SS_SaveManager.md) | Direct call | Save/load settings |
|
||||||
|
| [`BPC_LightingManager`](65_BPC_LightingManager.md) | Get from player | Light quality reduction |
|
||||||
|
| [`BPC_AudioManager`](66_BPC_AudioManager.md) | Get from player | Audio quality reduction |
|
||||||
|
| [`BPC_VFXManager`](67_BPC_VFXManager.md) | Get from player | Particle LOD control |
|
||||||
|
| [`BPC_AtmosphereController`](64_BPC_AtmosphereController.md) | Get from player | Preset complexity reduction |
|
||||||
|
| [`WBP_SettingsUI`](../06-ui/44_WBP_SettingsUI.md) | Event | User quality override |
|
||||||
|
| [`BPC_PlayerController`] | Cast | Console command execution |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
- Works with any subsystem that exposes quality API: Lighting, Audio, VFX, etc.
|
||||||
|
- Frame time history rolling window: last 60 frames for smooth average
|
||||||
|
- Adaptive mode is off by default; user must enable in settings
|
||||||
|
- User override locks quality level until adaptive mode is re-enabled
|
||||||
|
- PlatformProfile stores detected hardware for initial recommendation
|
||||||
|
- Adjustment cooldown prevents oscillation between quality levels
|
||||||
|
- Per-scaling-target notifications enable UI to show specific quality changes
|
||||||
288
docs/blueprints/10-adaptive/92_BPC_ProceduralEncounter.md
Normal file
288
docs/blueprints/10-adaptive/92_BPC_ProceduralEncounter.md
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
# 70 — BPC_ProceduralEncounter
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`BPC_DifficultyManager`](89_BPC_DifficultyManager.md) — Encounter difficulty scaling
|
||||||
|
- [`BPC_FearSystem`](90_BPC_FearSystem.md) — Fear-based encounter intensity
|
||||||
|
- [`BPC_AtmosphereController`](64_BPC_AtmosphereController.md) — Atmosphere-aligned encounters
|
||||||
|
- [`BP_DynamicEvent`](68_BP_DynamicEvent.md) — Encounter as dynamic event
|
||||||
|
- [`DA_EncounterTable`](../12-content/76_DA_EncounterTable.md) — Encounter data tables (Phase 12)
|
||||||
|
- [`I_Damageable`](../08-weapons/58_I_Damageable.md) — Enemy damage integration
|
||||||
|
- [`AI_EnemyBase`](../09-ai/58_AI_EnemyBase.md) — Enemy AI controller
|
||||||
|
- [`BP_EnemyBase`](../09-ai/57_BP_EnemyBase.md) — Enemy pawn reference
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Procedurally generates enemy encounters, ambushes, and spawn events based on player state, difficulty, fear level, atmosphere, and narrative progression. Operates as a tactical encounter director that decides when, where, and what enemies to spawn, how they behave, and when to retreat or reinforce. Prevents encounter fatigue by managing cooldowns, variety, and pacing. Supports wave-based encounters, ambushes, patrol reinforcement, and boss encounters.
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
|
||||||
|
**EEncounterType**
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| Patrol | Wandering enemy, player can evade |
|
||||||
|
| Ambush | Pre-placed enemies triggered by player |
|
||||||
|
| WaveBased | Multiple waves of increasing difficulty |
|
||||||
|
| Reinforcement | Enemies called in during combat |
|
||||||
|
| Boss | Single powerful enemy |
|
||||||
|
| Escalation | Increasing intensity until player leaves area |
|
||||||
|
| Environmental | Hazard-based encounter (not enemies) |
|
||||||
|
| Scripted | Level designer placed encounter |
|
||||||
|
|
||||||
|
**ESpawnMethod**
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| FromLocations | Use pre-placed spawn points |
|
||||||
|
| FromNavMesh | Random point on nav mesh |
|
||||||
|
| FromVolumes | Spawn in volume bounds |
|
||||||
|
| FromPortals | Enemy emerges from gate/door |
|
||||||
|
| FromVent | Spawn at vent locations |
|
||||||
|
| CeilingDrop | Enemy drops from above |
|
||||||
|
| ClosestNav | Closest valid nav point to player |
|
||||||
|
|
||||||
|
**EEncounterIntensity**
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| Low | 1–2 weak enemies |
|
||||||
|
| Moderate | 2–3 standard enemies |
|
||||||
|
| High | 3–5 mixed enemies |
|
||||||
|
| Critical | 5+ enemies with elites |
|
||||||
|
| Boss | Single boss enemy |
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
**FEncounterSpawnSlot**
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| SpawnLocation | FVector | World position |
|
||||||
|
| EnemyClass | TSubclassOf\<BP_EnemyBase\> | Enemy to spawn |
|
||||||
|
| SpawnDelay | Float | Delay before this enemy appears |
|
||||||
|
| bDropIn | Bool | Animated drop-in |
|
||||||
|
| SpawnEffect | UNiagaraSystem | Visual spawn effect |
|
||||||
|
| PatrolRouteIndex | Int32 | Patrol path to follow |
|
||||||
|
|
||||||
|
**FEncounterWave**
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| WaveName | FName | Identifier |
|
||||||
|
| SpawnSlots | TArray\<FEncounterSpawnSlot\> | Enemies in this wave |
|
||||||
|
| ActivationDelay | Float | Time before wave starts |
|
||||||
|
| bWaitForAllDead | Bool | Next wave waits for all dead |
|
||||||
|
| DifficultyMultiplier | Float | Wave-specific difficulty |
|
||||||
|
| DialogueTrigger | FName | Optional dialogue on wave start |
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `ActiveEncounterType` | EEncounterType | Current encounter type |
|
||||||
|
| `EncounterState` | FName | Active, Cooldown, Completed |
|
||||||
|
| `bPlayerInEncounterZone` | Bool | Player proximity trigger |
|
||||||
|
| `EncounterRadius` | Float | Zone size |
|
||||||
|
| `MinEnemyDistance` | Float | Min distance from player to spawn |
|
||||||
|
| `MaxConcurrentEnemies` | Int32 | Hard limit on active enemies |
|
||||||
|
| `ActiveEnemies` | TArray\<BP_EnemyBase\> | Currently alive enemies |
|
||||||
|
| `Waves` | TArray\<FEncounterWave\> | Defined waves |
|
||||||
|
| `CurrentWaveIndex` | Int32 | Active wave |
|
||||||
|
| `bWaveInProgress` | Bool | Currently executing wave |
|
||||||
|
| `CooldownRemaining` | Float | Encounter cooldown |
|
||||||
|
| `MinCooldown` | Float | Min time between encounters |
|
||||||
|
| `MaxCooldown` | Float | Max time between encounters |
|
||||||
|
| `EncounterCount` | Int32 | Encounters this session |
|
||||||
|
| `MaxEncountersPerZone` | Int32 | Encounters before exhaustion |
|
||||||
|
| `bZoneExhausted` | Bool | No more encounters in this zone |
|
||||||
|
| `SpawnLocations` | TArray\<FEncounterSpawnSlot\> | Configured spawn points |
|
||||||
|
| `bUseNavMesh` | Bool | Use nav mesh for spawns |
|
||||||
|
| `EncounterVolumeBox` | FBox | Spawn volume bounds |
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `Initialize` | — | — | Register spawn points, validate setup |
|
||||||
|
| `TriggerEncounter` | Type: EEncounterType, Intensity: EEncounterIntensity | — | Start encounter |
|
||||||
|
| `TriggerWaveBasedEncounter` | Intensity: EEncounterIntensity | — | Multi-wave encounter |
|
||||||
|
| `SpawnWave` | WaveIndex: Int32 | — | Execute wave spawning |
|
||||||
|
| `SpawnSingleEnemy` | Slot: FEncounterSpawnSlot | BP_EnemyBase | Spawn one enemy |
|
||||||
|
| `GetRandomSpawnLocation` | — | FVector | Pick valid spawn point |
|
||||||
|
| `GetSpawnLocationByMethod` | Method: ESpawnMethod | FVector | Contextual spawn position |
|
||||||
|
| `OnEnemyKilled` | Enemy: BP_EnemyBase | — | Track enemy death |
|
||||||
|
| `OnAllEnemiesDead` | — | — | Wave completed or encounter ended |
|
||||||
|
| `ReinforceEncounter` | Count: Int32 | — | Spawn additional enemies |
|
||||||
|
| `EndEncounter` | — | — | Cleanup and cooldown |
|
||||||
|
| `RetreatEnemies` | — | — | All enemies flee |
|
||||||
|
| `CalculateIntensity` | — | EEncounterIntensity | Based on difficulty + fear + atmosphere |
|
||||||
|
| `IsSpawnLocationValid` | Location: FVector | Bool | Check line of sight, nav mesh |
|
||||||
|
| `IsInEncounterZone` | PlayerLocation: FVector | Bool | Proximity check |
|
||||||
|
| `StartCooldown` | — | — | Begin cooldown timer |
|
||||||
|
| `ExhaustZone` | — | — | Mark zone as depleted |
|
||||||
|
| `ResetEncounter` | — | — | Full reset for reuse |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[Initialize]
|
||||||
|
└─► Register spawn point locations from child actors or volume
|
||||||
|
└─► If bUseNavMesh:
|
||||||
|
Get nav mesh bounds from EncounterVolumeBox
|
||||||
|
└─► Listen for player proximity via overlap events
|
||||||
|
└─► Set initial EncounterState = Ready
|
||||||
|
|
||||||
|
[TriggerEncounter]
|
||||||
|
└─► If CooldownRemaining > 0 or bZoneExhausted: return
|
||||||
|
└─► EncounterState = Active
|
||||||
|
└─► ActiveEncounterType = Type
|
||||||
|
└─► CalculateIntensity()
|
||||||
|
└─► Based on Type:
|
||||||
|
Patrol: Spawn 1-2 patrol enemies, set patrol routes
|
||||||
|
Ambush: All enemies spawn at once, no delay, trigger dialogue
|
||||||
|
WaveBased: TriggerWaveBasedEncounter(Intensity)
|
||||||
|
Reinforcement: Spawn 1-3 enemies near existing combat
|
||||||
|
Boss: Spawn single boss enemy, lock area
|
||||||
|
Escalation: Start wave timer, spawn escalating until retreat or all dead
|
||||||
|
Environmental: Trigger hazard events instead of enemies
|
||||||
|
Scripted: Execute designer-defined encounter
|
||||||
|
└─► Broadcast OnEncounterStarted
|
||||||
|
|
||||||
|
[TriggerWaveBasedEncounter]
|
||||||
|
└─► CurrentWaveIndex = 0
|
||||||
|
└─► For each wave in Waves:
|
||||||
|
Wave.DifficultyMultiplier = Get difficulty scale from BPC_DifficultyManager
|
||||||
|
Apply difficulty scaling to each enemy class
|
||||||
|
└─► SpawnWave(0)
|
||||||
|
|
||||||
|
[SpawnWave]
|
||||||
|
└─► bWaveInProgress = true
|
||||||
|
└─► For each FEncounterSpawnSlot in Waves[WaveIndex].SpawnSlots:
|
||||||
|
Delay(SpawnSlot.SpawnDelay)
|
||||||
|
SSpawnSingleEnemy(SpawnSlot)
|
||||||
|
└─► If Waves[WaveIndex].DialogueTrigger:
|
||||||
|
Trigger dialogue via BPC_DialoguePlayback
|
||||||
|
|
||||||
|
[SpawnSingleEnemy]
|
||||||
|
└─► SpawnLocation = Slot.SpawnLocation
|
||||||
|
└─► If Slot.SpawnLocation == ZeroVector:
|
||||||
|
SpawnLocation = GetSpawnLocationByMethod(FromLocations or FromNavMesh)
|
||||||
|
└─► If not IsSpawnLocationValid(SpawnLocation): return
|
||||||
|
└─► Enemy = SpawnActor Deferred(Slot.EnemyClass, SpawnLocation)
|
||||||
|
└─► Set difficulty-scaled stats on Enemy:
|
||||||
|
Health *= DifficultyManager.HealthMultiplier
|
||||||
|
Damage *= DifficultyManager.DamageMultiplier
|
||||||
|
Speed *= DifficultyManager.SpeedMultiplier
|
||||||
|
└─► If Slot.PatrolRouteIndex >= 0:
|
||||||
|
Assign patrol path to enemy
|
||||||
|
└─► FinishSpawning(Enemy)
|
||||||
|
└─► If Slot.bDropIn:
|
||||||
|
Play drop-in animation on Enemy
|
||||||
|
└─► If Slot.SpawnEffect:
|
||||||
|
UNiagaraFunctionLibrary::SpawnSystemAtLocation
|
||||||
|
└─► Bind OnEnemyKilled to Enemy.OnDestroyed
|
||||||
|
└─► ActiveEnemies.Add(Enemy)
|
||||||
|
|
||||||
|
[OnEnemyKilled]
|
||||||
|
└─► ActiveEnemies.Remove(Enemy)
|
||||||
|
└─► Add to MetricsTracker for scoring
|
||||||
|
└─► If ActiveEnemies.Num() == 0:
|
||||||
|
OnAllEnemiesDead()
|
||||||
|
└─► If EncounterState == Active and ActiveEncounterType == Escalation:
|
||||||
|
CalculateIntensity()
|
||||||
|
If should escalate:
|
||||||
|
SpawnWave more enemies
|
||||||
|
If should de-escalate:
|
||||||
|
RetreatEnemies()
|
||||||
|
|
||||||
|
[OnAllEnemiesDead]
|
||||||
|
└─► If ActiveEncounterType == WaveBased:
|
||||||
|
CurrentWaveIndex++
|
||||||
|
If CurrentWaveIndex < Waves.Num():
|
||||||
|
SpawnWave(CurrentWaveIndex)
|
||||||
|
Else:
|
||||||
|
EndEncounter()
|
||||||
|
└─► Else:
|
||||||
|
EndEncounter()
|
||||||
|
|
||||||
|
[EndEncounter]
|
||||||
|
└─► EncounterState = Completed
|
||||||
|
└─► EncounterCount++
|
||||||
|
└─► If EncounterCount >= MaxEncountersPerZone:
|
||||||
|
ExhaustZone()
|
||||||
|
└─► Else:
|
||||||
|
StartCooldown(MinCooldown, MaxCooldown)
|
||||||
|
└─► Broadcast OnEncounterEnded
|
||||||
|
└─► Reward player via BPC_PlayerMetricsTracker
|
||||||
|
|
||||||
|
[CalculateIntensity]
|
||||||
|
└─► Base intensity from BPC_DifficultyManager.CurrentDifficulty
|
||||||
|
└─► Multiply by BPC_FearSystem.CurrentFearLevel factor (0.5 at Calm, 2.0 at Panic)
|
||||||
|
└─► Adjust by BPC_AtmosphereController.CurrentState:
|
||||||
|
Exploration: *0.5 (fewer enemies)
|
||||||
|
Suspense: *1.0 (standard)
|
||||||
|
Combat: *2.0 (more enemies)
|
||||||
|
Panic: *1.5 (intense but fewer than combat)
|
||||||
|
└─► Clamp to EEncounterIntensity range
|
||||||
|
└─► Return
|
||||||
|
|
||||||
|
[GetSpawnLocationByMethod]
|
||||||
|
└─► FromLocations: Pick random from SpawnLocations array
|
||||||
|
└─► FromNavMesh: Query UNavigationSystemV1::GetRandomPointInNavigableRadius
|
||||||
|
Ensure distance from player >= MinEnemyDistance
|
||||||
|
└─► FromVolumes: Random point within EncounterVolumeBox
|
||||||
|
Project to nav mesh
|
||||||
|
└─► FromPortals: Pre-placed portal actor locations
|
||||||
|
└─► FromVent: Vent location list
|
||||||
|
└─► CeilingDrop: Above player location + random offset
|
||||||
|
Ensure valid nav mesh below
|
||||||
|
|
||||||
|
[IsSpawnLocationValid]
|
||||||
|
└─► Check distance from player >= MinEnemyDistance
|
||||||
|
└─► Line trace from player to spawn location:
|
||||||
|
If blocked and distance < 500: location is behind obstacle, valid (ambush)
|
||||||
|
If clear and distance < 500: location is visible, valid for patrol
|
||||||
|
If distance > 2000: always valid
|
||||||
|
└─► Check nav mesh projection is valid
|
||||||
|
└─► Check not overlapping existing enemies
|
||||||
|
```
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Payload | Description |
|
||||||
|
|------|---------|-------------|
|
||||||
|
| `OnEncounterStarted` | EncounterType: EEncounterType, Intensity: EEncounterIntensity | Encounter began |
|
||||||
|
| `OnEncounterEnded` | bPlayerSurvived: Bool, EnemiesKilled: Int32 | Encounter finished |
|
||||||
|
| `OnWaveStarted` | WaveIndex: Int32, WaveName: FName | New wave beginning |
|
||||||
|
| `OnWaveCompleted` | WaveIndex: Int32 | Wave cleared |
|
||||||
|
| `OnEnemySpawned` | Enemy: BP_EnemyBase | Individual enemy spawned |
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target | Method | Why |
|
||||||
|
|--------|--------|-----|
|
||||||
|
| [`BPC_DifficultyManager`](89_BPC_DifficultyManager.md) | Direct call | Difficulty scaling for encounter |
|
||||||
|
| [`BPC_FearSystem`](90_BPC_FearSystem.md) | Get owner | Fear influence on intensity |
|
||||||
|
| [`BPC_AtmosphereController`](64_BPC_AtmosphereController.md) | Get owner | Atmosphere alignment |
|
||||||
|
| [`BP_DynamicEvent`](68_BP_DynamicEvent.md) | Cast | Encounter as dynamic event |
|
||||||
|
| [`AI_EnemyBase`](../09-ai/55_AI_EnemyBase.md) | On spawned | Set encounter-aware behaviors |
|
||||||
|
| [`BP_EnemyBase`](../09-ai/57_BP_EnemyBase.md) | On spawned | Set difficulty-scaled stats |
|
||||||
|
| [`BPC_PlayerMetricsTracker`](../02-player/14_BPC_PlayerMetricsTracker.md) | Event | Encounter scoring |
|
||||||
|
| [`BPC_NarrativeState`](../07-narrative/36_BPC_NarrativeState.md) | Event | Narrative-gated encounters |
|
||||||
|
| [`BPC_DialoguePlayback`](../07-narrative/37_BPC_DialoguePlayback.md) | Event | Wave-start dialogue |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
- Place BPC_ProceduralEncounter on a volume actor or game mode
|
||||||
|
- Configure spawn points as child actors or arrays in details panel
|
||||||
|
- Encounter exhaustion prevents infinite grinding in one area
|
||||||
|
- Difficulty + Fear + Atmosphere combine to produce dynamic intensity
|
||||||
|
- Nav mesh spawning ensures enemies always have valid pathfinding
|
||||||
|
- Encounter state machine: Ready -> Active -> Cooldown -> Exhausted
|
||||||
|
- All encounters work with or without pre-placed spawn locations
|
||||||
|
- Each enemy spawned gets difficulty-scaled stats at spawn time
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
# BPC_AdaptiveEnvironmentDirector — Adaptive Environment Director
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- **Requires:** [`BPC_PlaystyleClassifier`](BPC_PlaystyleClassifier.md) — Subscribes to `OnPlaystyleChanged` for routing
|
||||||
|
- **Required By:** [`BPC_AtmosphereStateController`](BPC_AtmosphereStateController.md) — Receives atmosphere change commands
|
||||||
|
- **Required By:** [`BPC_ScareEventSystem`](BPC_ScareEventSystem.md) — Coordinates scare scheduling with playstyle
|
||||||
|
- **Required By:** [`BPC_PacingDirector`](BPC_PacingDirector.md) — Receives pacing guidance
|
||||||
|
- **Required By:** [`BPC_MemoryDriftSystem`](BPC_MemoryDriftSystem.md) — Triggers room mutations based on adaptation rules
|
||||||
|
- **Required By:** [`SS_AudioManager`](../10-adaptive/132_SS_AudioManager.md) — Music state changes (via `SetMusicLayer()`)
|
||||||
|
- **Engine/Plugin Requirements:** GameplayTags, DA_AdaptationRule data assets
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
The master controller of all adaptive systems. Receives playstyle classification and orchestrates world responses — atmosphere changes, scare scheduling, pacing adjustments, and memory drift triggers. Enforces cooldowns between major adaptive changes to prevent overwhelming the player.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Enums
|
||||||
|
|
||||||
|
*No new enums. Uses playstyle tags and atmosphere tags from dependent systems.*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Structs
|
||||||
|
|
||||||
|
*No new structs defined. Uses [`DA_AdaptationRule`](../14-data-assets/DA_AdaptationRule.md) for rule definitions.*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Variables
|
||||||
|
|
||||||
|
### Configuration (Instance Editable, Expose On Spawn)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `AdaptationRules` | Array of DA_AdaptationRule | Empty | Adaptation | Rule set defining playstyle → adaptation mappings |
|
||||||
|
| `AdaptationCooldown` | Float | 60.0 | Adaptation | Minimum seconds between major adaptive changes |
|
||||||
|
|
||||||
|
### Internal (Private / Protected, No Expose)
|
||||||
|
|
||||||
|
| Variable | Type | Default | Category | Description |
|
||||||
|
|----------|------|---------|----------|-------------|
|
||||||
|
| `LastAdaptationTime` | Float | 0.0 | Internal | Game time of last adaptation trigger |
|
||||||
|
| `ActiveAtmosphereTag` | GameplayTag | None | Internal | Currently active atmosphere state |
|
||||||
|
| `CooldownTimer` | TimerHandle | — | Internal | Timer tracking cooldown expiry |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Functions
|
||||||
|
|
||||||
|
### Public Functions
|
||||||
|
|
||||||
|
#### `EvaluateAndAdapt` → `void`
|
||||||
|
- **Description:** Main entry point. Called when playstyle changes or on timer. Evaluates adaptation rules against current playstyle and triggers appropriate responses.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `PlaystyleTag` | GameplayTag | Current player playstyle |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
- **Flow:** Check cooldown → iterate AdaptationRules → find matching rules → execute adaptation actions → update LastAdaptationTime
|
||||||
|
|
||||||
|
#### `RequestAdaptation` → `void`
|
||||||
|
- **Description:** Forces an immediate adaptation check bypassing cooldown. Used for scripted/boss encounters.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `AdaptationTag` | GameplayTag | Specific adaptation to trigger |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `SetAdaptationRules` → `void`
|
||||||
|
- **Description:** Replaces the active rule set (used when entering new chapters/zones).
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `NewRules` | Array of DA_AdaptationRule | Replacement rule set |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `GetActiveAtmosphere` → `GameplayTag`
|
||||||
|
- **Description:** Returns the currently active atmosphere tag.
|
||||||
|
- **Parameters:** None
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
#### `DisableAdaptation` → `void`
|
||||||
|
- **Description:** Disables all adaptive responses. Used during cutscenes, boss fights, or scripted sequences.
|
||||||
|
- **Parameters:**
|
||||||
|
| Param | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `bDisabled` | Bool | True = disable, False = re-enable |
|
||||||
|
- **Blueprint Authority:** Any
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Event Dispatchers
|
||||||
|
|
||||||
|
| Dispatcher | Parameters | Bind Access | Description |
|
||||||
|
|------------|-----------|-------------|-------------|
|
||||||
|
| `OnAdaptationTriggered` | RuleTag: GameplayTag, PlaystyleTag: GameplayTag | Public | An adaptation rule was executed |
|
||||||
|
| `OnAtmosphereChangeRequested` | NewAtmosphereTag: GameplayTag | Public | Atmosphere change needed |
|
||||||
|
| `OnScareScheduleRequested` | ScareTier: E_ScareTier | Public | Scare event should be considered |
|
||||||
|
| `OnMemoryDriftRequested` | DriftIntensity: Float | Public | Memory drift/room mutation suggested |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Overridden Events / Custom Events
|
||||||
|
|
||||||
|
### Event: `BeginPlay`
|
||||||
|
- **Description:** Subscribes to [`BPC_PlaystyleClassifier`](BPC_PlaystyleClassifier.md)`OnPlaystyleChanged` dispatcher.
|
||||||
|
- **Flow:**
|
||||||
|
1. Find BPC_PlaystyleClassifier and subscribe to OnPlaystyleChanged
|
||||||
|
2. If classifier exists and has a current playstyle, perform initial EvaluateAndAdapt
|
||||||
|
|
||||||
|
### Event: `OnPlaystyleChanged`
|
||||||
|
- **Description:** Called when playstyle classification changes. Triggers re-evaluation.
|
||||||
|
- **Flow:**
|
||||||
|
1. If cooldown has elapsed → EvaluateAndAdapt(NewPlaystyleTag)
|
||||||
|
2. Else → queue adaptation for after cooldown expiry
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blueprint Graph Logic Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[BeginPlay] --> B[Subscribe to PlaystyleClassifier.OnPlaystyleChanged]
|
||||||
|
B --> C[Idle]
|
||||||
|
C --> D{OnPlaystyleChanged fires?}
|
||||||
|
D --> E{AdaptationCooldown elapsed?}
|
||||||
|
E -->|Yes| F[EvaluateAndAdapt NewPlaystyleTag]
|
||||||
|
E -->|No| G[Queue for cooldown expiry]
|
||||||
|
F --> H[Iterate AdaptationRules]
|
||||||
|
H --> I{Rule matches playstyle?}
|
||||||
|
I -->|Yes| J[Execute adaptation actions]
|
||||||
|
I -->|No| K[Skip rule]
|
||||||
|
J --> L[Route to sub-systems]
|
||||||
|
L --> M[Atmosphere → BPC_AtmosphereStateController]
|
||||||
|
L --> N[Scare → BPC_ScareEventSystem]
|
||||||
|
L --> O[Pacing → BPC_PacingDirector]
|
||||||
|
L --> P[Audio → SS_AudioManager.SetMusicLayer]
|
||||||
|
L --> Q[Drift → BPC_MemoryDriftSystem]
|
||||||
|
M --> R[Update LastAdaptationTime]
|
||||||
|
N --> R
|
||||||
|
O --> R
|
||||||
|
P --> R
|
||||||
|
Q --> R
|
||||||
|
R --> S[Broadcast OnAdaptationTriggered]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Communication Matrix
|
||||||
|
|
||||||
|
| Who Talks | How | What Is Sent |
|
||||||
|
|-----------|-----|-------------|
|
||||||
|
| [`BPC_PlaystyleClassifier`](BPC_PlaystyleClassifier.md) | Dispatcher (`OnPlaystyleChanged`) | `NewPlaystyleTag: GameplayTag` |
|
||||||
|
| [`BPC_AtmosphereStateController`](BPC_AtmosphereStateController.md) | Direct call | `SetAtmosphere(NewAtmosphereTag, BlendTime)` |
|
||||||
|
| [`BPC_ScareEventSystem`](BPC_ScareEventSystem.md) | Direct call | Scare scheduling parameters based on playstyle |
|
||||||
|
| [`BPC_PacingDirector`](BPC_PacingDirector.md) | Direct call | Tension adjustments |
|
||||||
|
| [`SS_AudioManager`](../10-adaptive/132_SS_AudioManager.md) | Direct call | `SetMusicLayer(LayerIndex, Sound, Intensity)` |
|
||||||
|
| [`BPC_MemoryDriftSystem`](BPC_MemoryDriftSystem.md) | Direct call | `TriggerDrift(Intensity)` |
|
||||||
|
| [`DA_AdaptationRule`](../14-data-assets/DA_AdaptationRule.md) | Data asset read | Rule definition: conditions, targets, parameters |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Validation / Testing Checklist
|
||||||
|
|
||||||
|
- [ ] AdaptationRules array is populated for the current level/chapter
|
||||||
|
- [ ] EvaluateAndAdapt respects AdaptationCooldown
|
||||||
|
- [ ] OnPlaystyleChanged triggers correct rule for the new playstyle
|
||||||
|
- [ ] RequestAdaptation bypasses cooldown for scripted events
|
||||||
|
- [ ] SetAdaptationRules swaps rule set cleanly on chapter transitions
|
||||||
|
- [ ] DisableAdaptation prevents all adaptive responses during cutscenes
|
||||||
|
- [ ] Edge case: no rules match current playstyle → no changes made
|
||||||
|
- [ ] Edge case: multiple rules match → all executed in order
|
||||||
|
- [ ] Edge case: adaptation cooldown during rapid playstyle changes → only last change applies
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Reuse Notes
|
||||||
|
|
||||||
|
- AdaptationRules are defined per chapter/zone via DA_AdaptationRule data assets — swap rules on level load
|
||||||
|
- AdaptationCooldown prevents jarring rapid changes; tune per project for desired responsiveness
|
||||||
|
- This director is the single routing hub for all adaptive sub-systems — do NOT create direct cross-system links
|
||||||
|
- For non-adaptive games, leave AdaptationRules empty — all sub-systems run on their defaults
|
||||||
|
- Extend DA_AdaptationRule with new action types for project-specific adaptations
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Specification based on Master Section 9.2, line 2737.*
|
||||||
165
docs/blueprints/10-adaptive/94_BPC_AtmosphereStateController.md
Normal file
165
docs/blueprints/10-adaptive/94_BPC_AtmosphereStateController.md
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
# BPC_AtmosphereStateController — Atmosphere State Controller
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`BPC_FearSystem`](90_BPC_FearSystem.md) — Fear state input
|
||||||
|
- [`BPC_DifficultyManager`](89_BPC_DifficultyManager.md) — Tuning bias
|
||||||
|
- [`BPC_LightEventController`](BPC_LightEventController.md) — Lighting response orchestration
|
||||||
|
- [`SS_AudioManager`](../10-adaptive/132_SS_AudioManager.md) — Audio ambience orchestration (via `PlayAmbient()`, `SetMusicLayer()`)
|
||||||
|
- [`BPC_NarrativeStateSystem`](../07-narrative/58_BPC_NarrativeStateSystem.md) — Story phase input
|
||||||
|
- [`BPC_PlayerMetricsTracker`](../02-player/15_BPC_PlayerMetricsTracker.md) — Player state input
|
||||||
|
- [`BPC_PerformanceScaler`](91_BPC_PerformanceScaler.md) — Performance budget input
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Central atmosphere orchestrator. Manages the overall mood and tension of the environment by coordinating lighting, audio, VFX, and other atmospheric systems based on the current fear state, narrative phase, difficulty bias, and player context. Acts as the single decision point for all ambient presentation, decoupling individual media systems from game logic.
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `CurrentAtmosphereState` | EAtmosphereState | Active atmosphere preset |
|
||||||
|
| `AtmosphereIntensity` | Float (0.0–1.0) | Global intensity bias |
|
||||||
|
| `FearIntensityBias` | Float | From DifficultyManager |
|
||||||
|
| `NarrativeAtmosphereOverride` | EAtmosphereState | Story-forced atmosphere |
|
||||||
|
| `bAllowAtmosphereChanges` | Bool | Master toggle |
|
||||||
|
| `bHasNarrativeOverride` | Bool | Active story override |
|
||||||
|
| `AmbientTransitionTime` | Float | Seconds for crossfade between states |
|
||||||
|
| `CurrentLightingPreset` | FName | Active lighting key |
|
||||||
|
| `CurrentAudioPreset` | FName | Active ambient audio key |
|
||||||
|
| `CurrentVFXPreset` | FName | Active VFX preset key |
|
||||||
|
| `AtmosphereBlendWeight` | Float (0.0–1.0) | Current crossfade progress |
|
||||||
|
| `bIsInPanicMode` | Bool | Panic state active |
|
||||||
|
| `PanicModePreset` | FName | Preset for panic state |
|
||||||
|
| `SafeZonePreset` | FName | Preset for safe zones |
|
||||||
|
| `ExplorationPreset` | FName | Default exploration preset |
|
||||||
|
| `CombatPreset` | FName | Combat tension preset |
|
||||||
|
| `InvestigationPreset` | FName | Suspense/investigation preset |
|
||||||
|
| `AtmospherePresets` | TMap<FName, FAtmospherePreset> | All registered presets |
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
|
||||||
|
| Enum | Values | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| `EAtmosphereState` | Exploration, Suspense, Combat, Panic, SafeZone, Cinematic, Custom | Atmosphere modes |
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
| Struct | Fields | Description |
|
||||||
|
|--------|--------|-------------|
|
||||||
|
| `FAtmospherePreset` | State: EAtmosphereState, LightingKey: FName, AudioAmbienceKey: FName, VFXPresetKey: FName, Intensity: Float, TransitionTime: Float, bLoops: Bool | Complete atmosphere configuration |
|
||||||
|
| `FAtmosphereTransition` | FromState: EAtmosphereState, ToState: EAtmosphereState, Duration: Float, BlendCurve: UCurveFloat | Transition definition |
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `Initialize` | — | — | Register presets, bind events |
|
||||||
|
| `SetAtmosphere` | State: EAtmosphereState | — | Transition to atmosphere state |
|
||||||
|
| `SetAtmosphereIntensity` | Intensity: Float | — | Override global intensity |
|
||||||
|
| `GetCurrentAtmosphere` | — | EAtmosphereState | Current state |
|
||||||
|
| `RegisterPreset` | Key: FName, Preset: FAtmospherePreset | — | Add custom preset |
|
||||||
|
| `ApplyNarrativeOverride` | State: EAtmosphereState | — | Story-forced atmosphere |
|
||||||
|
| `ClearNarrativeOverride` | — | — | Return to gameplay atmosphere |
|
||||||
|
| `TriggerPanicMode` | — | — | Immediate panic atmosphere |
|
||||||
|
| `EndPanicMode` | — | — | Restore previous atmosphere |
|
||||||
|
| `SetTuningBias` | Bias: Float | — | From DifficultyManager |
|
||||||
|
| `GetPresetForState` | State: EAtmosphereState | FAtmospherePreset | Lookup preset |
|
||||||
|
| `EvaluateAtmosphere` | — | — | Determine best state from inputs |
|
||||||
|
| `ApplyPreset` | Preset: FAtmospherePreset | — | Push to all subsystems |
|
||||||
|
| `BlendToAtmosphere` | TargetState: EAtmosphereState, Duration: Float | — | Smooth transition |
|
||||||
|
|
||||||
|
### Event Dispatchers
|
||||||
|
|
||||||
|
| Name | Parameters | Fired When |
|
||||||
|
|------|-----------|-----------|
|
||||||
|
| `OnAtmosphereChanged` | NewState: EAtmosphereState, Intensity: Float | Atmosphere state changes |
|
||||||
|
| `OnAtmosphereIntensityChanged` | NewIntensity: Float | Intensity bias changes |
|
||||||
|
| `OnPanicAtmosphereStarted` | — | Panic mode activated |
|
||||||
|
| `OnPanicAtmosphereEnded` | — | Panic mode deactivated |
|
||||||
|
| `OnNarrativeOverrideApplied` | State: EAtmosphereState | Story override |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[Tick]
|
||||||
|
└─► If bAllowAtmosphereChanges AND not bHasNarrativeOverride:
|
||||||
|
EvaluateAtmosphere()
|
||||||
|
└─► If atmosphere state should change:
|
||||||
|
BlendToAtmosphere(TargetState, AmbientTransitionTime)
|
||||||
|
|
||||||
|
[EvaluateAtmosphere — main decision logic]
|
||||||
|
└─► Inputs: Current fear level, player metrics, narrative phase
|
||||||
|
└─► Priority order:
|
||||||
|
1. If bIsInPanicMode: Return Panic preset
|
||||||
|
2. If narrative override active: Return narrative preset
|
||||||
|
3. If player in combat with fear > Terrified: Return Combat preset
|
||||||
|
4. If player in combat: Return Combat preset
|
||||||
|
5. If player investigating or suspicious: Return Suspense preset
|
||||||
|
6. If player in safe zone: Return SafeZone preset
|
||||||
|
7. If player exploring: Return Exploration preset
|
||||||
|
└─► Apply FearIntensityBias to preset intensity
|
||||||
|
└─► Return selected preset
|
||||||
|
|
||||||
|
[SetAtmosphere — direct override]
|
||||||
|
└─► Lookup or get preset for state
|
||||||
|
└─► ApplyPreset(Preset)
|
||||||
|
└─► CurrentAtmosphereState = State
|
||||||
|
└─► Broadcast OnAtmosphereChanged
|
||||||
|
|
||||||
|
[ApplyPreset — push to subsystems]
|
||||||
|
└─► BPC_LightEventController.SetLightingPreset(Preset.LightingKey, TransitionTime)
|
||||||
|
└─► SS_AudioManager.PlayAmbient(Preset.AudioAmbienceKey, TransitionTime)
|
||||||
|
└─► Apply preset intensity modifiers to each subsystem
|
||||||
|
└─► BlendWeight = 0.0 → lerp to 1.0 over TransitionTime
|
||||||
|
|
||||||
|
[TriggerPanicMode]
|
||||||
|
└─► bIsInPanicMode = true
|
||||||
|
└─► ApplyPreset(AtmospherePresets[PanicModePreset])
|
||||||
|
└─► Broadcast OnPanicAtmosphereStarted
|
||||||
|
|
||||||
|
[EndPanicMode]
|
||||||
|
└─► bIsInPanicMode = false
|
||||||
|
└─► EvaluateAtmosphere()
|
||||||
|
└─► Broadcast OnPanicAtmosphereEnded
|
||||||
|
|
||||||
|
[ApplyNarrativeOverride]
|
||||||
|
└─► bHasNarrativeOverride = true
|
||||||
|
└─► NarrativeAtmosphereOverride = State
|
||||||
|
└─► SetAtmosphere(State)
|
||||||
|
└─► Broadcast OnNarrativeOverrideApplied
|
||||||
|
|
||||||
|
[ClearNarrativeOverride]
|
||||||
|
└─► bHasNarrativeOverride = false
|
||||||
|
└─► EvaluateAtmosphere()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target | Method | Why |
|
||||||
|
|--------|--------|-----|
|
||||||
|
| [`BPC_FearSystem`](90_BPC_FearSystem.md) | Get Owner Component | Fear state for atmosphere selection |
|
||||||
|
| [`BPC_DifficultyManager`](89_BPC_DifficultyManager.md) | Get Owner Component | Intensity bias, fear modifier |
|
||||||
|
| [`BPC_LightEventController`](BPC_LightEventController.md) | Direct call | Set lighting preset |
|
||||||
|
| [`SS_AudioManager`](../10-adaptive/132_SS_AudioManager.md) | Direct call | Set ambient audio, music layers |
|
||||||
|
| [`BPC_NarrativeStateSystem`](../07-narrative/58_BPC_NarrativeStateSystem.md) | Get Owner Component | Narrative phase for override |
|
||||||
|
| [`BPC_PlayerMetricsTracker`](../02-player/15_BPC_PlayerMetricsTracker.md) | Get Owner Component | Player context metrics |
|
||||||
|
| [`BPC_PerformanceScaler`](91_BPC_PerformanceScaler.md) | Direct call | Scale quality based on performance |
|
||||||
|
| [`SS_UIManager`](../06-ui/44_SS_UIManager.md) | Subsystem | UI atmosphere indicators |
|
||||||
|
| [`BP_NarrativeTriggerVolume`](../07-narrative/BP_NarrativeTriggerVolume.md) | Overlap event | Atmosphere cue from volume |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
- Single instance on player character or persistent game actor
|
||||||
|
- All atmosphere presets are data-driven (can be expanded via Data Assets)
|
||||||
|
- Presets are keyed by FName for extensibility without enum changes
|
||||||
|
- Narrative overrides take priority over gameplay atmosphere
|
||||||
|
- Panic mode is a temporary atmosphere state; EndPanicMode restores evaluation
|
||||||
|
- Subsystems (Lighting, Audio, VFX) are decoupled; AtmosphereStateController only sets keys and intensities
|
||||||
|
- Blend curves control crossfade smoothness between states
|
||||||
|
- Renamed from `BPC_AtmosphereController` to `BPC_AtmosphereStateController` per Master naming convention.
|
||||||
|
- Cross-references updated: `BPC_LightingManager` → `BPC_LightEventController`, `BPC_AudioManager` → `BPC_AudioAtmosphereController` → `SS_AudioManager` (132 — deprecated), `BPC_VFXManager` removed (not in Master).
|
||||||
165
docs/blueprints/10-adaptive/95_BPC_AudioAtmosphereController.md
Normal file
165
docs/blueprints/10-adaptive/95_BPC_AudioAtmosphereController.md
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
# BPC_AudioAtmosphereController — Audio Atmosphere Controller
|
||||||
|
|
||||||
|
> **⚠️ DEPRECATED — Phase 14d**
|
||||||
|
> This component is superseded by [`SS_AudioManager` (132)](../10-adaptive/132_SS_AudioManager.md), the single entry point for all MetaSounds audio playback.
|
||||||
|
> - `PlayOneShotSFX()` → `SS_AudioManager.PlaySFX()`
|
||||||
|
> - `PlayAmbientSound()` → `SS_AudioManager.PlayAmbient()`
|
||||||
|
> - `SetMusicLayer()` → `SS_AudioManager.SetMusicLayer()`
|
||||||
|
> - `PlayDialogue()` → `SS_AudioManager.PlayDialogue()`
|
||||||
|
> - `SetReverb()` → `SS_AudioManager.SetRoomPreset()` via `BP_RoomAudioZone`
|
||||||
|
> - `FearReactiveAudioUpdate()` → `SS_AudioManager.UpdateFearAudio()`
|
||||||
|
> - Volume modifiers → `SS_AudioManager.SetBusVolume()` bound to `SS_SettingsSystem`
|
||||||
|
> See [`metasounds-audio-system.md`](../../architecture/metasounds-audio-system.md) for full migration guide.
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`BPC_AtmosphereStateController`](BPC_AtmosphereStateController.md) — Atmosphere state receiver
|
||||||
|
- [`BPC_FearSystem`](90_BPC_FearSystem.md) — Fear-driven audio modulation
|
||||||
|
- [`BPC_PlayerMetricsTracker`](../02-player/15_BPC_PlayerMetricsTracker.md) — Player state context
|
||||||
|
- [`UAudioComponent`] — UE built-in audio component
|
||||||
|
- [`UMetaSoundSource`] — UE 5.5 MetaSounds
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Centralized audio controller that manages ambient soundscapes, fear-reactive audio modulation, dynamic music layers, spatial audio events, and dialogue playback. Receives atmosphere state changes from AtmosphereStateController and adjusts audio parameters accordingly. Supports MetaSounds for dynamic audio generation (UE 5.5+), real-time audio parameter modulation, and multi-layered audio blending.
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `ActiveAudioPreset` | FName | Current audio snapshot |
|
||||||
|
| `TargetAudioPreset` | FName | Audio preset being transitioned to |
|
||||||
|
| `MasterVolumeModifier` | Float (0.0–2.0) | Global volume multiplier |
|
||||||
|
| `SFXVolumeModifier` | Float (0.0–2.0) | Sound effects volume |
|
||||||
|
| `MusicVolumeModifier` | Float (0.0–2.0) | Music layer volume |
|
||||||
|
| `AmbientVolumeModifier` | Float (0.0–2.0) | Ambient layer volume |
|
||||||
|
| `DialogueVolumeModifier` | Float (0.0–2.0) | Dialogue volume |
|
||||||
|
| `FearAudioIntensity` | Float (0.0–1.0) | Fear-based audio effect amount |
|
||||||
|
| `CurrentAudioLayers` | TArray<FAudioLayerState> | Active audio layers |
|
||||||
|
| `AmbientAudioComp` | UAudioComponent | Main ambient sound |
|
||||||
|
| `MusicAudioComp` | UAudioComponent | Music layer |
|
||||||
|
| `SubMusicComp1` | UAudioComponent | Sub-music layer 1 |
|
||||||
|
| `SubMusicComp2` | UAudioComponent | Sub-music layer 2 |
|
||||||
|
| `ReverbEffect` | UReverbEffect | Current reverb setting |
|
||||||
|
| `OcclusionIntensity` | Float | Obstacle-based audio occlusion |
|
||||||
|
| `ReverbWetLevel` | Float | Reverb mix amount |
|
||||||
|
| `bUseMetaSounds` | Bool | Enable MetaSound support |
|
||||||
|
| `ActiveMetaSound` | UMetaSoundSource | Current active MetaSound |
|
||||||
|
| `AudioParameterMap` | TMap<FName, float> | Dynamic MetaSound parameters |
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
**FAudioLayerState**
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| LayerName | FName | Identifier |
|
||||||
|
| AudioComp | UAudioComponent | Assigned component |
|
||||||
|
| TargetVolume | Float | Desired volume |
|
||||||
|
| CurrentVolume | Float | Actual volume (lerped) |
|
||||||
|
| FadeSpeed | Float | Volume fade rate |
|
||||||
|
| bIsPlaying | Bool | Active state |
|
||||||
|
| Priority | Int32 | Layer priority (higher = more important) |
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `Initialize` | — | — | Cache references, create audio components |
|
||||||
|
| `SetAudioPreset` | PresetKey: FName, TransitionTime: Float | — | Queue audio preset transition |
|
||||||
|
| `ApplyPresetDirect` | PresetKey: FName | — | Instant preset application |
|
||||||
|
| `PlayAmbientSound` | Sound: USoundBase, FadeInTime: Float | — | Crossfade ambient |
|
||||||
|
| `StopAmbientSound` | FadeOutTime: Float | — | Fade out ambient |
|
||||||
|
| `SetMusicLayer` | LayerIndex: Int32, Sound: USoundBase, TargetVolume: Float, FadeTime: Float | — | Set music layer with crossfade |
|
||||||
|
| `StopAllMusic` | FadeOutTime: Float | — | Fade out all music layers |
|
||||||
|
| `PlayOneShotSFX` | Sound: USoundBase, Location: FVector | — | Spatial sound effect |
|
||||||
|
| `PlayDialogue` | DialogueSound: USoundBase | — | Dialogue playback with prioritization |
|
||||||
|
| `StopDialogue` | FadeOutTime: Float | — | Stop current dialogue |
|
||||||
|
| `SetReverb` | Reverb: UReverbEffect, WetLevel: Float, TransitionTime: Float | — | Apply reverb effect |
|
||||||
|
| `SetOcclusion` | Intensity: Float | — | Obstacle audio occlusion |
|
||||||
|
| `SetAudioParameter` | ParameterName: FName, Value: Float | — | MetaSound parameter update |
|
||||||
|
| `FearReactiveAudioUpdate` | FearLevel: Float, Threshold: EFearThreshold | — | Per-tick fear audio modulation |
|
||||||
|
| `OnAtmosphereChanged` | NewState: EAtmosphereState | — | React to atmosphere change |
|
||||||
|
| `HandleRoomChange` | RoomAcousticPreset: FName | — | Room-based reverb switching |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[Tick]
|
||||||
|
└─► For each FAudioLayerState:
|
||||||
|
Lerp CurrentVolume toward TargetVolume at FadeSpeed
|
||||||
|
Update AudioComp volume
|
||||||
|
└─► FearReactiveAudioUpdate(CurrentFearLevel, CurrentThreshold)
|
||||||
|
|
||||||
|
[SetAudioPreset]
|
||||||
|
└─► If TransitionTime > 0:
|
||||||
|
Begin crossfade: lerp volumes from current to target
|
||||||
|
Transition layers sequentially by priority
|
||||||
|
└─► Else:
|
||||||
|
ApplyPresetDirect(PresetKey)
|
||||||
|
|
||||||
|
[FearReactiveAudioUpdate]
|
||||||
|
└─► Based on fear threshold:
|
||||||
|
Calm: Normal audio, no modulation
|
||||||
|
Uneasy: SubMusicLayer1 fade in (low drone), slight reverb increase
|
||||||
|
Nervous: Music pitch shift down, ambient filter low-pass, increased reverb
|
||||||
|
Afraid: Heartbeat layer, audio distortion, occlusion simulation, surround pan random
|
||||||
|
Terrified: All audio layers active, dissonant harmony, heavy reverb + echo, random cuts
|
||||||
|
Panic: Audio stutter effect, high-frequency ringing, audio buffer glitch simulation
|
||||||
|
└─► Apply fear modulation:
|
||||||
|
Set MasterVolumeModifier based on fear: 1.0 at 0 fear, 1.2 at high fear
|
||||||
|
Set SFX distortion/overtone shift
|
||||||
|
Adjust reverb wet level proportional to fear
|
||||||
|
|
||||||
|
[OnAtmosphereChanged]
|
||||||
|
└─► Exploration: Normal ambient, no music, natural reverb
|
||||||
|
└─► Suspense: Low drone music, increased reverb, distant SFX
|
||||||
|
└─► Combat: High-energy music, aggressive SFX, reduced reverb
|
||||||
|
└─► Panic: Chaotic layers, distortion, stutter
|
||||||
|
└─► SafeZone: Calm music, no reverb, warm ambient
|
||||||
|
└─► Cinematic: Dialogue-priority mode, cinematic mix, wide soundstage
|
||||||
|
|
||||||
|
[HandleRoomChange]
|
||||||
|
└─► Lookup room acoustic preset from data asset
|
||||||
|
└─► Apply reverb, occlusion, filter settings
|
||||||
|
└─► Fade transition over 1-2 seconds
|
||||||
|
|
||||||
|
[PlayDialogue]
|
||||||
|
└─► If current dialogue priority >= incoming dialogue priority:
|
||||||
|
Queue dialogue
|
||||||
|
└─► Else:
|
||||||
|
Stop current, play new
|
||||||
|
└─► Duck (lower volume) all other audio layers during dialogue
|
||||||
|
└─► Restore on dialogue complete
|
||||||
|
|
||||||
|
[PlayOneShotSFX]
|
||||||
|
└─► Use UGameplayStatics::SpawnSoundAtLocation
|
||||||
|
└─► Apply occlusion based on line-of-sight to player
|
||||||
|
└─► Apply distance-based low-pass filter
|
||||||
|
```
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target | Method | Why |
|
||||||
|
|--------|--------|-----|
|
||||||
|
| [`BPC_AtmosphereStateController`](BPC_AtmosphereStateController.md) | Called by | Receive atmosphere state changes |
|
||||||
|
| [`BPC_FearSystem`](90_BPC_FearSystem.md) | Get Owner Component | Fear level for reactive audio |
|
||||||
|
| [`BPC_PlayerMetricsTracker`](../02-player/15_BPC_PlayerMetricsTracker.md) | Direct call | Contextual audio (health low, hiding) |
|
||||||
|
| [`BPC_PerformanceScaler`](91_BPC_PerformanceScaler.md) | Direct call | Reduce audio quality on low spec |
|
||||||
|
| [`BP_Checkpoint`](../05-saveload/BP_Checkpoint.md) | Event | Restore audio state on load |
|
||||||
|
| [`BPC_DialoguePlaybackSystem`](../07-narrative/40_BPC_DialoguePlaybackSystem.md) | Direct call | Dialogue audio routing |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
- Audio layers allow stacking multiple ambiences
|
||||||
|
- MetaSound parameter map enables dynamic audio without new Blueprint logic
|
||||||
|
- Dialogue ducking is automatic; use priority for interruption rules
|
||||||
|
- Audio occlusion uses line trace from sound origin to player
|
||||||
|
- All audio preset transitions are smooth crossfades, never cuts
|
||||||
|
- PerformanceScaler can disable spatial audio and reduce layer count on low-end
|
||||||
|
- Renamed from `BPC_AudioManager` to `BPC_AudioAtmosphereController` per Master naming convention.
|
||||||
|
- Cross-references updated: `BPC_AtmosphereController` → `BPC_AtmosphereStateController`, `BPC_PlayerMetricsTracker` → updated path, `BPC_CheckpointSystem` → `BP_Checkpoint`, `BPC_DialoguePlayback` → `BPC_DialoguePlaybackSystem`.
|
||||||
119
docs/blueprints/10-adaptive/96_BPC_LightEventController.md
Normal file
119
docs/blueprints/10-adaptive/96_BPC_LightEventController.md
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
# BPC_LightEventController — Light Event Controller
|
||||||
|
|
||||||
|
## Blueprint Spec — UE 5.5–5.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Parent Class
|
||||||
|
`ActorComponent`
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- [`BPC_AtmosphereStateController`](BPC_AtmosphereStateController.md) — Atmosphere state receiver
|
||||||
|
- [`BPC_FearSystem`](90_BPC_FearSystem.md) — Fear-driven lighting modulation
|
||||||
|
- [`BPC_PerformanceScaler`](91_BPC_PerformanceScaler.md) — Performance budget input
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Manages all dynamic lighting in the game world including global color grading, fog, shadows, emissive materials, point light intensity modulation, and lighting transitions. Receives preset keys from AtmosphereStateController and applies them with smooth crossfades. Supports fear-reactive lighting effects (flicker, desaturation, shadow intensity) based on the player's fear state.
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `ActivePresetKey` | FName | Currently applied lighting preset |
|
||||||
|
| `TargetPresetKey` | FName | Preset being transitioned to |
|
||||||
|
| `CurrentColorGrading` | FPostProcessSettings | Active post-process |
|
||||||
|
| `CurrentFogDensity` | Float | Active exponential fog density |
|
||||||
|
| `CurrentShadowIntensity` | Float | Shadow darkness bias |
|
||||||
|
| `LightFlickerIntensity` | Float (0.0–1.0) | Fear-driven flicker effect |
|
||||||
|
| `LightFlickerFrequency` | Float | Flicker speed |
|
||||||
|
| `bEnableFearReactiveLights` | Bool | Fear modulation toggle |
|
||||||
|
| `TransitionDuration` | Float | Current crossfade duration |
|
||||||
|
| `TransitionProgress` | Float (0.0–1.0) | Crossfade interpolation |
|
||||||
|
| `RegisteredLights` | TArray<ULightComponent> | Dynamic lights to modulate |
|
||||||
|
| `DesaturationAmount` | Float (0.0–1.0) | Fear-driven color loss |
|
||||||
|
| `VignetteIntensity` | Float | Post-process vignette |
|
||||||
|
| `bIsInDarkVolume` | Bool | Inside darkness volume |
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Name | Inputs | Outputs | Description |
|
||||||
|
|------|--------|---------|-------------|
|
||||||
|
| `Initialize` | — | — | Cache references, bind events |
|
||||||
|
| `SetLightingPreset` | PresetKey: FName, TransitionTime: Float | — | Queue preset transition |
|
||||||
|
| `ApplyPresetDirect` | PresetKey: FName | — | Instant preset application |
|
||||||
|
| `GetCurrentPresetKey` | — | FName | Current active preset |
|
||||||
|
| `RegisterDynamicLight` | Light: ULightComponent | — | Add to modulation list |
|
||||||
|
| `UnregisterDynamicLight` | Light: ULightComponent | — | Remove from modulation list |
|
||||||
|
| `SetFearFlicker` | Intensity: Float, Frequency: Float | — | Apply flicker to all registered lights |
|
||||||
|
| `SetDesaturation` | Amount: Float | — | Post-process color loss |
|
||||||
|
| `SetVignette` | Intensity: Float | — | Post-process vignette |
|
||||||
|
| `SetShadowIntensity` | Intensity: Float | — | Global shadow bias |
|
||||||
|
| `SetFogDensity` | Density: Float | — | Exponential fog density |
|
||||||
|
| `EnterDarkVolume` | — | — | Enter darkness zone |
|
||||||
|
| `ExitDarkVolume` | — | — | Leave darkness zone |
|
||||||
|
| `FearReactiveUpdate` | FearLevel: Float, Threshold: EFearThreshold | — | Per-tick fear modulation |
|
||||||
|
|
||||||
|
### Blueprint Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[Tick]
|
||||||
|
└─► If TransitionProgress < 1.0:
|
||||||
|
TransitionProgress += DeltaTime / TransitionDuration
|
||||||
|
Lerp all lighting properties between current and target
|
||||||
|
On complete: ActivePresetKey = TargetPresetKey
|
||||||
|
└─► If bEnableFearReactiveLights:
|
||||||
|
FearReactiveUpdate(CurrentFearLevel, CurrentThreshold)
|
||||||
|
|
||||||
|
[SetLightingPreset]
|
||||||
|
└─► If TransitionTime > 0:
|
||||||
|
TargetPresetKey = PresetKey
|
||||||
|
TransitionDuration = TransitionTime
|
||||||
|
TransitionProgress = 0.0
|
||||||
|
Begin lerp from current to target
|
||||||
|
└─► Else:
|
||||||
|
ApplyPresetDirect(PresetKey)
|
||||||
|
|
||||||
|
[FearReactiveUpdate]
|
||||||
|
└─► Based on fear threshold:
|
||||||
|
Calm: No lighting change
|
||||||
|
Uneasy: Subtle light flicker (intensity 0.1), no color change
|
||||||
|
Nervous: Light flicker (0.3), slight desaturation (0.1)
|
||||||
|
Afraid: Heavy flicker (0.6), desaturation (0.3), vignette (0.2)
|
||||||
|
Terrified: Max flicker (0.8), desaturation (0.5), vignette (0.4), shadow intensify
|
||||||
|
Panic: Strobe flicker, heavy desaturation (0.7), max vignette
|
||||||
|
└─► Apply to all RegisteredLights:
|
||||||
|
For each ULightComponent:
|
||||||
|
If flicker enabled: randomize Intensity between 0..Original* (1 - LightFlickerIntensity)
|
||||||
|
└─► Apply post-process:
|
||||||
|
SetDesaturation(DesaturationAmount)
|
||||||
|
SetVignette(VignetteIntensity)
|
||||||
|
|
||||||
|
[EnterDarkVolume]
|
||||||
|
└─► bIsInDarkVolume = true
|
||||||
|
└─► Override current preset with Darkness preset
|
||||||
|
└─► Disable fear-reactive lights during dark volume
|
||||||
|
└─► DarkVolume exclusive: very low ambient, high fog, long shadows
|
||||||
|
|
||||||
|
[ExitDarkVolume]
|
||||||
|
└─► bIsInDarkVolume = false
|
||||||
|
└─► Restore previous lighting preset
|
||||||
|
└─► Re-enable fear-reactive lights
|
||||||
|
```
|
||||||
|
|
||||||
|
### Communications With
|
||||||
|
|
||||||
|
| Target | Method | Why |
|
||||||
|
|--------|--------|-----|
|
||||||
|
| [`BPC_AtmosphereStateController`](BPC_AtmosphereStateController.md) | Called by | Receive lighting preset keys |
|
||||||
|
| [`BPC_FearSystem`](90_BPC_FearSystem.md) | Get Owner Component | Fear level for reactive lighting |
|
||||||
|
| [`BPC_PerformanceScaler`](91_BPC_PerformanceScaler.md) | Direct call | Reduce light quality on low spec |
|
||||||
|
| [`BP_Checkpoint`](../05-saveload/BP_Checkpoint.md) | Event | Restore lighting on load |
|
||||||
|
|
||||||
|
### Reuse Notes
|
||||||
|
- Works with any ULightComponent (point, spot, directional, rect)
|
||||||
|
- Post-process settings are blended, not stomped
|
||||||
|
- Dark volumes override atmosphere-driven lighting
|
||||||
|
- Fear-reactive lighting is additive on top of current preset
|
||||||
|
- PerformanceScaler can disable dynamic lighting and fear flicker on low-end hardware
|
||||||
|
- Renamed from `BPC_LightingManager` to `BPC_LightEventController` per Master naming convention.
|
||||||
|
- Cross-references updated: `BPC_AtmosphereController` → `BPC_AtmosphereStateController`, `BPC_CheckpointSystem` → `BP_Checkpoint`.
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user