Code/ChitChat/Components/DialogueWriterComponent.cs
using Sandbox;
using System;
using System.Text;

namespace ChitChat;

[Title("Dialogue Writer")][Category("ChitChat")][Icon("history_edu")]
public sealed class DialogueWriterComponent : Component
{
	[Property] public float PunctuationPauseDelay { get; set; } = 0.25f;

	public Action onCharacterAdded;
	public Action onWritingDone;

	public bool IsWriting { get; private set; } = false;
	
	private StringBuilder _dialogueBuilder = new();
	private int _currentDialogueTextCharacterIndex = 0;
	private float _writingTimer = 0.0f;
	private float _currentWriteSpeed = 0.0f;
	
	private float _defaultWritingSpeed = 0.0f;

	private string m_Text;
	private DialoguePanelComponent _uiComponent;

	public void SetUIComponent(DialoguePanelComponent ui) => _uiComponent = ui;

	public void StartWriting(string text, float writingSpeed)
	{
		_writingTimer = 0;
		_currentDialogueTextCharacterIndex = 0;
		_dialogueBuilder.Clear();

		m_Text = text;
		_defaultWritingSpeed = writingSpeed;
		_currentWriteSpeed = writingSpeed;

		IsWriting = true;
	}

	public void StopWriting()
	{
		IsWriting = false;
	}

	public void CompleteWriting()
	{
		_uiComponent.SetText(m_Text);
		IsWriting = false;
	}

	protected override void OnUpdate()
	{
		if(!IsWriting)
			return;

		if (_writingTimer >= _currentWriteSpeed)
		{
			if (_currentDialogueTextCharacterIndex < m_Text.Length)
			{
				char dialogueChar = m_Text[_currentDialogueTextCharacterIndex];
				_dialogueBuilder.Append(dialogueChar);
				_currentDialogueTextCharacterIndex++;

				//If the current character is a punctuation then change to punctuation writing delay
				if (char.IsPunctuation(dialogueChar) && dialogueChar == '.')
				{
					_currentWriteSpeed = PunctuationPauseDelay;
				}
				else
					_currentWriteSpeed = _defaultWritingSpeed;

				//Fire event if the current character isn't white space or punctuation
				if (!char.IsWhiteSpace(dialogueChar) && !char.IsPunctuation(dialogueChar))
				{
					onCharacterAdded?.Invoke();
				}

				_uiComponent.SetText(_dialogueBuilder.ToString());
			}
			else //Writing effect is finished
			{
				IsWriting = false;
				onWritingDone?.Invoke();
				return;
			}

			_writingTimer = 0;
		}
		else
		{
			_writingTimer += Time.Delta;
		}
	}
}