Code/ChitChat/Components/DialogueSystemComponent.cs
using Sandbox;
using System;
using System.Collections.Generic;
namespace ChitChat;
[Title("Dialogue System")][Category("ChitChat")][Icon("forum")]
public sealed class DialogueSystemComponent : Component
{
[Header("Input")]
[Property][InputAction] public List<string> Inputs { get; set; } = ["attack1", "jump"];
[Header("Data")]
[Property][ResourceType(".chit")] public DialogueData DialogueData { get; set; }
[Header("Components")]
[Property] public DialoguePanelComponent UIComponent { get; set; }
[Property][Description("Will automatically be added if not set.")][RequireComponent]
public DialogueWriterComponent Writer { get; set; }
[Property][Description("Will automatically be added if not set.")][RequireComponent]
public DialogueAudioComponent Audio { get; set; }
[Header("Settings")]
[Property] private bool PlayOnStart { get; set; } = true;
[Property][Description("How long it takes before another input event is registered.")]
private float InputDelay { get; set; } = 0.3f;
[Header("Mouse")]
[Property][Description("If the cursor should be visable during dialogue.")]
private bool MouseVisableInDialogue { get; set; } = true;
[Property][Description("If the cursor should be visable after the dialogue has ended.")]
private bool MouseVisableAfterDialogueEnded { get; set; } = false;
public event Action<DialogueData> OnDialogueStarted;
public event Action<DialogueData> OnDialogueEnded;
private DialogueActionBase _currentDialogueData;
private DialogueInputState _currentInputState;
private bool _isRunning = false;
private int _currentDialogueIndex = -1;
private float _inputTimer = 0.0f;
private float _delayTimer = 0.0f;
private float _inputDelay = 0.0f;
protected override void OnStart()
{
base.OnStart();
if (!UIComponent.IsValid())
{
Log.Error("No " + nameof(DialoguePanelComponent) + " on Dialogue System!");
return;
}
if (!Writer.IsValid())
{
Writer = AddComponent<DialogueWriterComponent>();
}
if (!Audio.IsValid())
{
Audio = AddComponent<DialogueAudioComponent>();
}
Writer.SetUIComponent(UIComponent);
Writer.onCharacterAdded += Audio.OnCharacterAdded;
Writer.onWritingDone += Audio.StopSounds;
if (PlayOnStart)
StartDialogue();
}
protected override void OnDestroy()
{
Writer.onCharacterAdded -= Audio.OnCharacterAdded;
Writer.onWritingDone -= Audio.StopSounds;
}
public void StartDialogue(DialogueData data)
{
DialogueData = data;
StartDialogue();
}
public void StartDialogue()
{
if (DialogueData == null)
{
Log.Error("No Dialogue data on Dialogue System!");
return;
}
UIComponent.Activate();
if(UIComponent.DialoguePanel == null) return;
UIComponent.DialoguePanel.onChoiceClicked = OnChoiceSelected;
if(MouseVisableInDialogue)
Mouse.Visibility = MouseVisibility.Visible;
else
Mouse.Visibility = MouseVisibility.Hidden;
ResetDialogue();
NextDialogue();
_isRunning = true;
OnDialogueStarted?.Invoke(DialogueData);
}
/// <summary>
/// Continues the dialogue if there are more entries in the DialogueData.
/// </summary>
/// <param name="nextDialogue">If it should start after the last dialogue played.</param>
public void ContinueDialogue(bool nextDialogue = true)
{
if(nextDialogue)
_currentDialogueIndex++;
StartDialogue();
}
public void StopDialogue()
{
UIComponent.Deactivate();
Writer.StopWriting();
Audio.StopSounds();
if(MouseVisableAfterDialogueEnded)
Mouse.Visibility = MouseVisibility.Visible;
else
Mouse.Visibility = MouseVisibility.Hidden;
OnDialogueEnded?.Invoke(DialogueData);
}
public void ResetDialogue()
{
_currentDialogueIndex = -1;
_delayTimer = 0;
_inputTimer = 0;
_inputDelay = 0;
_currentInputState = DialogueInputState.None;
}
public void SetInputState(DialogueInputState state, float delay)
{
_inputDelay = delay;
_currentInputState = state;
}
public void SetChoices(List<Choice> choices)
{
UIComponent.SetChoices(choices);
}
public void OnEventAction(string eventName)
{
IEventActionListener.Post(x => x.OnEventAction(eventName));
}
protected override void OnUpdate()
{
if (_inputTimer < InputDelay)
_inputTimer += Time.Delta;
if (!_isRunning || _currentInputState == DialogueInputState.None)
return;
if (_inputTimer >= InputDelay)
{
if(_currentInputState == DialogueInputState.WaitForInput)
{
if (OnInput())
{
if (!Writer.IsWriting)
NextDialogue();
else
{
Writer.CompleteWriting();
_inputTimer = 0;
}
}
}
else if(_currentInputState == DialogueInputState.WaitForSeconds)
{
if(_delayTimer >= _inputDelay)
{
NextDialogue();
}
else
_delayTimer += Time.Delta;
}
else if(_currentInputState == DialogueInputState.WaitForWriteEffect && !Writer.IsWriting)
{
if (OnInput())
{
NextDialogue();
}
}
}
}
private void NextDialogue()
{
_currentDialogueIndex++;
_delayTimer = 0;
_inputTimer = 0;
_inputDelay = 0;
_currentInputState = DialogueInputState.None;
Audio.StopSounds();
if (_currentDialogueIndex < DialogueData.DialogueDatas.Count)
{
if (_currentDialogueData != null)
_currentDialogueData.OnExit();
_currentDialogueData = DialogueData.DialogueDatas[_currentDialogueIndex];
while (!_currentDialogueData.OnEnter(this, UIComponent, Writer, Audio))
{
_currentDialogueIndex++;
if (_currentDialogueIndex < DialogueData.DialogueDatas.Count)
{
_currentDialogueData = DialogueData.DialogueDatas[_currentDialogueIndex];
}
else
{
StopDialogue();
break;
}
}
}
else
{
StopDialogue();
}
}
private void OnChoiceSelected(DialogueData data)
{
if (_inputTimer >= InputDelay)
{
if (data.IsValid())
StartDialogue(data);
else
NextDialogue();
}
}
private bool OnInput()
{
foreach (string action in Inputs)
{
if(Input.Released(action))
return true;
}
return false;
}
}