using System; using System.Threading; using BriarQueen.Framework.Coordinators.Events; using BriarQueen.Framework.Events.UI; using BriarQueen.Framework.Services.Subtitles; using Cysharp.Threading.Tasks; using PrimeTween; using UnityEngine; using VContainer; namespace BriarQueen.UI.HUD { public class SubtitleUI : MonoBehaviour { [Header("UI")] [SerializeField] private InteractTextUI _interactTextUI; [SerializeField] private CanvasGroup _canvasGroup; [Header("Tweens")] [SerializeField] private TweenSettings _tweenSettings = new() { duration = 0.2f, ease = Ease.InOutSine, useUnscaledTime = true }; private EventCoordinator _eventCoordinator; private SubtitleService _subtitleService; private CancellationTokenSource _cancellationTokenSource; private Sequence _sequence; [Inject] public void Construct(EventCoordinator eventCoordinator, SubtitleService subtitleService) { _eventCoordinator = eventCoordinator; _subtitleService = subtitleService; } private void Awake() { ApplyImmediate(string.Empty, false); } private void OnEnable() { _eventCoordinator?.Subscribe(OnSubtitleDisplayChanged); SyncFromService(); } private void OnDisable() { _eventCoordinator?.Unsubscribe(OnSubtitleDisplayChanged); StopTween(); } private void OnDestroy() { StopTween(); } private void SyncFromService() { if (_subtitleService != null && _subtitleService.IsVisible) { ApplyImmediate(_subtitleService.CurrentText, true); return; } ApplyImmediate(string.Empty, false); } private void OnSubtitleDisplayChanged(SubtitleDisplayChangedEvent eventData) { if (!eventData.Visible || string.IsNullOrWhiteSpace(eventData.Text)) { HideSubtitle().Forget(); return; } ShowSubtitle(eventData.Text).Forget(); } private async UniTaskVoid ShowSubtitle(string text) { if (_canvasGroup == null || _interactTextUI == null) return; StopTween(); _interactTextUI.SetText(text); _canvasGroup.blocksRaycasts = false; _canvasGroup.interactable = false; _cancellationTokenSource = new CancellationTokenSource(); var tween = new TweenSettings { startValue = _canvasGroup.alpha, endValue = 1f, settings = _tweenSettings }; _sequence = Sequence.Create(useUnscaledTime: true) .Group(Tween.Alpha(_canvasGroup, tween)); try { await _sequence.ToUniTask(cancellationToken: _cancellationTokenSource.Token); } catch (OperationCanceledException) { return; } finally { DisposeTweenState(); } _canvasGroup.alpha = 1f; } private async UniTaskVoid HideSubtitle() { if (_canvasGroup == null || _interactTextUI == null) return; StopTween(); _cancellationTokenSource = new CancellationTokenSource(); var tween = new TweenSettings { startValue = _canvasGroup.alpha, endValue = 0f, settings = _tweenSettings }; _sequence = Sequence.Create(useUnscaledTime: true) .Group(Tween.Alpha(_canvasGroup, tween)); try { await _sequence.ToUniTask(cancellationToken: _cancellationTokenSource.Token); } catch (OperationCanceledException) { return; } finally { DisposeTweenState(); } ApplyImmediate(string.Empty, false); } private void ApplyImmediate(string text, bool visible) { if (_interactTextUI != null) { if (visible) _interactTextUI.SetText(text); else _interactTextUI.ClearText(); } if (_canvasGroup == null) return; _canvasGroup.alpha = visible ? 1f : 0f; _canvasGroup.blocksRaycasts = false; _canvasGroup.interactable = false; } private void StopTween() { if (_sequence.isAlive) _sequence.Stop(); _sequence = default; if (_cancellationTokenSource == null) return; try { _cancellationTokenSource.Cancel(); } catch { } _cancellationTokenSource.Dispose(); _cancellationTokenSource = null; } private void DisposeTweenState() { _sequence = default; if (_cancellationTokenSource == null) return; _cancellationTokenSource.Dispose(); _cancellationTokenSource = null; } } }