Refine UI stack and add Ashwick keypad puzzle
This commit is contained in:
@@ -98,7 +98,7 @@ namespace BriarQueen.Data.IO.Saves
|
||||
public enum LevelFlag
|
||||
{
|
||||
None = 0,
|
||||
MarketGateOpen,
|
||||
AshwickGateOpen,
|
||||
MarketplaceFirstEntry,
|
||||
}
|
||||
|
||||
|
||||
@@ -25,13 +25,9 @@ namespace BriarQueen.Data.Identifiers
|
||||
{
|
||||
None = 0,
|
||||
ChapterOneArrivalRoad,
|
||||
ChapterOneAshwickRidgeway,
|
||||
ChapterOneAshwickOutskirts,
|
||||
ChapterOneInsideBrokenDownCar,
|
||||
ChapterOneAshwickMarketplace,
|
||||
ChapterOneAshwickMournfall,
|
||||
ChapterOneAshwickRavensQuill,
|
||||
ChapterOneGloomedVeil,
|
||||
ChapterOneAshwickWaxworks
|
||||
ChapterOneAshwickOutskirtsKeypad,
|
||||
}
|
||||
|
||||
public enum AssetItemKey
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
using BriarQueen.Framework.Events.System;
|
||||
using BriarQueen.Framework.Managers.UI.Base;
|
||||
|
||||
namespace BriarQueen.Framework.Events.UI
|
||||
{
|
||||
public record UIWindowStateChangedEvent(WindowType WindowType, bool IsOpen) : IEvent;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d8f895856a064fb09a7f2ec4393f686
|
||||
@@ -5,6 +5,7 @@ using BriarQueen.Framework.Events.Gameplay;
|
||||
using BriarQueen.Framework.Events.Input;
|
||||
using BriarQueen.Framework.Events.UI;
|
||||
using BriarQueen.Framework.Managers.UI;
|
||||
using BriarQueen.Framework.Managers.UI.Base;
|
||||
using BriarQueen.Framework.Services.Game;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
@@ -109,7 +110,7 @@ namespace BriarQueen.Framework.Managers.Input
|
||||
if (_eventCoordinator != null)
|
||||
{
|
||||
_eventCoordinator.Unsubscribe<UIToggleHudEvent>(OnHudStateChanged);
|
||||
_eventCoordinator.Unsubscribe<ToggleCodexEvent>(OnCodexStateChanged);
|
||||
_eventCoordinator.Unsubscribe<UIWindowStateChangedEvent>(OnWindowStateChanged);
|
||||
_eventCoordinator.Unsubscribe<ToggleToolScreenEvent>(OnToolScreenStateChanged);
|
||||
_eventCoordinator.Unsubscribe<UIStackChangedEvent>(OnUIStackChanged);
|
||||
}
|
||||
@@ -158,7 +159,7 @@ namespace BriarQueen.Framework.Managers.Input
|
||||
ApplyCursorModeForCurrentScheme();
|
||||
|
||||
_eventCoordinator.Subscribe<UIToggleHudEvent>(OnHudStateChanged);
|
||||
_eventCoordinator.Subscribe<ToggleCodexEvent>(OnCodexStateChanged);
|
||||
_eventCoordinator.Subscribe<UIWindowStateChangedEvent>(OnWindowStateChanged);
|
||||
_eventCoordinator.Subscribe<ToggleToolScreenEvent>(OnToolScreenStateChanged);
|
||||
_eventCoordinator.Subscribe<UIStackChangedEvent>(OnUIStackChanged);
|
||||
|
||||
@@ -422,9 +423,10 @@ namespace BriarQueen.Framework.Managers.Input
|
||||
_hudHidden = !evt.Show;
|
||||
}
|
||||
|
||||
private void OnCodexStateChanged(ToggleCodexEvent evt)
|
||||
private void OnWindowStateChanged(UIWindowStateChangedEvent evt)
|
||||
{
|
||||
_codexShown = evt.Shown;
|
||||
if (evt.WindowType == WindowType.CodexWindow)
|
||||
_codexShown = evt.IsOpen;
|
||||
}
|
||||
|
||||
private void OnToolScreenStateChanged(ToggleToolScreenEvent evt)
|
||||
@@ -461,8 +463,8 @@ namespace BriarQueen.Framework.Managers.Input
|
||||
{
|
||||
if(_gameService.IsMainMenuSceneLoaded)
|
||||
return;
|
||||
_codexShown = !_codexShown;
|
||||
_eventCoordinator?.Publish(new ToggleCodexEvent(_codexShown));
|
||||
|
||||
_eventCoordinator?.Publish(new ToggleCodexEvent(!_codexShown));
|
||||
}
|
||||
|
||||
private void OnClick(InputAction.CallbackContext ctx)
|
||||
|
||||
@@ -323,7 +323,7 @@ namespace BriarQueen.Framework.Managers.Interaction
|
||||
|
||||
if (candidate.sortingOrder != currentBest.sortingOrder)
|
||||
return candidate.sortingOrder > currentBest.sortingOrder;
|
||||
|
||||
|
||||
if (candidate.depth != currentBest.depth)
|
||||
return candidate.depth > currentBest.depth;
|
||||
|
||||
|
||||
10
Assets/Scripts/Framework/Managers/UI/Base/IUIOverlayHost.cs
Normal file
10
Assets/Scripts/Framework/Managers/UI/Base/IUIOverlayHost.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace BriarQueen.Framework.Managers.UI.Base
|
||||
{
|
||||
public interface IUIOverlayHost
|
||||
{
|
||||
UniTask SuspendForOverlay();
|
||||
UniTask ResumeFromOverlay();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4424bf26f2b764c1ca63bc73d75470d6
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace BriarQueen.Framework.Managers.UI.Events
|
||||
{
|
||||
public enum SettingsOpenSource
|
||||
{
|
||||
Unknown,
|
||||
PauseMenu,
|
||||
MainMenu
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ed020edca638d4f1cbdf9b3468316efa
|
||||
@@ -2,5 +2,5 @@ using BriarQueen.Framework.Events.System;
|
||||
|
||||
namespace BriarQueen.Framework.Managers.UI.Events
|
||||
{
|
||||
public record UIToggleSettingsWindow(bool Show) : IEvent;
|
||||
}
|
||||
public record UIToggleSettingsWindow(bool Show, SettingsOpenSource Source = SettingsOpenSource.Unknown) : IEvent;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using BriarQueen.Data.Identifiers;
|
||||
using BriarQueen.Framework.Coordinators.Events;
|
||||
using BriarQueen.Framework.Events.UI;
|
||||
@@ -34,6 +35,7 @@ namespace BriarQueen.Framework.Managers.UI
|
||||
|
||||
private readonly Dictionary<WindowType, IUIWindow> _windows = new();
|
||||
private readonly Stack<IUIWindow> _windowStack = new();
|
||||
private readonly SemaphoreSlim _windowTransitionGate = new(1, 1);
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
@@ -43,6 +45,8 @@ namespace BriarQueen.Framework.Managers.UI
|
||||
private IPopup _infoPopup;
|
||||
private IPopup _tutorialPopup;
|
||||
private IScreenFader _screenFader;
|
||||
private IUIOverlayHost _mainMenuOverlayHost;
|
||||
private IUIOverlayHost _activeSettingsOverlayHost;
|
||||
|
||||
[Inject]
|
||||
public UIManager(
|
||||
@@ -155,11 +159,33 @@ namespace BriarQueen.Framework.Managers.UI
|
||||
_screenFader = screenFader;
|
||||
}
|
||||
|
||||
public void RegisterMainMenuOverlayHost(IUIOverlayHost host)
|
||||
{
|
||||
_mainMenuOverlayHost = host;
|
||||
}
|
||||
|
||||
public void UnregisterMainMenuOverlayHost(IUIOverlayHost host)
|
||||
{
|
||||
if (!ReferenceEquals(_mainMenuOverlayHost, host))
|
||||
return;
|
||||
|
||||
if (ReferenceEquals(_activeSettingsOverlayHost, host))
|
||||
_activeSettingsOverlayHost = null;
|
||||
|
||||
_mainMenuOverlayHost = null;
|
||||
}
|
||||
|
||||
private IUIWindow GetWindow(WindowType windowType)
|
||||
{
|
||||
return _windows.TryGetValue(windowType, out var window) ? window : null;
|
||||
}
|
||||
|
||||
public bool IsWindowOpen(WindowType windowType)
|
||||
{
|
||||
var target = GetWindow(windowType);
|
||||
return target != null && _windowStack.Contains(target);
|
||||
}
|
||||
|
||||
private async UniTask ApplyHudVisibility(bool visible)
|
||||
{
|
||||
if (_disposed || _hudContainer == null)
|
||||
@@ -197,7 +223,7 @@ namespace BriarQueen.Framework.Managers.UI
|
||||
private void ToggleSettingsWindow(UIToggleSettingsWindow eventData)
|
||||
{
|
||||
if (eventData.Show)
|
||||
OpenWindow(WindowType.SettingsWindow);
|
||||
OpenSettingsWindow(eventData.Source).Forget();
|
||||
else
|
||||
CloseWindow(WindowType.SettingsWindow);
|
||||
}
|
||||
@@ -208,9 +234,15 @@ namespace BriarQueen.Framework.Managers.UI
|
||||
return;
|
||||
|
||||
if (eventData.Shown)
|
||||
OpenWindow(WindowType.CodexWindow);
|
||||
{
|
||||
if (!IsWindowOpen(WindowType.CodexWindow))
|
||||
OpenWindow(WindowType.CodexWindow);
|
||||
}
|
||||
else
|
||||
CloseWindow(WindowType.CodexWindow);
|
||||
{
|
||||
if (IsWindowOpen(WindowType.CodexWindow))
|
||||
CloseWindow(WindowType.CodexWindow);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCodexChangedEvent(CodexChangedEvent eventData)
|
||||
@@ -300,32 +332,94 @@ namespace BriarQueen.Framework.Managers.UI
|
||||
OpenWindowInternal(windowType).Forget();
|
||||
}
|
||||
|
||||
private async UniTask OpenSettingsWindow(SettingsOpenSource source)
|
||||
{
|
||||
await _windowTransitionGate.WaitAsync();
|
||||
try
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
var window = GetWindow(WindowType.SettingsWindow);
|
||||
if (window == null)
|
||||
{
|
||||
Debug.LogError("[UIManager] Window of type SettingsWindow not registered.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_windowStack.Contains(window))
|
||||
return;
|
||||
|
||||
_activeSettingsOverlayHost = null;
|
||||
|
||||
var openingSettingsOverPause =
|
||||
source == SettingsOpenSource.PauseMenu &&
|
||||
ActiveWindow?.WindowType == WindowType.PauseMenuWindow &&
|
||||
ActiveWindow is IUIOverlayHost;
|
||||
|
||||
var openingSettingsOverMainMenu =
|
||||
source == SettingsOpenSource.MainMenu &&
|
||||
_mainMenuOverlayHost != null;
|
||||
|
||||
if (openingSettingsOverPause)
|
||||
{
|
||||
_activeSettingsOverlayHost = (IUIOverlayHost)ActiveWindow;
|
||||
await _activeSettingsOverlayHost.SuspendForOverlay();
|
||||
}
|
||||
else if (openingSettingsOverMainMenu)
|
||||
{
|
||||
_activeSettingsOverlayHost = _mainMenuOverlayHost;
|
||||
await _activeSettingsOverlayHost.SuspendForOverlay();
|
||||
}
|
||||
else if (ActiveWindow != null)
|
||||
{
|
||||
await ActiveWindow.Hide();
|
||||
}
|
||||
|
||||
_windowStack.Push(window);
|
||||
await window.Show();
|
||||
|
||||
NotifyWindowStateChanged(window.WindowType, true);
|
||||
NotifyUIStackChanged();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_windowTransitionGate.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private async UniTask OpenWindowInternal(WindowType windowType)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
var window = GetWindow(windowType);
|
||||
|
||||
if (window == null)
|
||||
await _windowTransitionGate.WaitAsync();
|
||||
try
|
||||
{
|
||||
Debug.LogError($"[UIManager] Window of type {windowType} not registered.");
|
||||
return;
|
||||
}
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
if (ActiveWindow == window)
|
||||
return;
|
||||
|
||||
if (ActiveWindow != null)
|
||||
{
|
||||
var window = GetWindow(windowType);
|
||||
|
||||
await ActiveWindow.Hide();
|
||||
if (window == null)
|
||||
{
|
||||
Debug.LogError($"[UIManager] Window of type {windowType} not registered.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_windowStack.Contains(window))
|
||||
return;
|
||||
|
||||
if (ActiveWindow != null)
|
||||
await ActiveWindow.Hide();
|
||||
|
||||
_windowStack.Push(window);
|
||||
await window.Show();
|
||||
|
||||
NotifyWindowStateChanged(window.WindowType, true);
|
||||
NotifyUIStackChanged();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_windowTransitionGate.Release();
|
||||
}
|
||||
|
||||
_windowStack.Push(window);
|
||||
await window.Show();
|
||||
|
||||
NotifyUIStackChanged();
|
||||
}
|
||||
|
||||
public void CloseWindow(WindowType windowType)
|
||||
@@ -335,27 +429,45 @@ namespace BriarQueen.Framework.Managers.UI
|
||||
|
||||
private async UniTask CloseWindowInternal(WindowType windowType)
|
||||
{
|
||||
if (_disposed || _windowStack.Count == 0)
|
||||
return;
|
||||
|
||||
var target = GetWindow(windowType);
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
while (_windowStack.Count > 0)
|
||||
await _windowTransitionGate.WaitAsync();
|
||||
try
|
||||
{
|
||||
var current = _windowStack.Pop();
|
||||
if (current != null)
|
||||
await current.Hide();
|
||||
if (_disposed || _windowStack.Count == 0)
|
||||
return;
|
||||
|
||||
if (current == target)
|
||||
break;
|
||||
var target = GetWindow(windowType);
|
||||
if (target == null || !_windowStack.Contains(target))
|
||||
return;
|
||||
|
||||
while (_windowStack.Count > 0)
|
||||
{
|
||||
var current = _windowStack.Pop();
|
||||
if (current != null)
|
||||
{
|
||||
await current.Hide();
|
||||
NotifyWindowStateChanged(current.WindowType, false);
|
||||
}
|
||||
|
||||
if (current == target)
|
||||
break;
|
||||
}
|
||||
|
||||
if (target.WindowType == WindowType.SettingsWindow && _activeSettingsOverlayHost != null)
|
||||
{
|
||||
await _activeSettingsOverlayHost.ResumeFromOverlay();
|
||||
_activeSettingsOverlayHost = null;
|
||||
}
|
||||
else if (ActiveWindow != null)
|
||||
{
|
||||
await ActiveWindow.Show();
|
||||
}
|
||||
|
||||
NotifyUIStackChanged();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_windowTransitionGate.Release();
|
||||
}
|
||||
|
||||
if (ActiveWindow != null)
|
||||
await ActiveWindow.Show();
|
||||
|
||||
NotifyUIStackChanged();
|
||||
}
|
||||
|
||||
public void CloseTopWindow()
|
||||
@@ -380,18 +492,38 @@ namespace BriarQueen.Framework.Managers.UI
|
||||
|
||||
private async UniTask CloseTopWindowInternal()
|
||||
{
|
||||
if (_disposed || _windowStack.Count == 0)
|
||||
return;
|
||||
await _windowTransitionGate.WaitAsync();
|
||||
try
|
||||
{
|
||||
if (_disposed || _windowStack.Count == 0)
|
||||
return;
|
||||
|
||||
var top = _windowStack.Pop();
|
||||
var top = _windowStack.Pop();
|
||||
|
||||
if (top != null)
|
||||
await top.Hide();
|
||||
if (top != null)
|
||||
{
|
||||
await top.Hide();
|
||||
NotifyWindowStateChanged(top.WindowType, false);
|
||||
}
|
||||
|
||||
if (ActiveWindow != null)
|
||||
await ActiveWindow.Show();
|
||||
if (top != null &&
|
||||
top.WindowType == WindowType.SettingsWindow &&
|
||||
_activeSettingsOverlayHost != null)
|
||||
{
|
||||
await _activeSettingsOverlayHost.ResumeFromOverlay();
|
||||
_activeSettingsOverlayHost = null;
|
||||
}
|
||||
else if (ActiveWindow != null)
|
||||
{
|
||||
await ActiveWindow.Show();
|
||||
}
|
||||
|
||||
NotifyUIStackChanged();
|
||||
NotifyUIStackChanged();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_windowTransitionGate.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetUIState()
|
||||
@@ -401,44 +533,71 @@ namespace BriarQueen.Framework.Managers.UI
|
||||
|
||||
public async UniTask ResetUIStateAsync()
|
||||
{
|
||||
while (_windowStack.Count > 0)
|
||||
await _windowTransitionGate.WaitAsync();
|
||||
try
|
||||
{
|
||||
var window = _windowStack.Pop();
|
||||
if (window != null)
|
||||
var shouldResumeSettingsHost = false;
|
||||
|
||||
while (_windowStack.Count > 0)
|
||||
{
|
||||
var window = _windowStack.Pop();
|
||||
if (window == null)
|
||||
continue;
|
||||
|
||||
if (window.WindowType == WindowType.SettingsWindow && _activeSettingsOverlayHost != null)
|
||||
shouldResumeSettingsHost = true;
|
||||
|
||||
try
|
||||
{
|
||||
await window.Hide();
|
||||
NotifyWindowStateChanged(window.WindowType, false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_tutorialPopup != null)
|
||||
if (shouldResumeSettingsHost)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _activeSettingsOverlayHost.ResumeFromOverlay();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
_activeSettingsOverlayHost = null;
|
||||
|
||||
if (_tutorialPopup != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _tutorialPopup.Hide();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
if (_infoPopup != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _infoPopup.Hide();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
NotifyUIStackChanged();
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
await _tutorialPopup.Hide();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
_windowTransitionGate.Release();
|
||||
}
|
||||
|
||||
if (_infoPopup != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _infoPopup.Hide();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
NotifyUIStackChanged();
|
||||
}
|
||||
|
||||
private void ResetUIStateHard()
|
||||
@@ -462,11 +621,18 @@ namespace BriarQueen.Framework.Managers.UI
|
||||
faderComponent.gameObject.SetActive(false);
|
||||
|
||||
_windowStack.Clear();
|
||||
_activeSettingsOverlayHost = null;
|
||||
_mainMenuOverlayHost = null;
|
||||
}
|
||||
|
||||
private void NotifyUIStackChanged()
|
||||
{
|
||||
_eventCoordinator.Publish(new UIStackChangedEvent(_windowStack.Count > 0));
|
||||
}
|
||||
|
||||
private void NotifyWindowStateChanged(WindowType windowType, bool isOpen)
|
||||
{
|
||||
_eventCoordinator.Publish(new UIWindowStateChangedEvent(windowType, isOpen));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ using VContainer;
|
||||
|
||||
namespace BriarQueen.Game.Items.HoverZones
|
||||
{
|
||||
public class InteractZone : MonoBehaviour, IInteractable
|
||||
public class TransitionZone : MonoBehaviour, IInteractable
|
||||
{
|
||||
[Header("Level Setup")]
|
||||
[SerializeField]
|
||||
@@ -0,0 +1,57 @@
|
||||
using BriarQueen.Data.IO.Saves;
|
||||
using BriarQueen.Framework.Events.UI;
|
||||
using BriarQueen.Framework.Managers.Levels.Data;
|
||||
using BriarQueen.Game.Items.HoverZones;
|
||||
using BriarQueen.Game.Puzzles.ChapterOne.AshwickHallow;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace BriarQueen.Game.Levels.ChapterOne.Ashwick
|
||||
{
|
||||
public class AshwickOutskirts : BaseLevel
|
||||
{
|
||||
[Header("Background")]
|
||||
[SerializeField]
|
||||
private Image _background;
|
||||
[SerializeField]
|
||||
private Sprite _backgroundOpenSprite;
|
||||
|
||||
[Header("Keypad Puzzle")]
|
||||
[SerializeField]
|
||||
private AshwickGate _ashwickGate;
|
||||
|
||||
[SerializeField]
|
||||
private TransitionZone _keypadZone;
|
||||
|
||||
[SerializeField]
|
||||
private TransitionZone _nextLevelZone;
|
||||
|
||||
private Sprite _defaultSprite;
|
||||
|
||||
protected async override UniTask PostLoadInternal()
|
||||
{
|
||||
if (SaveManager.GetLevelFlag(LevelFlag.AshwickGateOpen))
|
||||
{
|
||||
_background.sprite = _backgroundOpenSprite;
|
||||
_nextLevelZone.Unlock();
|
||||
await DestructionService.Destroy(_ashwickGate.gameObject);
|
||||
await DestructionService.Destroy(_keypadZone.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
public async UniTask OpenGate()
|
||||
{
|
||||
EventCoordinator.PublishImmediate(new FadeEvent(false));
|
||||
|
||||
_background.sprite = _backgroundOpenSprite;
|
||||
_nextLevelZone.Unlock();
|
||||
await DestructionService.Destroy(_ashwickGate.gameObject);
|
||||
await DestructionService.Destroy(_keypadZone.gameObject);
|
||||
|
||||
SaveManager.SetLevelFlag(LevelFlag.AshwickGateOpen, true);
|
||||
|
||||
EventCoordinator.PublishImmediate(new FadeEvent(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b17e98637f5042b4b70962875c373f5f
|
||||
timeCreated: 1778704389
|
||||
@@ -0,0 +1,24 @@
|
||||
using BriarQueen.Framework.Managers.Levels.Data;
|
||||
using BriarQueen.Framework.Managers.Player.Data;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BriarQueen.Game.Puzzles.ChapterOne.AshwickHallow
|
||||
{
|
||||
public class AshwickGate : BaseItem
|
||||
{
|
||||
[SerializeField] private AshwickGateKeypadPuzzle _keypadPuzzle;
|
||||
|
||||
public override string InteractableName => "Iron Gate";
|
||||
|
||||
public override UniTask OnInteract(ItemDataSo item = null)
|
||||
{
|
||||
if (!CheckEmptyHands())
|
||||
return UniTask.CompletedTask;
|
||||
|
||||
_keypadPuzzle?.Open();
|
||||
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b7b31f5b3d0844359def22f68ccea856
|
||||
timeCreated: 1778781649
|
||||
@@ -0,0 +1,393 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using BriarQueen.Data.Identifiers;
|
||||
using BriarQueen.Data.IO.Saves;
|
||||
using BriarQueen.Framework.Effects;
|
||||
using BriarQueen.Framework.Events.Save;
|
||||
using BriarQueen.Framework.Managers.Interaction;
|
||||
using BriarQueen.Framework.Services.Puzzles.Base;
|
||||
using BriarQueen.Game.Levels.ChapterOne.Ashwick;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using MemoryPack;
|
||||
using PrimeTween;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.UI;
|
||||
using VContainer;
|
||||
|
||||
namespace BriarQueen.Game.Puzzles.ChapterOne.AshwickHallow
|
||||
{
|
||||
[MemoryPackable]
|
||||
public partial class AshwickGateKeypadPuzzleState
|
||||
{
|
||||
public string Digits;
|
||||
}
|
||||
|
||||
public class AshwickGateKeypadPuzzle : BasePuzzle, IPuzzleStateful
|
||||
{
|
||||
private const string CorrectCode = "312";
|
||||
private const int RequiredDigits = 3;
|
||||
|
||||
[Header("Scene References")]
|
||||
[SerializeField] private AshwickOutskirts _outskirts;
|
||||
[SerializeField] private CanvasGroup _panelGroup;
|
||||
[SerializeField] private TMP_InputField _displayField;
|
||||
[SerializeField] private UILightGlow _statusGlow;
|
||||
[SerializeField] private GraphicRaycaster _graphicRaycaster;
|
||||
[SerializeField] private Button _closeButton;
|
||||
[SerializeField] private Button[] _digitButtons = new Button[9];
|
||||
|
||||
[Header("Light Colors")]
|
||||
[SerializeField] private Color _incorrectGlowColor = new(0.85f, 0.2f, 0.2f, 1f);
|
||||
[SerializeField] private Color _correctGlowColor = new(0.2f, 0.85f, 0.35f, 1f);
|
||||
|
||||
[Header("Timing")]
|
||||
[SerializeField] private float _feedbackDuration = 0.35f;
|
||||
|
||||
[SerializeField] private TweenSettings _panelFadeTweenSettings = new()
|
||||
{
|
||||
duration = 1.5f,
|
||||
ease = Ease.OutQuad,
|
||||
useUnscaledTime = true
|
||||
};
|
||||
|
||||
private readonly UnityAction[] _digitCallbacks = new UnityAction[9];
|
||||
|
||||
private InteractManager _interactManager;
|
||||
private CancellationTokenSource _panelCts;
|
||||
private Sequence _panelSequence;
|
||||
|
||||
private string _currentDigits = string.Empty;
|
||||
private bool _isCompleted;
|
||||
private bool _isEvaluating;
|
||||
private bool _isOpen;
|
||||
private bool _raycasterRegistered;
|
||||
|
||||
public override string PuzzleID => PuzzleIdentifiers.AllPuzzles[PuzzleKey.AshwickMarketGate];
|
||||
public bool IsCompleted => _isCompleted || SaveManager.GetLevelFlag(LevelFlag.AshwickGateOpen);
|
||||
|
||||
[Inject]
|
||||
public void ConstructKeypad(InteractManager interactManager)
|
||||
{
|
||||
_interactManager = interactManager;
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (_displayField != null)
|
||||
{
|
||||
_displayField.readOnly = true;
|
||||
_displayField.text = string.Empty;
|
||||
}
|
||||
|
||||
BindButtons();
|
||||
SetPanelState(0f, false, false);
|
||||
SyncDisplay();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
UnbindButtons();
|
||||
TryUnregisterRaycaster();
|
||||
CancelPanelTween();
|
||||
}
|
||||
|
||||
public override UniTask PostLoad()
|
||||
{
|
||||
_isCompleted = SaveManager.GetLevelFlag(LevelFlag.AshwickGateOpen);
|
||||
_isOpen = false;
|
||||
_isEvaluating = false;
|
||||
|
||||
SetPanelState(0f, false, false);
|
||||
SyncDisplay();
|
||||
_statusGlow?.TurnOff().Forget();
|
||||
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
public override UniTask CompletePuzzle()
|
||||
{
|
||||
return CompletePuzzleInternal();
|
||||
}
|
||||
|
||||
public void Open()
|
||||
{
|
||||
OpenInternal().Forget();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
CloseInternal(requestSave: true).Forget();
|
||||
}
|
||||
|
||||
public UniTask<byte[]> CaptureState()
|
||||
{
|
||||
var state = new AshwickGateKeypadPuzzleState
|
||||
{
|
||||
Digits = IsCompleted ? string.Empty : _currentDigits
|
||||
};
|
||||
|
||||
return UniTask.FromResult(MemoryPackSerializer.Serialize(state));
|
||||
}
|
||||
|
||||
public UniTask RestoreState(byte[] state)
|
||||
{
|
||||
_isCompleted = SaveManager.GetLevelFlag(LevelFlag.AshwickGateOpen);
|
||||
_currentDigits = string.Empty;
|
||||
|
||||
if (!_isCompleted && state is { Length: > 0 })
|
||||
{
|
||||
var restored = MemoryPackSerializer.Deserialize<AshwickGateKeypadPuzzleState>(state);
|
||||
_currentDigits = restored?.Digits ?? string.Empty;
|
||||
|
||||
if (_currentDigits.Length > RequiredDigits)
|
||||
_currentDigits = _currentDigits[..RequiredDigits];
|
||||
}
|
||||
|
||||
SyncDisplay();
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
private async UniTask CompletePuzzleInternal()
|
||||
{
|
||||
if (IsCompleted || _outskirts == null)
|
||||
return;
|
||||
|
||||
_isCompleted = true;
|
||||
_currentDigits = string.Empty;
|
||||
SyncDisplay();
|
||||
|
||||
SaveManager.SetPuzzleCompleted(PuzzleKey.AshwickMarketGate, true, requestSave: false);
|
||||
|
||||
await CloseInternal(requestSave: false);
|
||||
await _outskirts.OpenGate();
|
||||
}
|
||||
|
||||
private async UniTaskVoid OpenInternal()
|
||||
{
|
||||
if (IsCompleted || _isEvaluating || _isOpen)
|
||||
return;
|
||||
|
||||
_isOpen = true;
|
||||
ResetPanelTween();
|
||||
|
||||
SetPanelState(0f, false, true);
|
||||
SyncDisplay();
|
||||
TryRegisterRaycaster();
|
||||
_statusGlow?.TurnOff().Forget();
|
||||
|
||||
try
|
||||
{
|
||||
_panelSequence = Sequence.Create(useUnscaledTime: true)
|
||||
.Group(Tween.Alpha(_panelGroup, new TweenSettings<float>
|
||||
{
|
||||
startValue = 0f,
|
||||
endValue = 1f,
|
||||
settings = _panelFadeTweenSettings
|
||||
}));
|
||||
|
||||
await _panelSequence.ToUniTask(cancellationToken: _panelCts.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_panelSequence = default;
|
||||
}
|
||||
|
||||
SetPanelState(1f, true, true);
|
||||
}
|
||||
|
||||
private async UniTask CloseInternal(bool requestSave)
|
||||
{
|
||||
if (!_isOpen)
|
||||
return;
|
||||
|
||||
_isOpen = false;
|
||||
ResetPanelTween();
|
||||
|
||||
if (_panelGroup != null)
|
||||
{
|
||||
_panelGroup.interactable = false;
|
||||
_panelGroup.blocksRaycasts = true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_panelSequence = Sequence.Create(useUnscaledTime: true)
|
||||
.Group(Tween.Alpha(_panelGroup, new TweenSettings<float>
|
||||
{
|
||||
startValue = _panelGroup != null ? _panelGroup.alpha : 0f,
|
||||
endValue = 0f,
|
||||
settings = _panelFadeTweenSettings
|
||||
}));
|
||||
|
||||
await _panelSequence.ToUniTask(cancellationToken: _panelCts.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_panelSequence = default;
|
||||
}
|
||||
|
||||
SetPanelState(0f, false, false);
|
||||
TryUnregisterRaycaster();
|
||||
|
||||
if (requestSave)
|
||||
EventCoordinator.PublishImmediate(new RequestGameSaveEvent());
|
||||
}
|
||||
|
||||
private void OnDigitPressed(int digit)
|
||||
{
|
||||
if (_isEvaluating || IsCompleted || !_isOpen || _currentDigits.Length >= RequiredDigits)
|
||||
return;
|
||||
|
||||
_currentDigits += digit.ToString();
|
||||
SyncDisplay();
|
||||
|
||||
if (_currentDigits.Length == RequiredDigits)
|
||||
EvaluateCode().Forget();
|
||||
}
|
||||
|
||||
private async UniTaskVoid EvaluateCode()
|
||||
{
|
||||
_isEvaluating = true;
|
||||
|
||||
if (_panelGroup != null)
|
||||
_panelGroup.interactable = false;
|
||||
|
||||
if (_currentDigits == CorrectCode)
|
||||
{
|
||||
if (_statusGlow != null)
|
||||
{
|
||||
_statusGlow.SetLightColor(_correctGlowColor);
|
||||
await _statusGlow.TurnOn();
|
||||
}
|
||||
|
||||
await CompletePuzzleInternal();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_statusGlow != null)
|
||||
{
|
||||
_statusGlow.SetLightColor(_incorrectGlowColor);
|
||||
await _statusGlow.TurnOn();
|
||||
}
|
||||
|
||||
_currentDigits = string.Empty;
|
||||
SyncDisplay();
|
||||
|
||||
if (_statusGlow != null)
|
||||
await _statusGlow.TurnOff();
|
||||
|
||||
if (_isOpen && _panelGroup != null)
|
||||
_panelGroup.interactable = true;
|
||||
}
|
||||
|
||||
_isEvaluating = false;
|
||||
}
|
||||
|
||||
private void BindButtons()
|
||||
{
|
||||
for (var i = 0; i < _digitButtons.Length; i++)
|
||||
{
|
||||
var button = _digitButtons[i];
|
||||
if (button == null)
|
||||
continue;
|
||||
|
||||
var digit = i + 1;
|
||||
_digitCallbacks[i] = () => OnDigitPressed(digit);
|
||||
button.onClick.AddListener(_digitCallbacks[i]);
|
||||
}
|
||||
|
||||
if (_closeButton != null)
|
||||
_closeButton.onClick.AddListener(Close);
|
||||
}
|
||||
|
||||
private void UnbindButtons()
|
||||
{
|
||||
for (var i = 0; i < _digitButtons.Length; i++)
|
||||
{
|
||||
var button = _digitButtons[i];
|
||||
if (button == null || _digitCallbacks[i] == null)
|
||||
continue;
|
||||
|
||||
button.onClick.RemoveListener(_digitCallbacks[i]);
|
||||
_digitCallbacks[i] = null;
|
||||
}
|
||||
|
||||
if (_closeButton != null)
|
||||
_closeButton.onClick.RemoveListener(Close);
|
||||
}
|
||||
|
||||
private void SyncDisplay()
|
||||
{
|
||||
if (_displayField == null)
|
||||
return;
|
||||
|
||||
_displayField.text = _currentDigits;
|
||||
}
|
||||
|
||||
private void SetPanelState(float alpha, bool interactable, bool blocksRaycasts)
|
||||
{
|
||||
if (_panelGroup == null)
|
||||
return;
|
||||
|
||||
_panelGroup.alpha = alpha;
|
||||
_panelGroup.interactable = interactable;
|
||||
_panelGroup.blocksRaycasts = blocksRaycasts;
|
||||
}
|
||||
|
||||
private void ResetPanelTween()
|
||||
{
|
||||
CancelPanelTween();
|
||||
_panelCts = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
private void CancelPanelTween()
|
||||
{
|
||||
if (_panelSequence.isAlive)
|
||||
_panelSequence.Stop();
|
||||
|
||||
if (_panelCts != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_panelCts.Cancel();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
_panelCts.Dispose();
|
||||
_panelCts = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void TryRegisterRaycaster()
|
||||
{
|
||||
if (_raycasterRegistered || _interactManager == null || _graphicRaycaster == null)
|
||||
return;
|
||||
|
||||
_interactManager.AddUIRaycaster(_graphicRaycaster);
|
||||
_interactManager.SetExclusiveRaycaster(_graphicRaycaster);
|
||||
_raycasterRegistered = true;
|
||||
}
|
||||
|
||||
private void TryUnregisterRaycaster()
|
||||
{
|
||||
if (!_raycasterRegistered || _interactManager == null || _graphicRaycaster == null)
|
||||
return;
|
||||
|
||||
_interactManager.RemoveUIRaycaster(_graphicRaycaster);
|
||||
_interactManager.ClearExclusiveRaycaster();
|
||||
_raycasterRegistered = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 229b8c87b487340b8b0a02349675f70c
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9afe762158da4f8c8bb8a1bcb98d62b1
|
||||
timeCreated: 1778242706
|
||||
@@ -1,26 +0,0 @@
|
||||
using System.Linq;
|
||||
using BriarQueen.Data.Identifiers;
|
||||
using BriarQueen.Framework.Events.UI;
|
||||
using BriarQueen.Framework.Managers.Levels.Data;
|
||||
using BriarQueen.Framework.Managers.Player.Data;
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace BriarQueen.Game.Puzzles.ChapterOne.AshwickHallow.GatePuzzle
|
||||
{
|
||||
public class AshwickGate : BaseItem
|
||||
{
|
||||
public override UniTask OnInteract(ItemDataSo item = null)
|
||||
{
|
||||
var codex = PlayerManager.GetDiscoveredCodexEntriesByType(CodexType.PuzzleClue);
|
||||
|
||||
if (codex.Any(x => x.UniqueID == CodexEntryIDs.Get(ClueEntryID.AshwickMarketGate)))
|
||||
{
|
||||
EventCoordinator.Publish(new DisplayInteractEvent($"The note said to use the lights."));
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
EventCoordinator.Publish(new DisplayInteractEvent($"It's locked."));
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1bc3c6697e5b4c029e807116f930e9a3
|
||||
timeCreated: 1778247353
|
||||
@@ -1,167 +0,0 @@
|
||||
using BriarQueen.Data.Identifiers;
|
||||
using BriarQueen.Data.IO.Saves;
|
||||
using BriarQueen.Framework.Effects;
|
||||
using BriarQueen.Framework.Events.Save;
|
||||
using BriarQueen.Framework.Services.Puzzles.Base;
|
||||
using BriarQueen.Game.Items.HoverZones;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using MemoryPack;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BriarQueen.Game.Puzzles.ChapterOne.AshwickHallow.GatePuzzle
|
||||
{
|
||||
public class AshwickMarketGatePuzzle : BasePuzzle, IPuzzleStateful
|
||||
{
|
||||
[Header("Lights")]
|
||||
[SerializeField]
|
||||
private StreetlightGlow _leftLight;
|
||||
[SerializeField]
|
||||
private StreetlightGlow _rightLight;
|
||||
|
||||
[Header("Solution")]
|
||||
[SerializeField]
|
||||
private StreetlightGlowState _leftLightRequiredState = StreetlightGlowState.Blue;
|
||||
[SerializeField]
|
||||
private StreetlightGlowState _rightLightRequiredState = StreetlightGlowState.Blue;
|
||||
|
||||
[Header("Gate")]
|
||||
[SerializeField]
|
||||
private UIDissolveImage _gateImage;
|
||||
|
||||
[Header("Transition")]
|
||||
[SerializeField]
|
||||
private InteractZone _marketplaceZone;
|
||||
|
||||
private bool _isCompleted;
|
||||
private bool _isCompleting;
|
||||
|
||||
public override string PuzzleID => PuzzleIdentifiers.AllPuzzles[PuzzleKey.AshwickMarketGate];
|
||||
|
||||
public bool IsCompleted => _isCompleted;
|
||||
|
||||
public override UniTask PostLoad()
|
||||
{
|
||||
_leftLight?.Initialize(this);
|
||||
_rightLight?.Initialize(this);
|
||||
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
public async UniTask EvaluateCompletion()
|
||||
{
|
||||
if (_isCompleted || _isCompleting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CheckComplete())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await CompletePuzzle();
|
||||
}
|
||||
|
||||
public override async UniTask CompletePuzzle()
|
||||
{
|
||||
if (_isCompleted || _isCompleting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isCompleting = true;
|
||||
|
||||
try
|
||||
{
|
||||
_isCompleted = true;
|
||||
|
||||
_leftLight?.Lock();
|
||||
_rightLight?.Lock();
|
||||
|
||||
_marketplaceZone.gameObject.SetActive(true);
|
||||
|
||||
SaveManager.SetPuzzleCompleted(PuzzleKey.AshwickMarketGate, true, false);
|
||||
SaveManager.SetLevelFlag(LevelFlag.MarketGateOpen, true, false);
|
||||
EventCoordinator.PublishImmediate(new RequestGameSaveEvent());
|
||||
|
||||
if (_gateImage != null)
|
||||
{
|
||||
await _gateImage.DissolveOutAndDestroy();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isCompleting = false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool CheckComplete()
|
||||
{
|
||||
if (_leftLight == null || _rightLight == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _leftLight.CurrentState == _leftLightRequiredState &&
|
||||
_rightLight.CurrentState == _rightLightRequiredState;
|
||||
}
|
||||
|
||||
public UniTask<byte[]> CaptureState()
|
||||
{
|
||||
var payload = new AshwickMarketGatePuzzleStatePayload
|
||||
{
|
||||
LeftLightState = _leftLight != null ? _leftLight.CurrentState : StreetlightGlowState.Off,
|
||||
RightLightState = _rightLight != null ? _rightLight.CurrentState : StreetlightGlowState.Off,
|
||||
IsCompleted = _isCompleted
|
||||
};
|
||||
|
||||
return UniTask.FromResult(MemoryPackSerializer.Serialize(payload));
|
||||
}
|
||||
|
||||
public async UniTask RestoreState(byte[] state)
|
||||
{
|
||||
var isMarkedComplete = SaveManager.CurrentSave?.PersistentVariables?.Game
|
||||
?.IsPuzzleCompleted(PuzzleKey.AshwickMarketGate) == true;
|
||||
|
||||
var payload = new AshwickMarketGatePuzzleStatePayload
|
||||
{
|
||||
LeftLightState = StreetlightGlowState.Off,
|
||||
RightLightState = StreetlightGlowState.Off,
|
||||
IsCompleted = isMarkedComplete
|
||||
};
|
||||
|
||||
if (state is { Length: > 0 })
|
||||
{
|
||||
payload = MemoryPackSerializer.Deserialize<AshwickMarketGatePuzzleStatePayload>(state);
|
||||
payload.IsCompleted |= isMarkedComplete;
|
||||
}
|
||||
|
||||
if (_leftLight != null)
|
||||
{
|
||||
await _leftLight.SetState(payload.LeftLightState, false);
|
||||
}
|
||||
|
||||
if (_rightLight != null)
|
||||
{
|
||||
await _rightLight.SetState(payload.RightLightState, false);
|
||||
}
|
||||
|
||||
if (!payload.IsCompleted)
|
||||
{
|
||||
_isCompleted = false;
|
||||
_leftLight?.Unlock();
|
||||
_rightLight?.Unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
_isCompleted = true;
|
||||
_leftLight?.Lock();
|
||||
_rightLight?.Lock();
|
||||
|
||||
if (_gateImage != null && DestructionService != null)
|
||||
{
|
||||
await DestructionService.Destroy(_gateImage.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 701653a82bc1487d8aff6ea83a9abaeb
|
||||
timeCreated: 1778243473
|
||||
@@ -1,12 +0,0 @@
|
||||
using MemoryPack;
|
||||
|
||||
namespace BriarQueen.Game.Puzzles.ChapterOne.AshwickHallow.GatePuzzle
|
||||
{
|
||||
[MemoryPackable]
|
||||
public partial struct AshwickMarketGatePuzzleStatePayload
|
||||
{
|
||||
public StreetlightGlowState LeftLightState;
|
||||
public StreetlightGlowState RightLightState;
|
||||
public bool IsCompleted;
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: de15a1ddef77473cb145cd59500cde71
|
||||
@@ -1,181 +0,0 @@
|
||||
using BriarQueen.Data.Identifiers;
|
||||
using BriarQueen.Framework.Effects;
|
||||
using BriarQueen.Framework.Events.UI;
|
||||
using BriarQueen.Framework.Managers.Levels.Data;
|
||||
using BriarQueen.Framework.Managers.Player.Data;
|
||||
using BriarQueen.Framework.Managers.UI;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BriarQueen.Game.Puzzles.ChapterOne.AshwickHallow.GatePuzzle
|
||||
{
|
||||
public enum StreetlightGlowState
|
||||
{
|
||||
Off = 0,
|
||||
Red = 1,
|
||||
Orange = 2,
|
||||
Green = 3,
|
||||
Blue = 4
|
||||
}
|
||||
|
||||
public class StreetlightGlow : BaseItem
|
||||
{
|
||||
[Header("Puzzle")]
|
||||
[SerializeField]
|
||||
private AshwickMarketGatePuzzle _puzzle;
|
||||
|
||||
[Header("Light")]
|
||||
[SerializeField]
|
||||
private UILightGlow _light;
|
||||
|
||||
[SerializeField]
|
||||
private float _changeDuration = 0.25f;
|
||||
|
||||
[SerializeField]
|
||||
private float _activeIntensity = 1.5f;
|
||||
|
||||
[Header("Colours")]
|
||||
[SerializeField]
|
||||
private Color _red = new(0.872f, 0.08f, 0.1704798f, 1f);
|
||||
|
||||
[SerializeField]
|
||||
private Color _orange = new(0.8666667f, 0.5109999f, 0f, 1f);
|
||||
|
||||
[SerializeField]
|
||||
private Color _green = new(0.12f, 0.865f, 0.1f, 1f);
|
||||
|
||||
[SerializeField]
|
||||
private Color _blue = new(0.1490196f, 0.6001954f, 1f, 1f);
|
||||
|
||||
private bool _isChanging;
|
||||
private StreetlightGlowState _currentState = StreetlightGlowState.Off;
|
||||
|
||||
public StreetlightGlowState CurrentState => _currentState;
|
||||
|
||||
public override string InteractableName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_isLocked)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return _currentState == StreetlightGlowState.Off
|
||||
? "Turn On"
|
||||
: "Switch Colour";
|
||||
}
|
||||
}
|
||||
|
||||
public override UICursorService.CursorStyle ApplicableCursorStyle => UICursorService.CursorStyle.Interact;
|
||||
|
||||
public void Initialize(AshwickMarketGatePuzzle puzzle)
|
||||
{
|
||||
if (_puzzle == null)
|
||||
{
|
||||
_puzzle = puzzle;
|
||||
}
|
||||
|
||||
SetState(_currentState, false).Forget();
|
||||
}
|
||||
|
||||
public override async UniTask OnInteract(ItemDataSo item = null)
|
||||
{
|
||||
if (item != null)
|
||||
{
|
||||
EventCoordinator.Publish(new DisplayInteractEvent(InteractEventIDs.Get(ItemInteractKey.CantUseItem)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (_isLocked || _isChanging)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CheckEmptyHands())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await CycleNext();
|
||||
|
||||
if (_puzzle != null)
|
||||
{
|
||||
await _puzzle.EvaluateCompletion();
|
||||
}
|
||||
}
|
||||
|
||||
public async UniTask SetState(StreetlightGlowState state, bool animate)
|
||||
{
|
||||
_currentState = state;
|
||||
|
||||
if (_light == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var targetColor = GetColorForState(state);
|
||||
var targetIntensity = state == StreetlightGlowState.Off ? 0f : _activeIntensity;
|
||||
|
||||
if (!animate)
|
||||
{
|
||||
_light.SetLightColor(targetColor);
|
||||
_light.SetIntensity(targetIntensity);
|
||||
return;
|
||||
}
|
||||
|
||||
_isChanging = true;
|
||||
|
||||
try
|
||||
{
|
||||
await _light.TweenTo(targetColor, targetIntensity, _changeDuration);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isChanging = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Lock()
|
||||
{
|
||||
_isLocked = true;
|
||||
}
|
||||
|
||||
public void Unlock()
|
||||
{
|
||||
_isLocked = false;
|
||||
}
|
||||
|
||||
public Color GetCurrentColor()
|
||||
{
|
||||
return GetColorForState(_currentState);
|
||||
}
|
||||
|
||||
private UniTask CycleNext()
|
||||
{
|
||||
var nextState = _currentState switch
|
||||
{
|
||||
StreetlightGlowState.Off => StreetlightGlowState.Red,
|
||||
StreetlightGlowState.Red => StreetlightGlowState.Green,
|
||||
StreetlightGlowState.Green => StreetlightGlowState.Orange,
|
||||
StreetlightGlowState.Orange => StreetlightGlowState.Blue,
|
||||
StreetlightGlowState.Blue => StreetlightGlowState.Off,
|
||||
_ => StreetlightGlowState.Off
|
||||
};
|
||||
|
||||
return SetState(nextState, true);
|
||||
}
|
||||
|
||||
private Color GetColorForState(StreetlightGlowState state)
|
||||
{
|
||||
return state switch
|
||||
{
|
||||
StreetlightGlowState.Red => _red,
|
||||
StreetlightGlowState.Orange => _orange,
|
||||
StreetlightGlowState.Green => _green,
|
||||
StreetlightGlowState.Blue => _blue,
|
||||
_ => Color.black
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 74c68ae5d328428f87e9fb87424df340
|
||||
timeCreated: 1778242706
|
||||
@@ -341,12 +341,10 @@ namespace BriarQueen.UI.Codex
|
||||
|
||||
if (_canvasGroup != null)
|
||||
{
|
||||
_canvasGroup.blocksRaycasts = false;
|
||||
_canvasGroup.blocksRaycasts = true;
|
||||
_canvasGroup.interactable = false;
|
||||
}
|
||||
|
||||
TryUnregisterRaycaster();
|
||||
|
||||
_windowSequence = Sequence.Create(useUnscaledTime: true)
|
||||
.Group(Tween.Alpha(_backgroundGroup, new TweenSettings<float>
|
||||
{
|
||||
@@ -375,6 +373,7 @@ namespace BriarQueen.UI.Codex
|
||||
_canvasGroup.interactable = false;
|
||||
}
|
||||
|
||||
TryUnregisterRaycaster();
|
||||
gameObject.SetActive(false);
|
||||
Debug.Log($"[CodexWindow] Codex Window Hide Complete.");
|
||||
}
|
||||
@@ -1276,4 +1275,4 @@ namespace BriarQueen.UI.Codex
|
||||
|
||||
private enum LeftPanelState { Categories, Locations, Entries }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ 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;
|
||||
@@ -19,7 +21,7 @@ using VContainer;
|
||||
|
||||
namespace BriarQueen.UI.Menus
|
||||
{
|
||||
public class MainMenuWindow : MonoBehaviour
|
||||
public class MainMenuWindow : MonoBehaviour, IUIOverlayHost
|
||||
{
|
||||
[Header("Intro Screen")]
|
||||
[SerializeField]
|
||||
@@ -38,9 +40,9 @@ namespace BriarQueen.UI.Menus
|
||||
[FormerlySerializedAs("_mainMenuWindowCanvasGroup")]
|
||||
private CanvasGroup _mainMenuGroup;
|
||||
|
||||
[Header("Buttons")]
|
||||
[Header("Display")]
|
||||
[SerializeField]
|
||||
private CanvasGroup _buttonsGroup;
|
||||
private CanvasGroup _displayGroup;
|
||||
|
||||
[SerializeField]
|
||||
private AnimatedSelectionButtonGroup _mainMenuSelectionGroup;
|
||||
@@ -93,6 +95,14 @@ namespace BriarQueen.UI.Menus
|
||||
useUnscaledTime = true
|
||||
};
|
||||
|
||||
[SerializeField]
|
||||
private TweenSettings _settingsOverlayTweenSettings = new()
|
||||
{
|
||||
duration = 0.25f,
|
||||
ease = Ease.OutQuad,
|
||||
useUnscaledTime = true
|
||||
};
|
||||
|
||||
[SerializeField]
|
||||
private TweenSettings _pressStartPulseTweenSettings = new()
|
||||
{
|
||||
@@ -106,23 +116,31 @@ namespace BriarQueen.UI.Menus
|
||||
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)
|
||||
public void Construct(
|
||||
GameService gameService,
|
||||
EventCoordinator eventCoordinator,
|
||||
InputManager inputManager,
|
||||
UIManager uiManager)
|
||||
{
|
||||
_gameService = gameService;
|
||||
_eventCoordinator = eventCoordinator;
|
||||
_inputManager = inputManager;
|
||||
_uiManager = uiManager;
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
@@ -139,6 +157,7 @@ namespace BriarQueen.UI.Menus
|
||||
private void OnEnable()
|
||||
{
|
||||
BindButtons();
|
||||
_uiManager?.RegisterMainMenuOverlayHost(this);
|
||||
|
||||
_eventCoordinator?.PublishImmediate(new UIToggleHudEvent(false));
|
||||
_inputManager?.BindSubmitForStart(OnIntroSubmit);
|
||||
@@ -164,22 +183,27 @@ namespace BriarQueen.UI.Menus
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -386,7 +410,7 @@ namespace BriarQueen.UI.Menus
|
||||
|
||||
private void OnSettingsClicked()
|
||||
{
|
||||
_eventCoordinator?.PublishImmediate(new UIToggleSettingsWindow(true));
|
||||
_eventCoordinator?.PublishImmediate(new UIToggleSettingsWindow(true, SettingsOpenSource.MainMenu));
|
||||
}
|
||||
|
||||
private void OnQuitClicked()
|
||||
@@ -432,9 +456,9 @@ namespace BriarQueen.UI.Menus
|
||||
endValue = 1f,
|
||||
settings = _selectSaveTweenSettings
|
||||
}))
|
||||
.Group(Tween.Alpha(_buttonsGroup, new TweenSettings<float>
|
||||
.Group(Tween.Alpha(_displayGroup, new TweenSettings<float>
|
||||
{
|
||||
startValue = _buttonsGroup.alpha,
|
||||
startValue = _displayGroup.alpha,
|
||||
endValue = 0f,
|
||||
settings = _selectSaveTweenSettings
|
||||
}));
|
||||
@@ -483,9 +507,9 @@ namespace BriarQueen.UI.Menus
|
||||
endValue = 0f,
|
||||
settings = _selectSaveTweenSettings
|
||||
}))
|
||||
.Group(Tween.Alpha(_buttonsGroup, new TweenSettings<float>
|
||||
.Group(Tween.Alpha(_displayGroup, new TweenSettings<float>
|
||||
{
|
||||
startValue = _buttonsGroup.alpha,
|
||||
startValue = _displayGroup.alpha,
|
||||
endValue = 1f,
|
||||
settings = _selectSaveTweenSettings
|
||||
}));
|
||||
@@ -544,6 +568,19 @@ namespace BriarQueen.UI.Menus
|
||||
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)
|
||||
@@ -597,5 +634,67 @@ namespace BriarQueen.UI.Menus
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ using VContainer;
|
||||
|
||||
namespace BriarQueen.UI.Menus
|
||||
{
|
||||
public class PauseMenuWindow : MonoBehaviour, IUIWindow
|
||||
public class PauseMenuWindow : MonoBehaviour, IUIWindow, IUIOverlayHost
|
||||
{
|
||||
[Header("Root UI")]
|
||||
[SerializeField] private CanvasGroup _canvasGroup;
|
||||
@@ -186,11 +186,8 @@ namespace BriarQueen.UI.Menus
|
||||
{
|
||||
StopAndResetCancellation();
|
||||
|
||||
_canvasGroup.blocksRaycasts = false;
|
||||
_canvasGroup.blocksRaycasts = true;
|
||||
_canvasGroup.interactable = false;
|
||||
|
||||
|
||||
TryUnregisterRaycaster();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -212,9 +209,46 @@ namespace BriarQueen.UI.Menus
|
||||
_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]
|
||||
@@ -299,7 +333,7 @@ namespace BriarQueen.UI.Menus
|
||||
|
||||
private void OnSettingsClicked(UnderlineButton _)
|
||||
{
|
||||
_eventCoordinator?.Publish(new UIToggleSettingsWindow(true));
|
||||
_eventCoordinator?.Publish(new UIToggleSettingsWindow(true, SettingsOpenSource.PauseMenu));
|
||||
}
|
||||
|
||||
private void OnExitClicked(UnderlineButton _)
|
||||
@@ -430,4 +464,4 @@ namespace BriarQueen.UI.Menus
|
||||
_cts = new CancellationTokenSource();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,10 +306,8 @@ namespace BriarQueen.UI.Menus
|
||||
StopAndResetCancellation();
|
||||
_confirmUnappliedChangesWindow?.CloseImmediate();
|
||||
|
||||
_canvasGroup.blocksRaycasts = false;
|
||||
_canvasGroup.blocksRaycasts = true;
|
||||
_canvasGroup.interactable = false;
|
||||
|
||||
TryUnregisterRaycaster();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -345,6 +343,10 @@ namespace BriarQueen.UI.Menus
|
||||
_panelSequence = default;
|
||||
}
|
||||
|
||||
_canvasGroup.blocksRaycasts = false;
|
||||
_canvasGroup.interactable = false;
|
||||
|
||||
TryUnregisterRaycaster();
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
@@ -916,6 +918,7 @@ namespace BriarQueen.UI.Menus
|
||||
return;
|
||||
|
||||
_interactManager.AddUIRaycaster(_graphicRaycaster);
|
||||
_interactManager.SetExclusiveRaycaster(_graphicRaycaster);
|
||||
_raycasterRegistered = true;
|
||||
}
|
||||
|
||||
@@ -928,7 +931,8 @@ namespace BriarQueen.UI.Menus
|
||||
return;
|
||||
|
||||
_interactManager.RemoveUIRaycaster(_graphicRaycaster);
|
||||
_interactManager.ClearExclusiveRaycaster();
|
||||
_raycasterRegistered = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,4 +28,4 @@ namespace BriarQueen.UI.Scopes
|
||||
builder.RegisterComponent(_newSaveWindow);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user