# 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.

---

## Manual Implementation Guide

### Class Setup
1. Create Blueprint Class: Parent = `ActorComponent`, Name = `BPC_NarrativeStateSystem`
2. Add to Player Character or GameState (if shared)
3. Implement Interface: `I_Persistable`

### Variable Init (BeginPlay)
```
Event BeginPlay
  ├─ Set NarrativeFlags = empty Map<GameplayTag, Boolean>
  ├─ Set NarrativeValues = empty Map<GameplayTag, Float>
  ├─ Set NarrativeHistory = empty Array<GameplayTag>
  ├─ Set MaxHistorySize = 500
  └─ If implementing I_Persistable: register with SS_SaveManager
```

### Function Node-by-Node

#### `SetFlag(Tag: GameplayTag, Value: Boolean)` → `void`
```
Step 1: OldValue = NarrativeFlags.Find(Tag) or false
Step 2: NarrativeFlags.Add(Tag, Value)  ← Map Add (overwrites if exists)
Step 3: If Value == true AND OldValue == false:
  NarrativeHistory.Add(Tag)
  If NarrativeHistory.Length > MaxHistorySize: Remove oldest entry
Step 4: Fire OnFlagChanged(Tag, Value)
Step 5: Notify I_Persistable: Mark dirty for save
```
**Nodes:** `Map Find`, `Map Add`, `Array Add`, `Array Length`, `Remove Index 0`

#### `SetValue(Tag, Value: Float)` → `void`
```
Step 1: NarrativeValues.Add(Tag, Value)
Step 2: Fire OnValueChanged(Tag, Value)
```

#### `GetFlag(Tag)` → `Boolean` *(Pure)*
```
NarrativeFlags.Find(Tag) → if found: return value, else: return false
```

#### `HasAllFlags(Tags: Array<GameplayTag>)` → `Boolean`
```
ForEach Tags:
  If GetFlag(ArrayElement) == false → Return false
Return true
```

#### `CollectState()` → `S_WorldObjectState` *(I_Persistable)*
```
Step 1: Create S_WorldObjectState
Step 2: Serialize NarrativeFlags: ForEach → store (Tag, Value) as string pairs in CustomData
Step 3: Serialize NarrativeValues similarly
Step 4: Return struct
```

#### `RestoreState(Data: S_WorldObjectState)` *(I_Persistable)*
```
Step 1: Clear NarrativeFlags, NarrativeValues, NarrativeHistory
Step 2: Parse CustomData → ForEach entry → NarrativeFlags.Add(Tag, BoolValue)
Step 3: Parse values → NarrativeValues.Add(Tag, FloatValue)
```

### Build Checklist
- [ ] Create BPC_NarrativeStateSystem, add to Player Character
- [ ] Define variables: NarrativeFlags (Map), NarrativeValues (Map), NarrativeHistory (Array), MaxHistorySize
- [ ] Implement SetFlag/GetFlag with map operations
- [ ] Implement SetValue/GetValue for numeric values
- [ ] Implement HasAllFlags/HasAnyFlags for batch queries
- [ ] Implement CollectState/RestoreState for I_Persistable
- [ ] Create OnFlagChanged/OnValueChanged event dispatchers
- [ ] Test: call SetFlag → verify OnFlagChanged fires → other systems react