132 lines
3.4 KiB
C#
132 lines
3.4 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
public class DialogueManager : MonoBehaviour
|
|
{
|
|
public static DialogueManager Instance { get; private set; }
|
|
|
|
[Header("Hook up the UI component here")]
|
|
public DialogueUI ui;
|
|
|
|
[Header("Flags saved during play (simple global state)")]
|
|
public HashSet<string> flags = new();
|
|
|
|
DialogueAsset _active;
|
|
DialogueAsset.Node _node;
|
|
int _lineIndex;
|
|
|
|
bool _isTyping;
|
|
Coroutine _typeRoutine;
|
|
|
|
void Awake()
|
|
{
|
|
if (Instance != null && Instance != this) { Destroy(gameObject); return; }
|
|
Instance = this;
|
|
DontDestroyOnLoad(gameObject);
|
|
}
|
|
|
|
// Public API -------------
|
|
public bool IsRunning => _active != null;
|
|
|
|
public void StartDialogue(DialogueAsset asset)
|
|
{
|
|
if (asset == null) return;
|
|
_active = asset;
|
|
GoToNode(asset.startNodeId);
|
|
ui.Show(true);
|
|
}
|
|
|
|
public void StopDialogue()
|
|
{
|
|
_active = null;
|
|
_node = null;
|
|
ui.Show(false);
|
|
}
|
|
|
|
public void Next() // Called by UI "Continue" button / input
|
|
{
|
|
if (!IsRunning || _node == null) return;
|
|
|
|
if (_isTyping)
|
|
{
|
|
ui.CompleteTypeThisFrame();
|
|
_isTyping = false;
|
|
return;
|
|
}
|
|
|
|
// Advance line within node
|
|
_lineIndex++;
|
|
if (_lineIndex < _node.lines.Count)
|
|
{
|
|
StartTyping(_node.lines[_lineIndex]);
|
|
}
|
|
else
|
|
{
|
|
// Lines finished -> show choices or auto-next / end
|
|
var visibleChoices = ui.BuildVisibleChoices(_node.choices, flags);
|
|
if (visibleChoices.Count > 0)
|
|
{
|
|
ui.ShowChoices(visibleChoices, OnChoicePicked);
|
|
}
|
|
else
|
|
{
|
|
if (_node.nextNodeId >= 0) GoToNode(_node.nextNodeId);
|
|
else StopDialogue();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Internal -------------
|
|
void GoToNode(int id)
|
|
{
|
|
if (!_active.TryGetNode(id, out _node))
|
|
{
|
|
Debug.LogWarning($"Dialogue node {id} not found.");
|
|
StopDialogue();
|
|
return;
|
|
}
|
|
|
|
_lineIndex = 0;
|
|
_node.onEnter?.Invoke();
|
|
|
|
ui.BindSpeaker(_node.speakerName, _node.speakerPortrait);
|
|
|
|
if (_node.lines.Count == 0)
|
|
{
|
|
// Empty node: immediately resolve choices/next
|
|
var visibleChoices = ui.BuildVisibleChoices(_node.choices, flags);
|
|
if (visibleChoices.Count > 0) ui.ShowChoices(visibleChoices, OnChoicePicked);
|
|
else if (_node.nextNodeId >= 0) GoToNode(_node.nextNodeId);
|
|
else StopDialogue();
|
|
}
|
|
else
|
|
{
|
|
StartTyping(_node.lines[_lineIndex]);
|
|
}
|
|
}
|
|
|
|
void StartTyping(string text)
|
|
{
|
|
ui.HideChoices();
|
|
if (_typeRoutine != null) StopCoroutine(_typeRoutine);
|
|
_typeRoutine = StartCoroutine(TypeRoutine(text));
|
|
}
|
|
|
|
IEnumerator TypeRoutine(string text)
|
|
{
|
|
_isTyping = true;
|
|
yield return ui.TypeText(text);
|
|
_isTyping = false;
|
|
}
|
|
|
|
void OnChoicePicked(DialogueAsset.Choice choice)
|
|
{
|
|
choice?.onChoose?.Invoke();
|
|
if (!string.IsNullOrEmpty(choice.setFlag)) flags.Add(choice.setFlag);
|
|
|
|
if (choice == null || choice.nextNodeId < 0) { StopDialogue(); return; }
|
|
GoToNode(choice.nextNodeId);
|
|
}
|
|
}
|