Code/ChitChat/Components/DialogueAudioComponent.cs
using Sandbox;

namespace ChitChat;

[Title("Dialogue Audio")][Category("ChitChat")][Icon("volume_up")]
public sealed class DialogueAudioComponent : Component
{
	[Property][RequireComponent] private SoundPointComponent SoundComponent { get; set; }

	private float _audioTimer = 0.0f;
	private float _audioTime = 0.0f;

	private bool _useDelay;
	private DialogueAudioType _audioType;
	private RangedFloat _audioDelay;

	protected override void OnValidate()
	{
		if (!SoundComponent.IsValid())
		{
			Log.Error(nameof(SoundPointComponent) + " not set on " + nameof(DialogueAudioComponent) + '!');
		}
		else
		{
			SoundComponent.StopOnNew = false;
			SoundComponent.PlayOnStart = false;
		}
	}

	/// <summary>When ever a character is added to dialogue text through the writing effect.</summary>
	public void OnCharacterAdded()
	{
		if (_audioType == DialogueAudioType.ForEachCharacter)
		{
			SoundComponent.StartSound();
		}
	}

	public void StopSounds()
	{
		_audioType = DialogueAudioType.None;
		SoundComponent.StopSound();
	}

	public void SetAudioSettings(DialogueAudioType type, SoundEvent sound, bool useDelay, RangedFloat audioDelay)
	{
		SoundComponent.SoundEvent = sound;
		_useDelay = useDelay;
		_audioDelay = audioDelay;
		_audioType = type;

		if (type != DialogueAudioType.ForEachCharacter && _useDelay)
		{
			_audioTimer = 0;
			_audioTime = GetAudioDelayTime();
		}
	}

	protected override void OnUpdate()
	{
		//If the type is none or for each character then we dont need to update
		if(_audioType == DialogueAudioType.None || _audioType == DialogueAudioType.ForEachCharacter) return;

		if (_useDelay)
		{
			UpdateDelayedAudio();
		}
		else
		{
			UpdateAudio();
		}
	}

	private void UpdateAudio()
	{
		if (!SoundComponent.IsPlaying())
		{
			SoundComponent.StartSound();

			//Only play audio once
			if (_audioType == DialogueAudioType.Once)
			{
				_audioType = DialogueAudioType.None;
			}
		}
	}

	private void UpdateDelayedAudio()
	{
		if (_audioTimer >= _audioTime)
		{
			SoundComponent.StartSound();

			//Only play audio once
			if (_audioType == DialogueAudioType.Once)
			{
				_audioType = DialogueAudioType.None;
			}
			else
			{
				_audioTime = GetAudioDelayTime();
			}

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

	private float GetAudioDelayTime()
	{
		return _audioDelay.GetValue();
	}
}

public static class DialogueSoundExtension
{
	public static bool IsPlaying(this BaseSoundComponent sound)
	{
		if (sound is Component.ITemporaryEffect temp)
		{
			return temp.IsActive;
		}
		return false;
	}
}