> For the complete documentation index, see [llms.txt](https://malbersanimations.gitbook.io/animal-controller/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://malbersanimations.gitbook.io/animal-controller/malbers-questforge/dialogue-system/dialogue-manager.md).

# Dialogue Manager

### Overview

`DialogueManager` is the central runtime engine of the Dialogue System. It takes a `DialogueContainerSO` asset, traverses the node graph from start to end, and coordinates every system involved in the conversation — the dialogue UI, Cinemachine cameras, Unity Timeline, Malbers character actions, quest operations, audio playback, and player input gating.

The component is a **persistent singleton** (`DontDestroyOnLoad`) and should appear once in your first scene. All other dialogue-related scripts communicate with it through `DialogueManager.Instance`.

<figure><img src="https://963537199-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Lzhr1XSMzMqNXjRnNlb%2Fuploads%2FBMP2ecMUpbl4xxpaOAdI%2Fimage.png?alt=media&amp;token=e75a3f82-787e-4b91-8707-d3bff564f445" alt=""><figcaption></figcaption></figure>

***

### Inspector Properties

#### Configuration

| Property       | Type   | Description                                         |
| -------------- | ------ | --------------------------------------------------- |
| **Debug Mode** | `bool` | Enables verbose per-node logging via `QuestLogger`. |

#### UI References

| Property        | Type         | Description                                                              |
| --------------- | ------------ | ------------------------------------------------------------------------ |
| **Dialogue UI** | `DialogueUI` | Required. The UI component that renders speaker text and player choices. |

#### Camera Integration

| Property                      | Type               | Description                                                              |
| ----------------------------- | ------------------ | ------------------------------------------------------------------------ |
| **Cinemachine Brain**         | `CinemachineBrain` | The scene's Cinemachine Brain. Auto-found at Start if not assigned.      |
| **Default Camera Blend Time** | `float`            | Default blend duration (seconds) for camera transitions. Default: `1.0`. |

#### Events

| Event                 | Signature                         | Fired When                                                                     |
| --------------------- | --------------------------------- | ------------------------------------------------------------------------------ |
| **OnDialogueStarted** | `UnityEvent<DialogueContainerSO>` | Dialogue begins.                                                               |
| **OnDialogueLine**    | `UnityEvent<string>`              | A SimpleDialogue or Choice node displays text. Passes the raw dialogue string. |
| **OnDialogueEnded**   | `UnityEvent`                      | Dialogue ends (End Node reached, or `EndDialogue()` called).                   |
| **OnChoiceMade**      | `UnityEvent<int>`                 | Player selects a choice. Passes the zero-based choice index.                   |

#### Timeline Events

| Event                   | Signature                   | Fired When                             |
| ----------------------- | --------------------------- | -------------------------------------- |
| **OnTimelineStarted**   | `UnityEvent<PlayableAsset>` | A Timeline begins playing.             |
| **OnTimelineCompleted** | `UnityEvent`                | A Timeline finishes playing naturally. |
| **OnTimelineSkipped**   | `UnityEvent`                | A Timeline is skipped by the player.   |

***

### How It Works

#### Startup & Teardown

When `StartDialogue(DialogueContainerSO)` is called:

1. If a dialogue is already running, it ends it first.
2. The player's movement is locked via `ILockCharacter.Lock(true)` (fetched from `QuestManager.Instance.playerTransform`).
3. The currently active Cinemachine virtual camera is stored as `previousCamera`, along with its priority.
4. `nodeCache` and `outgoingEdgesCache` dictionaries are built from all nodes and edges in the `DialogueContainerSO` for fast O(1) graph lookups.
5. The Start Node is found and `ProcessCurrentNode()` begins execution.
6. `OnDialogueStarted` fires.

When `EndDialogue()` is called (including from the End Node):

1. Any active Timeline is stopped (final frame evaluated before stop).
2. Any active FocusOnTarget coroutine is stopped and camera targets are restored.
3. All speaker cameras that were activated during dialogue are reset to priority `1`.
4. The stored `previousCamera` is restored to its original priority.
5. The Dialogue UI is hidden.
6. Player movement is unlocked via `ILockCharacter.Lock(false)`.
7. Node/edge caches are cleared.
8. `OnDialogueEnded` fires.

***

#### Node Processing

`ProcessCurrentNode()` reads `currentNode.type` (a string) and dispatches to a type-specific handler. Execution is always sequential — the graph moves to the next node via `AdvanceToNextNode()` or, for branching nodes, directly by GUID lookup.

| Node Type                             | Behaviour                                                                                                                                                                                                                          |
| ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **StartNode**                         | Immediately advances to the next node. The entry point for the dialogue.                                                                                                                                                           |
| **SimpleDialogueNode / DialogueNode** | Displays text and speaker in the UI. Optionally focuses the camera on the speaker's assigned virtual camera. Waits for `ContinueDialogue()`.                                                                                       |
| **ChoiceDialogueNode**                | Displays text and a list of choices. Waits for `MakeChoice(int)`. Routes to the connected node based on port name matching (`"Choice 1"`, `"Choice 2"`, etc.).                                                                     |
| **EndNode**                           | Calls `EndDialogue()`.                                                                                                                                                                                                             |
| **TrueFalseNode**                     | Evaluates a Malbers Conditions2 block against a target (node-specified → Player → DialogueManager as fallback). Routes via the `"True"` or `"False"` output port.                                                                  |
| **RandomNode**                        | Picks a random outgoing edge uniformly and jumps to that node.                                                                                                                                                                     |
| **QuestNode**                         | Calls `QuestManager.StartQuest`, `CompleteQuest`, `FailQuest`, or `AbandonQuest` for the assigned quest. Advances automatically.                                                                                                   |
| **MalbersEventNode**                  | Invokes the assigned `MEvent` ScriptableObject. Advances automatically.                                                                                                                                                            |
| **MalbersCharacterActionNode**        | Activates a **State** or **Mode** on an `MAnimal` component (resolved via `TransformVar`). If **Wait For Action** is enabled, a coroutine polls `ActiveStateID` or `ActiveMode` every frame until the action exits, then advances. |
| **Reactions2Node**                    | Applies an `MReactions2Var` to a target transform. Advances automatically (fire-and-forget).                                                                                                                                       |
| **WaitNode**                          | Hides the Dialogue UI, waits for `waitDuration` seconds via coroutine, then restores and advances.                                                                                                                                 |
| **AudioNode**                         | Plays, Stops, or Pauses an `AudioClip` on a target object's `AudioSource` (or one added dynamically). Advances automatically.                                                                                                      |
| **CinemachineDialogueNode**           | Executes a camera action (see below). Optionally plays a Unity Timeline. May wait for camera completion or timeline completion before advancing.                                                                                   |
| **ModalWindowNode**                   | Hides the Dialogue UI, instantiates a UI prefab, waits for `ContinueDialogue()`, fades out and destroys the prefab (0.3s fade), then advances.                                                                                     |

***

#### Camera System

The `CinemachineDialogueNode` supports six **Camera Actions**:

| Action              | Behaviour                                                                                                                                                                                                                                              |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **SwitchCamera**    | Boosts the target virtual camera's priority to `100`.                                                                                                                                                                                                  |
| **FocusOnSpeaker**  | Boosts the target `DialogueSpeaker`'s assigned virtual camera to priority `100`.                                                                                                                                                                       |
| **BlendToTarget**   | Directs the camera toward a custom `TransformVar` target.                                                                                                                                                                                              |
| **FocusOnTarget**   | Modifies the main gameplay camera's `Follow` and `LookAt` targets to point at a `TransformVar`. Stores original targets and restores them afterwards. Supports blend-wait, pause-at-target, and return-to-previous-camera — all configurable per node. |
| **ResetToGameplay** | Immediately resets all speaker cameras and restores the pre-dialogue camera.                                                                                                                                                                           |
| **PlayTimeline**    | Signals that a Timeline should play (handled by the Timeline pipeline).                                                                                                                                                                                |

All virtual cameras activated during a dialogue conversation are tracked in `activatedSpeakerCameras` and reset to priority `1` on `EndDialogue()`.

If **Wait For Camera Completion** is enabled on a node, the `CameraCompletionHandlerFactory` creates a type-specific handler (e.g. dolly cart, orbit) that is polled each frame until the camera reports it has finished its movement. An optional timeout prevents indefinite blocking.

***

#### Timeline Support

When a `CinemachineDialogueNode` has both a `TimelineAsset` and `PlayableDirector` assigned:

1. The `PlayableDirector` is assigned the asset and `Play()` is called.
2. `OnTimelineStarted` fires.
3. If **Wait For Timeline Completion** is enabled, a coroutine polls `PlayableDirector.state` until playback ends.
4. `OnTimelineCompleted` fires on natural completion.

**Skip**: If `allowTimelineSkip` is enabled on the node, calling `ContinueDialogue()` during playback will jump the director to its final frame, evaluate it, stop, and fire `OnTimelineSkipped` before advancing.

***

#### Dialogue Variables

`DialogueManager` maintains three persistent runtime dictionaries across conversations:

| Type     | Storage            | Accessed Via              |
| -------- | ------------------ | ------------------------- |
| `bool`   | `dialogueFlags`    | `SetFlag` / `GetFlag`     |
| `string` | `dialogueStrings`  | `SetString` / `GetString` |
| `int`    | `dialogueIntegers` | `SetInt` / `GetInt`       |

These variables are used by the custom Malbers Conditions2 conditions (`C2_DialogueFlag`, `C2_DialogueInt`, `C2_DialogueString`) to drive `TrueFalseNode` branching decisions.

The save system reads these via `GetAllFlags()`, `GetAllStrings()`, and `GetAllIntegers()`, and restores them on load via `RestoreVariables()`.

***

### Public API

#### Dialogue Control

```csharp
// Start a dialogue graph
DialogueManager.Instance.StartDialogue(dialogueContainerSO);

// Called by the UI "Next" button
DialogueManager.Instance.ContinueDialogue();

// Called by choice button at index i
DialogueManager.Instance.MakeChoice(int choiceIndex);

// End the current dialogue early
DialogueManager.Instance.EndDialogue();

// State queries
bool active = DialogueManager.Instance.IsDialogueActive();
bool playing = DialogueManager.Instance.IsTimelinePlaying();
```

#### Dialogue Variables

```csharp
// Flags (bool)
DialogueManager.Instance.SetFlag("MetKing", true);
bool met = DialogueManager.Instance.GetFlag("MetKing", false);

// Strings
DialogueManager.Instance.SetString("PlayerName", "Aria");
string name = DialogueManager.Instance.GetString("PlayerName", "Hero");

// Integers
DialogueManager.Instance.SetInt("TalkCount", 3);
int count = DialogueManager.Instance.GetInt("TalkCount", 0);

// Clear all
DialogueManager.Instance.ClearAllVariables();
```

#### Save / Load Integration

```csharp
// Save
var flags    = DialogueManager.Instance.GetAllFlags();
var strings  = DialogueManager.Instance.GetAllStrings();
var integers = DialogueManager.Instance.GetAllIntegers();

// Load
DialogueManager.Instance.RestoreVariables(flags, strings, integers);
```

***

### Notes

* **One instance only.** `DialogueManager` is a DontDestroyOnLoad singleton. Duplicate instances are destroyed on `Awake`. Place it in your first bootstrapping scene.
* **DialogueUI is required.** If `dialogueUI` is null at Start, an error is logged and dialogue calls will silently fail.
* **Player locking.** The player's movement is locked at the start of any dialogue and unlocked at the end. The player object must implement `ILockCharacter` (Malbers Animal Controller). If no `ILockCharacter` is found, locking is silently skipped.
* **Cinemachine Brain.** If not assigned in the inspector, the manager will attempt `FindFirstObjectByType<CinemachineBrain>()` at Start. For scenes that have a brain but load it dynamically, assign it manually.
* **Choice routing uses port names, not list order.** Choice edges are matched by the port name `"Choice 1"`, `"Choice 2"`, etc. — not by their serialization index. This makes re-ordering choices in the graph editor safe.
* **TrueFalse routing uses port names.** Edges are matched by `"True"` and `"False"` port names, ensuring correct routing regardless of edge serialization order.
* **Unconnected non-terminal nodes.** If a non-End node has no outgoing connections, a warning is logged and `EndDialogue()` is called automatically.
* **Dialogue variables persist.** Variable dictionaries (flags, strings, integers) are not cleared between separate dialogue conversations — only by `ClearAllVariables()` or a load operation. Design around this if using them for per-conversation state.
