Files
A-Fairytale-Gone-Bad-Briar-…/Assets/Scripts/UI/Menus/PauseMenuWindow.cs

468 lines
15 KiB
C#

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;
private void TryRegisterRaycaster()
{
if (_raycasterRegistered)
return;
if (_interactManager == null || _graphicRaycaster == null)
return;
_interactManager.AddUIRaycaster(_graphicRaycaster);
_interactManager.SetExclusiveRaycaster(_graphicRaycaster);
_raycasterRegistered = true;
}
private void TryUnregisterRaycaster()
{
if (!_raycasterRegistered)
return;
if (_interactManager == null || _graphicRaycaster == null)
return;
_interactManager.RemoveUIRaycaster(_graphicRaycaster);
_interactManager.ClearExclusiveRaycaster();
_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);
TryRegisterRaycaster();
_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();
_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<float>
{
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();
}
}
}