FantasyAsset/Assets/Scripts/Dialogues/DialogueManager.cs

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);
}
}