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

701 lines
22 KiB
C#

using System;
using System.Threading;
using BriarQueen.Framework.Coordinators.Events;
using BriarQueen.Framework.Effects;
using BriarQueen.Framework.Events.UI;
using BriarQueen.Framework.Managers.Input;
using BriarQueen.Framework.Managers.UI;
using BriarQueen.Framework.Managers.UI.Base;
using BriarQueen.Framework.Managers.UI.Events;
using BriarQueen.Framework.Services.Game;
using BriarQueen.Game.Effects;
using BriarQueen.UI.Menus.Components;
using Cysharp.Threading.Tasks;
using PrimeTween;
using TMPro;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.Serialization;
using UnityEngine.UI;
using VContainer;
namespace BriarQueen.UI.Menus
{
public class MainMenuWindow : MonoBehaviour, IUIOverlayHost
{
[Header("Intro Screen")]
[SerializeField]
[FormerlySerializedAs("_mainMenuIntroScreenCanvasGroup")]
private CanvasGroup _introScreenCanvas;
[SerializeField]
[FormerlySerializedAs("_introTextText")]
private TextMeshProUGUI _pressStartText;
[SerializeField]
private UIDissolveImage _introGroupDissolveGroup;
[Header("Main Menu")]
[SerializeField]
[FormerlySerializedAs("_mainMenuWindowCanvasGroup")]
private CanvasGroup _mainMenuGroup;
[Header("Display")]
[SerializeField]
private CanvasGroup _displayGroup;
[SerializeField]
private AnimatedSelectionButtonGroup _mainMenuSelectionGroup;
[SerializeField]
private AnimatedSelectionButton _startGameSelectionButton;
[SerializeField]
private AnimatedSelectionButton _settingsSelectionButton;
[SerializeField]
private AnimatedSelectionButton _quitSelectionButton;
[SerializeField]
private Button _startGameButton;
[SerializeField]
private Button _settingsButton;
[SerializeField]
private Button _quitButton;
[Header("Select Save Window")]
[SerializeField]
private SelectSaveWindow _selectSaveWindow;
[SerializeField]
private CanvasGroup _selectSaveContainerCanvasGroup;
[SerializeField]
private CanvasGroup _selectSaveWindowCanvasGroup;
[SerializeField]
private UIFogReveal _selectSaveWindowFog;
[Header("Tween Settings")]
[SerializeField]
private TweenSettings _selectSaveTweenSettings = new()
{
duration = 0.25f,
ease = Ease.OutQuad,
useUnscaledTime = true
};
[SerializeField]
private TweenSettings _pressStartFadeTweenSettings = new()
{
duration = 0.25f,
ease = Ease.OutQuad,
useUnscaledTime = true
};
[SerializeField]
private TweenSettings _settingsOverlayTweenSettings = new()
{
duration = 0.25f,
ease = Ease.OutQuad,
useUnscaledTime = true
};
[SerializeField]
private TweenSettings _pressStartPulseTweenSettings = new()
{
duration = 0.85f,
ease = Ease.InOutSine,
useUnscaledTime = true
};
[SerializeField]
[Range(0f, 1f)]
private float _pressStartPulseMinimumAlpha = 0.25f;
private CancellationTokenSource _introCts;
private CancellationTokenSource _settingsOverlayCts;
private CancellationTokenSource _selectSaveCts;
private EventCoordinator _eventCoordinator;
private GameService _gameService;
private InputManager _inputManager;
private UIManager _uiManager;
private Sequence _pressStartFadeSequence;
private Sequence _pressStartPulseSequence;
private Sequence _settingsOverlaySequence;
private Sequence _selectSaveSequence;
private bool _introFinished;
private bool _introTransitioning;
[Inject]
public void Construct(
GameService gameService,
EventCoordinator eventCoordinator,
InputManager inputManager,
UIManager uiManager)
{
_gameService = gameService;
_eventCoordinator = eventCoordinator;
_inputManager = inputManager;
_uiManager = uiManager;
}
private void Awake()
{
ApplyInitialVisualState();
if (_selectSaveWindow != null)
{
_selectSaveWindow.OnCloseWindow += CloseSelectSaveWindow;
_selectSaveWindow.gameObject.SetActive(false);
}
}
private void OnEnable()
{
BindButtons();
_uiManager?.RegisterMainMenuOverlayHost(this);
_eventCoordinator?.PublishImmediate(new UIToggleHudEvent(false));
_inputManager?.BindSubmitForStart(OnIntroSubmit);
_eventCoordinator?.Subscribe<UIBackRequestedEvent>(OnBackRequested);
ApplyInitialVisualState();
}
private void Update()
{
if (_introFinished || _introTransitioning)
{
return;
}
if (Mouse.current?.leftButton.wasPressedThisFrame == true ||
Gamepad.current?.buttonSouth.wasPressedThisFrame == true)
{
TransitionFromIntroToMainMenu().Forget();
}
}
private void OnDisable()
{
UnbindButtons();
_uiManager?.UnregisterMainMenuOverlayHost(this);
_inputManager?.ResetSubmitBind(OnIntroSubmit);
_eventCoordinator?.Unsubscribe<UIBackRequestedEvent>(OnBackRequested);
StopIntroTweens();
StopSettingsOverlayTween();
StopSelectSaveTween();
}
private void OnDestroy()
{
_uiManager?.UnregisterMainMenuOverlayHost(this);
if (_selectSaveWindow != null)
{
_selectSaveWindow.OnCloseWindow -= CloseSelectSaveWindow;
}
StopIntroTweens();
StopSettingsOverlayTween();
StopSelectSaveTween();
}
private void BindButtons()
{
var startGameButton = ResolveButton(_startGameSelectionButton, _startGameButton);
if (startGameButton != null)
{
startGameButton.onClick.AddListener(OnStartClicked);
}
var settingsButton = ResolveButton(_settingsSelectionButton, _settingsButton);
if (settingsButton != null)
{
settingsButton.onClick.AddListener(OnSettingsClicked);
}
var quitButton = ResolveButton(_quitSelectionButton, _quitButton);
if (quitButton != null)
{
quitButton.onClick.AddListener(OnQuitClicked);
}
}
private void UnbindButtons()
{
var startGameButton = ResolveButton(_startGameSelectionButton, _startGameButton);
if (startGameButton != null)
{
startGameButton.onClick.RemoveListener(OnStartClicked);
}
var settingsButton = ResolveButton(_settingsSelectionButton, _settingsButton);
if (settingsButton != null)
{
settingsButton.onClick.RemoveListener(OnSettingsClicked);
}
var quitButton = ResolveButton(_quitSelectionButton, _quitButton);
if (quitButton != null)
{
quitButton.onClick.RemoveListener(OnQuitClicked);
}
}
private void ApplyInitialVisualState()
{
_introFinished = false;
_introTransitioning = false;
SetCanvasGroupState(_introScreenCanvas, 1f, true);
if (_introScreenCanvas != null)
{
_introScreenCanvas.gameObject.SetActive(true);
}
SetCanvasGroupState(_mainMenuGroup, 1f, false);
if (_mainMenuGroup != null)
{
_mainMenuGroup.gameObject.SetActive(true);
}
SetCanvasGroupState(_selectSaveContainerCanvasGroup, 0f, false);
SetCanvasGroupState(_selectSaveWindowCanvasGroup, 0f, false);
if (_pressStartText != null)
{
var color = _pressStartText.color;
color.a = 1f;
_pressStartText.color = color;
_pressStartText.gameObject.SetActive(true);
}
StartPressStartPulse();
_introGroupDissolveGroup?.SetDissolveAmount(0f);
_selectSaveWindowFog?.FogSet(0f);
}
private void OnIntroSubmit(InputAction.CallbackContext ctx)
{
if (_introFinished || _introTransitioning)
{
return;
}
if (!ctx.performed)
{
return;
}
TransitionFromIntroToMainMenu().Forget();
}
private void OnBackRequested(UIBackRequestedEvent _)
{
if (!_introFinished || _selectSaveWindow == null || _selectSaveWindowCanvasGroup == null)
{
return;
}
if (!_selectSaveWindow.gameObject.activeInHierarchy || !_selectSaveWindowCanvasGroup.interactable)
{
return;
}
_selectSaveWindow.HandleBackRequest();
}
private async UniTaskVoid TransitionFromIntroToMainMenu()
{
if (_introFinished || _introTransitioning)
{
return;
}
_introTransitioning = true;
ResetIntroCtsAndCancelRunning();
try
{
var fadeTextTask = FadeOutPressStartText(_introCts.Token);
var dissolveTask = _introGroupDissolveGroup != null
? _introGroupDissolveGroup.DissolveOut(false).AttachExternalCancellation(_introCts.Token)
: UniTask.CompletedTask;
await UniTask.WhenAll(fadeTextTask, dissolveTask);
}
catch (OperationCanceledException)
{
_introTransitioning = false;
return;
}
finally
{
_pressStartFadeSequence = default;
}
SetCanvasGroupState(_introScreenCanvas, 0f, false);
if (_introScreenCanvas != null)
{
_introScreenCanvas.gameObject.SetActive(false);
}
if (_mainMenuGroup != null)
{
_mainMenuGroup.gameObject.SetActive(true);
}
SetCanvasGroupState(_mainMenuGroup, 1f, true);
_introFinished = true;
_introTransitioning = false;
_mainMenuSelectionGroup?.SelectIndex(0, true);
}
private async UniTask FadeOutPressStartText(CancellationToken token)
{
if (_pressStartText == null)
{
return;
}
_pressStartFadeSequence = Sequence.Create(useUnscaledTime: true)
.Group(Tween.Alpha(_pressStartText, new TweenSettings<float>
{
startValue = _pressStartText.color.a,
endValue = 0f,
settings = _pressStartFadeTweenSettings
}));
await _pressStartFadeSequence.ToUniTask(cancellationToken: token);
var color = _pressStartText.color;
color.a = 0f;
_pressStartText.color = color;
_pressStartText.gameObject.SetActive(false);
}
private void StartPressStartPulse()
{
StopSequence(ref _pressStartPulseSequence);
if (_pressStartText == null)
{
return;
}
_pressStartPulseSequence = Sequence.Create(
useUnscaledTime: true,
cycleMode: Sequence.SequenceCycleMode.Yoyo,
cycles: -1)
.Group(Tween.Alpha(_pressStartText, new TweenSettings<float>
{
startValue = _pressStartText.color.a,
endValue = _pressStartPulseMinimumAlpha,
settings = _pressStartPulseTweenSettings
}));
}
private void OnStartClicked()
{
ShowSelectSaveWindow().Forget();
}
private void OnSettingsClicked()
{
_eventCoordinator?.PublishImmediate(new UIToggleSettingsWindow(true, SettingsOpenSource.MainMenu));
}
private void OnQuitClicked()
{
_gameService?.QuitGame();
}
private async UniTask ShowSelectSaveWindow()
{
if (_selectSaveWindow == null || _selectSaveWindowCanvasGroup == null)
{
return;
}
ResetSelectSaveCtsAndCancelRunning();
_selectSaveWindow.gameObject.SetActive(true);
_selectSaveWindow.Refresh();
SetCanvasGroupState(_selectSaveContainerCanvasGroup, 0f, false);
SetCanvasGroupState(_selectSaveWindowCanvasGroup, 1f, false); // visible but not interactable yet
SetCanvasGroupInteractivity(_mainMenuGroup, false);
if (_selectSaveWindowCanvasGroup != null)
{
_selectSaveWindowFog.FogAmount = 0.2f;
_selectSaveWindowFog.MaxFog = 0.4f;
}
try
{
// Step 1: fog rolls in fully
if (_selectSaveWindowFog != null)
{
await _selectSaveWindowFog.FogIn().AttachExternalCancellation(_selectSaveCts.Token);
}
_selectSaveSequence = Sequence.Create(useUnscaledTime: true)
.Group(Tween.Alpha(_selectSaveContainerCanvasGroup, new TweenSettings<float>
{
startValue = 0f,
endValue = 1f,
settings = _selectSaveTweenSettings
}))
.Group(Tween.Alpha(_displayGroup, new TweenSettings<float>
{
startValue = _displayGroup.alpha,
endValue = 0f,
settings = _selectSaveTweenSettings
}));
await _selectSaveSequence.ToUniTask(cancellationToken: _selectSaveCts.Token);
}
catch (OperationCanceledException)
{
return;
}
finally
{
_selectSaveSequence = default;
}
// Step 3: tween complete — enable interaction
SetCanvasGroupInteractivity(_selectSaveContainerCanvasGroup, true);
SetCanvasGroupInteractivity(_selectSaveWindowCanvasGroup, true);
}
private void CloseSelectSaveWindow()
{
CloseSelectSaveWindowInternal().Forget();
}
private async UniTask CloseSelectSaveWindowInternal()
{
if (_selectSaveWindow == null || _selectSaveWindowCanvasGroup == null)
{
return;
}
ResetSelectSaveCtsAndCancelRunning();
// Disable interaction immediately, leave alpha for tween to read
SetCanvasGroupInteractivity(_selectSaveContainerCanvasGroup, false);
SetCanvasGroupInteractivity(_selectSaveWindowCanvasGroup, false);
try
{
// Step 1: container fades out
_selectSaveSequence = Sequence.Create(useUnscaledTime: true)
.Group(Tween.Alpha(_selectSaveContainerCanvasGroup, new TweenSettings<float>
{
startValue = _selectSaveContainerCanvasGroup.alpha,
endValue = 0f,
settings = _selectSaveTweenSettings
}))
.Group(Tween.Alpha(_displayGroup, new TweenSettings<float>
{
startValue = _displayGroup.alpha,
endValue = 1f,
settings = _selectSaveTweenSettings
}));
await _selectSaveSequence.ToUniTask(cancellationToken: _selectSaveCts.Token);
// Step 2: fog clears
if (_selectSaveWindowFog != null)
{
await _selectSaveWindowFog.FogOut().AttachExternalCancellation(_selectSaveCts.Token);
}
}
catch (OperationCanceledException)
{
return;
}
finally
{
_selectSaveSequence = default;
}
// Step 3: fully cleaned up
SetCanvasGroupState(_selectSaveContainerCanvasGroup, 0f, false);
SetCanvasGroupState(_selectSaveWindowCanvasGroup, 0f, false);
_selectSaveWindow.gameObject.SetActive(false);
_selectSaveWindowFog?.FogSet(0f);
SetCanvasGroupState(_mainMenuGroup, 1f, true);
}
private void ResetIntroCtsAndCancelRunning()
{
StopSequence(ref _pressStartPulseSequence);
StopSequence(ref _pressStartFadeSequence);
CancelAndDispose(ref _introCts);
_introCts = new CancellationTokenSource();
}
private void StopIntroTweens()
{
StopSequence(ref _pressStartPulseSequence);
StopSequence(ref _pressStartFadeSequence);
CancelAndDispose(ref _introCts);
}
private void ResetSelectSaveCtsAndCancelRunning()
{
StopSequence(ref _selectSaveSequence);
CancelAndDispose(ref _selectSaveCts);
_selectSaveCts = new CancellationTokenSource();
}
private void StopSelectSaveTween()
{
StopSequence(ref _selectSaveSequence);
CancelAndDispose(ref _selectSaveCts);
}
private void ResetSettingsOverlayCtsAndCancelRunning()
{
StopSequence(ref _settingsOverlaySequence);
CancelAndDispose(ref _settingsOverlayCts);
_settingsOverlayCts = new CancellationTokenSource();
}
private void StopSettingsOverlayTween()
{
StopSequence(ref _settingsOverlaySequence);
CancelAndDispose(ref _settingsOverlayCts);
}
private static void StopSequence(ref Sequence sequence)
{
if (sequence.isAlive)
{
sequence.Stop();
}
sequence = default;
}
private static void CancelAndDispose(ref CancellationTokenSource cts)
{
if (cts == null)
{
return;
}
try
{
cts.Cancel();
}
catch
{
}
cts.Dispose();
cts = null;
}
private static Button ResolveButton(AnimatedSelectionButton animatedSelectionButton, Button fallbackButton)
{
if (animatedSelectionButton != null && animatedSelectionButton.Button != null)
{
return animatedSelectionButton.Button;
}
return fallbackButton;
}
private static void SetCanvasGroupState(CanvasGroup group, float alpha, bool inputEnabled)
{
if (group == null) return;
group.alpha = alpha;
group.interactable = inputEnabled;
group.blocksRaycasts = inputEnabled;
}
private static void SetCanvasGroupInteractivity(CanvasGroup group, bool inputEnabled)
{
if (group == null) return;
group.interactable = inputEnabled;
group.blocksRaycasts = inputEnabled;
}
public async UniTask SuspendForOverlay()
{
if (_mainMenuGroup == null)
return;
ResetSettingsOverlayCtsAndCancelRunning();
SetCanvasGroupInteractivity(_mainMenuGroup, false);
try
{
_settingsOverlaySequence = Sequence.Create(useUnscaledTime: true)
.Group(Tween.Alpha(_displayGroup, new TweenSettings<float>
{
startValue = _displayGroup.alpha,
endValue = 0f,
settings = _settingsOverlayTweenSettings
}));
await _settingsOverlaySequence.ToUniTask(cancellationToken: _settingsOverlayCts.Token);
}
catch (OperationCanceledException)
{
return;
}
finally
{
_settingsOverlaySequence = default;
}
}
public async UniTask ResumeFromOverlay()
{
if (_mainMenuGroup == null)
return;
ResetSettingsOverlayCtsAndCancelRunning();
try
{
_settingsOverlaySequence = Sequence.Create(useUnscaledTime: true)
.Group(Tween.Alpha(_displayGroup, new TweenSettings<float>
{
startValue = _displayGroup.alpha,
endValue = 1f,
settings = _settingsOverlayTweenSettings
}));
await _settingsOverlaySequence.ToUniTask(cancellationToken: _settingsOverlayCts.Token);
}
catch (OperationCanceledException)
{
return;
}
finally
{
_settingsOverlaySequence = default;
}
SetCanvasGroupInteractivity(_mainMenuGroup, true);
_mainMenuSelectionGroup?.SelectIndex(1, true);
}
}
}