# 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`. --- ## Manual Implementation Guide ### Class Setup 1. Create Blueprint Class: Parent = `ActorComponent`, Name = `BPC_EndingAccumulator` 2. Add to Player Character or GameState ### Variable Init (BeginPlay) ``` Event BeginPlay ├─ Set EndingScores = empty Map ├─ Load all DA_EndingData assets into EndingDefinitions array └─ Bind to BPC_NarrativeStateSystem.OnFlagChanged ``` ### Function Node-by-Node #### `OnNarrativeFlagChanged(Flag: GameplayTag, NewValue: Boolean)` → `void` ``` Step 1: If NewValue == false → Return (only accumulate on true flags) Step 2: ForEach EndingDefinitions: ForEach Entry in Ending.ScoreEntries: If Entry.FlagTag == Flag: EndingScores[Ending.EndingTag] += Entry.Weight Break inner loop Step 3: Fire OnEndingScoresUpdated(EndingScores) ``` #### `EvaluateEndings()` → `DA_EndingData` ``` Step 1: QualifiedList = empty array Step 2: ForEach EndingDefinitions: If EndingScores[Ending.EndingTag] >= Ending.RequiredScore: Check Ending.RequiredFlags: all set? → Add to QualifiedList Check Ending.ExclusiveFlags: any set? → Skip Step 3: Sort QualifiedList by Priority (descending) Step 4: Return QualifiedList[0] (highest priority qualified ending) ``` #### `GetQualifiedEndings()` → `Array` *(Pure)* ``` Same logic as EvaluateEndings but returns all qualified, not just best ``` #### `TriggerGameEnding(OverrideTag: GameplayTag)` → `void` ``` Step 1: If OverrideTag valid → SelectedEnding = Find(OverrideTag) Else: SelectedEnding = EvaluateEndings() Step 2: Fire OnGameEndingTriggered(SelectedEnding) Step 3: GM_CoreGameMode.TriggerEnding(SelectedEnding.EndingTag) → initiates cutscene, game-over screen, credits ``` ### Build Checklist - [ ] Create BPC_EndingAccumulator - [ ] Define DA_EndingData with ScoreEntries, RequiredFlags, ExclusiveFlags, RequiredScore, Priority - [ ] Bind to NarrativeStateSystem.OnFlagChanged → accumulate scores - [ ] Implement EvaluateEndings with qualification gating - [ ] Wire to GM_CoreGameMode.TriggerEnding - [ ] Test: make choices throughout game → check ending scores → trigger end → correct ending plays