using System; using System.Threading; using BriarQueen.Framework.Coordinators.Events; using BriarQueen.Framework.Events.UI; using BriarQueen.Framework.Managers.Audio; using BriarQueen.Framework.Managers.Interaction; using BriarQueen.Framework.Managers.IO; using BriarQueen.Framework.Managers.Levels; using BriarQueen.Framework.Managers.UI.Base; using BriarQueen.Framework.Managers.UI.Events; using BriarQueen.Framework.Services.Game; using BriarQueen.UI.Menus.Components; using Cysharp.Threading.Tasks; using PrimeTween; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; using VContainer; namespace BriarQueen.UI.Menus { public class PauseMenuWindow : MonoBehaviour, IUIWindow, IUIOverlayHost { [Header("Root UI")] [SerializeField] private CanvasGroup _canvasGroup; [SerializeField] private RectTransform _windowRect; [Header("Background")] [SerializeField] private CanvasGroup _backgroundGroup; [Header("Transient Info")] [SerializeField] private TextMeshProUGUI _levelName; [Header("Buttons")] [SerializeField] private UnderlineButton _resumeButton; [SerializeField] private UnderlineButton _saveButton; [SerializeField] private UnderlineButton _settingsButton; [SerializeField] private UnderlineButton _exitButton; [SerializeField] private UnderlineButton _quitToDesktopButton; [Header("Layout")] [SerializeField] private CanvasGroup _buttonsGroup; [Header("Selection")] [SerializeField] private Selectable _firstSelectedOnOpen; [Header("Tween Settings")] [SerializeField] private TweenSettings _backgroundFadeSettings = new() { duration = 0.4f, ease = Ease.OutQuad, useUnscaledTime = true }; [SerializeField] private TweenSettings _buttonFadeSettings = new() { duration = 0.25f, ease = Ease.OutQuad, useUnscaledTime = true }; [Header("Scale")] [SerializeField] private float _hiddenScale = 0.85f; [Header("Internal")] [SerializeField] private GraphicRaycaster _graphicRaycaster; private AudioManager _audioManager; private EventCoordinator _eventCoordinator; private GameService _gameService; private InteractManager _interactManager; private SaveManager _saveManager; private LevelManager _levelManager; private CancellationTokenSource _cts; private Sequence _sequence; private bool _raycasterRegistered; public bool IsModal => true; public WindowType WindowType => WindowType.PauseMenuWindow; public UIPauseBehavior PauseBehavior => UIPauseBehavior.TreatAsBackRequest; public bool CanSuspendFor(WindowType incomingWindowType) { return incomingWindowType == WindowType.SettingsWindow; } private void EnsureExclusiveRaycaster() { if (_interactManager == null || _graphicRaycaster == null) return; if (!_raycasterRegistered) { _interactManager.AddUIRaycaster(_graphicRaycaster); _raycasterRegistered = true; } _interactManager.SetExclusiveRaycaster(_graphicRaycaster); } private void TryUnregisterRaycaster() { if (!_raycasterRegistered) return; if (_interactManager == null || _graphicRaycaster == null) return; _interactManager.RemoveUIRaycaster(_graphicRaycaster); _interactManager.ReleaseExclusiveRaycaster(_graphicRaycaster); _raycasterRegistered = false; } // ── Unity lifecycle ─────────────────────────────────────────── private void Awake() { if (_canvasGroup != null) { _canvasGroup.alpha = 0f; _canvasGroup.blocksRaycasts = false; _canvasGroup.interactable = false; } if (_windowRect != null) _windowRect.localScale = Vector3.one; SetGroupImmediate(_backgroundGroup, 0f); HideButtons(); } private void OnEnable() { BindButtons(); } private void OnDisable() { UnbindButtons(); } private void OnDestroy() { StopAndResetCancellation(); TryUnregisterRaycaster(); } // ── IUIWindow ───────────────────────────────────────────────── public async UniTask Show() { StopAndResetCancellation(); SetLevelName(); gameObject.SetActive(true); EnsureExclusiveRaycaster(); _canvasGroup.blocksRaycasts = false; _canvasGroup.interactable = false; SetGroupImmediate(_backgroundGroup, 0f); HideButtons(); try { var fadeWindowTask = FadeGroup(_canvasGroup, 1f, _backgroundFadeSettings, _cts.Token); var fadeBackgroundTask = FadeGroup(_backgroundGroup, 1f, _backgroundFadeSettings, _cts.Token); await UniTask.WhenAll(fadeWindowTask, fadeBackgroundTask); _backgroundGroup.blocksRaycasts = true; _backgroundGroup.interactable = true; _buttonsGroup.blocksRaycasts = true; _buttonsGroup.interactable = true; await FadeGroup(_buttonsGroup, 1f, _buttonFadeSettings, _cts.Token); } catch (OperationCanceledException) { return; } _canvasGroup.alpha = 1f; _canvasGroup.blocksRaycasts = true; _canvasGroup.interactable = true; } public async UniTask Hide() { StopAndResetCancellation(); _canvasGroup.blocksRaycasts = true; _canvasGroup.interactable = false; try { await FadeGroup(_buttonsGroup, 0f, _buttonFadeSettings, _cts.Token); await FadeGroup(_backgroundGroup, 0f, _backgroundFadeSettings, _cts.Token); } catch (OperationCanceledException) { return; } _resumeButton.SetSelectedState(false); _saveButton.SetSelectedState(false); _settingsButton.SetSelectedState(false); _exitButton.SetSelectedState(false); _quitToDesktopButton.SetSelectedState(false); _canvasGroup.alpha = 0f; _canvasGroup.blocksRaycasts = false; _canvasGroup.interactable = false; TryUnregisterRaycaster(); gameObject.SetActive(false); } public async UniTask SuspendForOverlay() { StopAndResetCancellation(); _buttonsGroup.blocksRaycasts = false; _buttonsGroup.interactable = false; try { await FadeGroup(_buttonsGroup, 0f, _buttonFadeSettings, _cts.Token); } catch (OperationCanceledException) { return; } } public async UniTask ResumeFromOverlay() { StopAndResetCancellation(); EnsureExclusiveRaycaster(); _buttonsGroup.blocksRaycasts = true; _buttonsGroup.interactable = true; try { await FadeGroup(_buttonsGroup, 1f, _buttonFadeSettings, _cts.Token); } catch (OperationCanceledException) { return; } SelectDefault(); } // ── DI ──────────────────────────────────────────────────────── [Inject] public void Construct( EventCoordinator eventCoordinator, SaveManager saveManager, GameService gameService, InteractManager interactManager, AudioManager audioManager, LevelManager levelManager) { _eventCoordinator = eventCoordinator; _saveManager = saveManager; _gameService = gameService; _interactManager = interactManager; _audioManager = audioManager; _levelManager = levelManager; } // ── Level ───────────────────────────────────────────────────── private void SetLevelName() { if (_levelManager == null || _levelName == null) return; var currentLevel = _levelManager.CurrentLevel; if (!string.IsNullOrEmpty(currentLevel.LevelName)) _levelName.text = currentLevel.LevelName; } // ── Button binding ──────────────────────────────────────────── private void BindButtons() { if (_resumeButton != null) _resumeButton.SelectionRequested += OnResumeClicked; if (_saveButton != null) _saveButton.SelectionRequested += OnSaveClicked; if (_settingsButton != null) _settingsButton.SelectionRequested += OnSettingsClicked; if (_exitButton != null) _exitButton.SelectionRequested += OnExitClicked; if (_quitToDesktopButton != null) _quitToDesktopButton.SelectionRequested += OnQuitToDesktopClicked; } private void UnbindButtons() { if (_resumeButton != null) _resumeButton.SelectionRequested -= OnResumeClicked; if (_saveButton != null) _saveButton.SelectionRequested -= OnSaveClicked; if (_settingsButton != null) _settingsButton.SelectionRequested -= OnSettingsClicked; if (_exitButton != null) _exitButton.SelectionRequested -= OnExitClicked; if (_quitToDesktopButton != null) _quitToDesktopButton.SelectionRequested -= OnQuitToDesktopClicked; } // ── Button callbacks ────────────────────────────────────────── private void OnResumeClicked(UnderlineButton _) { _eventCoordinator?.Publish(new PauseButtonClickedEvent()); } private void OnSaveClicked(UnderlineButton _) { SaveGame().Forget(); } private void OnSettingsClicked(UnderlineButton _) { _eventCoordinator?.Publish(new UIToggleSettingsWindow(true, SettingsOpenSource.PauseMenu)); } private void OnExitClicked(UnderlineButton _) { _eventCoordinator?.Publish(new FadeEvent(false, 1f)); ExitInternal().Forget(); } private void OnQuitToDesktopClicked(UnderlineButton _) { QuitInternal().Forget(); } // ── Internal async ──────────────────────────────────────────── private async UniTask ExitInternal() { await UniTask.Delay(TimeSpan.FromSeconds(1)); _audioManager?.StopAllAudio(); await SaveGame(); _eventCoordinator?.Publish(new PauseButtonClickedEvent()); await _gameService.LoadMainMenu(); } private async UniTask QuitInternal() { _eventCoordinator?.Publish(new FadeEvent(false, 1f)); await UniTask.Delay(TimeSpan.FromSeconds(1)); await SaveGame(); _gameService?.QuitGame(); } private async UniTask SaveGame() { if (_saveManager == null) return; await _saveManager.SaveGameDataLatest(); } // ── Layout helpers ──────────────────────────────────────────── private void HideButtons() { SetGroupImmediate(_buttonsGroup, 0f); } private static void SetGroupImmediate(CanvasGroup group, float alpha) { if (group == null) return; group.alpha = alpha; group.interactable = false; group.blocksRaycasts = false; } private async UniTask FadeGroup( CanvasGroup group, float target, TweenSettings settings, CancellationToken token) { if (group == null) return; await Tween.Alpha(group, new TweenSettings { startValue = group.alpha, endValue = target, settings = settings }).ToUniTask(cancellationToken: token); group.alpha = target; } // ── Selection ───────────────────────────────────────────────── private void SelectDefault() { if (EventSystem.current == null) return; EventSystem.current.SetSelectedGameObject(null); if (_firstSelectedOnOpen != null) { EventSystem.current.SetSelectedGameObject( _firstSelectedOnOpen.gameObject); } else if (_resumeButton != null) { EventSystem.current.SetSelectedGameObject( _resumeButton.gameObject); } Debug.Log( $"[PauseMenuWindow] Selected: {EventSystem.current.currentSelectedGameObject}"); } // ── CTS ─────────────────────────────────────────────────────── private void StopAndResetCancellation() { if (_sequence.isAlive) _sequence.Stop(); if (_cts != null) { try { _cts.Cancel(); } catch { } _cts.Dispose(); } _cts = new CancellationTokenSource(); } } }