Add subtitle UI for voice playback
This commit is contained in:
@@ -25,7 +25,7 @@ mainly on vines and thorns.
|
||||
9. **Tokens** - Cancellation Tokens should be re-used where possible.
|
||||
10. **Versions** - We're using C# 9 with Unity 6.3
|
||||
11. **Workflow** - You're not to make any code changes until you've shown me the proposed changes, I'll then either approve, deny, or modify them before you make the change.
|
||||
|
||||
12. **Events** - We use C# events, actions, and delegates rather than UnityEvents, except when dealing with raw/stock ui components.
|
||||
|
||||
## Documentation
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ namespace BriarQueen.Data.Identifiers
|
||||
ChapterOneArrivalRoad,
|
||||
ChapterOneAshwickOutskirts,
|
||||
ChapterOneInsideBrokenDownCar,
|
||||
ChapterOneAshwickOutskirtsKeypad,
|
||||
}
|
||||
|
||||
public enum AssetItemKey
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace BriarQueen.Data.Identifiers
|
||||
None = 0,
|
||||
CarDoorOpening,
|
||||
ItemPickup,
|
||||
AshwickGateOpening
|
||||
}
|
||||
|
||||
public enum UIFXKey
|
||||
@@ -30,6 +31,22 @@ namespace BriarQueen.Data.Identifiers
|
||||
public enum VoiceKey
|
||||
{
|
||||
None = 0,
|
||||
|
||||
EmptyHands,
|
||||
CantUseItem,
|
||||
SomethingMissing,
|
||||
CarefulInteract,
|
||||
LooksImportant,
|
||||
WrongTool,
|
||||
CodexLocked,
|
||||
|
||||
Locked,
|
||||
CantGoThere,
|
||||
DoesntBelong,
|
||||
FireHot,
|
||||
AshwickHallowSign,
|
||||
FirstSkeleton,
|
||||
ClockTower,
|
||||
}
|
||||
|
||||
public static class AudioNameIdentifiers
|
||||
@@ -46,16 +63,17 @@ namespace BriarQueen.Data.Identifiers
|
||||
new ReadOnlyDictionary<SFXKey, string>(
|
||||
new Dictionary<SFXKey, string>
|
||||
{
|
||||
{ SFXKey.CarDoorOpening, "SFX:CarDoorOpening" },
|
||||
{ SFXKey.ItemPickup, "SFX:ItemPickup" },
|
||||
{ SFXKey.CarDoorOpening, "SFX:General:CarDoorOpening" },
|
||||
{ SFXKey.ItemPickup, "SFX:General:ItemPickup" },
|
||||
{ SFXKey.AshwickGateOpening, "SFX:Level:AshwickOutskirts:GateOpening" },
|
||||
});
|
||||
|
||||
public static readonly IReadOnlyDictionary<UIFXKey, string> UIFX =
|
||||
new ReadOnlyDictionary<UIFXKey, string>(
|
||||
new Dictionary<UIFXKey, string>
|
||||
{
|
||||
{ UIFXKey.AchievementUnlocked, "UIFX:AchievementUnlocked" },
|
||||
{ UIFXKey.CodexEntryUnlocked, "UIFX:CodexEntryUnlocked" },
|
||||
{ UIFXKey.AchievementUnlocked, "UIFX:General:AchievementUnlocked" },
|
||||
{ UIFXKey.CodexEntryUnlocked, "UIFX:General:CodexEntryUnlocked" },
|
||||
});
|
||||
|
||||
public static readonly IReadOnlyDictionary<AmbienceKey, string> Ambience =
|
||||
@@ -69,7 +87,21 @@ namespace BriarQueen.Data.Identifiers
|
||||
new ReadOnlyDictionary<VoiceKey, string>(
|
||||
new Dictionary<VoiceKey, string>
|
||||
{
|
||||
// Add voice mappings here
|
||||
{ VoiceKey.EmptyHands, "Voice:Item:EmptyHands" },
|
||||
{ VoiceKey.CantUseItem, "Voice:Item:CantUseItem" },
|
||||
{ VoiceKey.SomethingMissing, "Voice:Item:SomethingMissing" },
|
||||
{ VoiceKey.CarefulInteract, "Voice:Item:CarefulInteract" },
|
||||
{ VoiceKey.LooksImportant, "Voice:Item:LooksImportant" },
|
||||
{ VoiceKey.WrongTool, "Voice:Item:WrongTool" },
|
||||
{ VoiceKey.CodexLocked, "Voice:Item:CodexLocked" },
|
||||
|
||||
{ VoiceKey.Locked, "Voice:Environment:Locked" },
|
||||
{ VoiceKey.CantGoThere, "Voice:Environment:CantGoThere" },
|
||||
{ VoiceKey.DoesntBelong, "Voice:Environment:DoesntBelong" },
|
||||
{ VoiceKey.FireHot, "Voice:Environment:FireHot" },
|
||||
{ VoiceKey.AshwickHallowSign, "Voice:Environment:AshwickHallowSign" },
|
||||
{ VoiceKey.FirstSkeleton, "Voice:Environment:FirstSkeleton" },
|
||||
{ VoiceKey.ClockTower, "Voice:Environment:ClockTower" },
|
||||
});
|
||||
|
||||
public static string Get(MusicKey key)
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace BriarQueen.Data.Identifiers
|
||||
public enum ClueEntryID
|
||||
{
|
||||
None = 0,
|
||||
AshwickMarketGate,
|
||||
JasonsNote,
|
||||
}
|
||||
|
||||
public enum PhotoEntryID
|
||||
@@ -37,7 +37,7 @@ namespace BriarQueen.Data.Identifiers
|
||||
new ReadOnlyDictionary<ClueEntryID, string>(
|
||||
new Dictionary<ClueEntryID, string>
|
||||
{
|
||||
{ ClueEntryID.AshwickMarketGate, $"{PHOTO_PREFIX}:AshwickMarketGate" },
|
||||
{ ClueEntryID.JasonsNote, $"{PHOTO_PREFIX}:AshwickMarketGate" },
|
||||
});
|
||||
|
||||
public static readonly IReadOnlyDictionary<PhotoEntryID, string> Photos =
|
||||
|
||||
@@ -18,7 +18,6 @@ namespace BriarQueen.Data.Identifiers
|
||||
public enum LevelInteractKey
|
||||
{
|
||||
None = 0,
|
||||
MarketplaceFirstEntry
|
||||
}
|
||||
|
||||
public enum EnvironmentInteractKey
|
||||
@@ -29,11 +28,8 @@ namespace BriarQueen.Data.Identifiers
|
||||
DoesntBelong = 3,
|
||||
FireHot = 4,
|
||||
AshwickHallowSign = 5,
|
||||
AshwickRidgewayStatue = 6,
|
||||
AshwickBlockedRidgwayRoad = 7,
|
||||
AshwickRidgewaySkeleton = 8,
|
||||
AskwickMarketplaceSign = 9,
|
||||
FirstSkeleton = 10,
|
||||
ClockTower = 11,
|
||||
}
|
||||
|
||||
public enum UIInteractKey
|
||||
@@ -62,7 +58,6 @@ namespace BriarQueen.Data.Identifiers
|
||||
new ReadOnlyDictionary<LevelInteractKey, string>(
|
||||
new Dictionary<LevelInteractKey, string>
|
||||
{
|
||||
{ LevelInteractKey.MarketplaceFirstEntry, "I wonder if any of these are unlocked."}
|
||||
});
|
||||
|
||||
public static readonly IReadOnlyDictionary<EnvironmentInteractKey, string> EnvironmentInteractions =
|
||||
@@ -74,9 +69,8 @@ namespace BriarQueen.Data.Identifiers
|
||||
{ EnvironmentInteractKey.DoesntBelong, "This feels out of place." },
|
||||
{ EnvironmentInteractKey.FireHot, "Too hot to get close." },
|
||||
{ EnvironmentInteractKey.AshwickHallowSign, "Ashwick Hallow… Even the name feels like a warning."},
|
||||
{ EnvironmentInteractKey.AshwickRidgewayStatue, "Lovely sculpture. Mildly terrifying, but lovely."},
|
||||
{ EnvironmentInteractKey.AshwickBlockedRidgwayRoad, "Right. Closed road. Of course it is."},
|
||||
{ EnvironmentInteractKey.FirstSkeleton, "What the hell happened here?"}
|
||||
{ EnvironmentInteractKey.FirstSkeleton, "What the hell happened here?"},
|
||||
{ EnvironmentInteractKey.ClockTower, "Even from here, something about that clock tower feels wrong."}
|
||||
});
|
||||
|
||||
public static readonly IReadOnlyDictionary<UIInteractKey, string> UIInteractions =
|
||||
@@ -106,4 +100,4 @@ namespace BriarQueen.Data.Identifiers
|
||||
return UIInteractions.TryGetValue(key, out var value) ? value : string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,24 +11,45 @@ namespace BriarQueen.Data.Identifiers
|
||||
// TutorialTip = 2
|
||||
}
|
||||
|
||||
public static class SubtitleIdentifiers
|
||||
public readonly struct SubtitleEntry
|
||||
{
|
||||
public static readonly IReadOnlyDictionary<SubtitleKey, string> Subtitles =
|
||||
new ReadOnlyDictionary<SubtitleKey, string>(
|
||||
new Dictionary<SubtitleKey, string>
|
||||
{
|
||||
// { SubtitleKey.IntroLine, "Subtitle:IntroLine" },
|
||||
// { SubtitleKey.TutorialTip, "Subtitle:TutorialTip" }
|
||||
});
|
||||
|
||||
public static string Get(SubtitleKey key)
|
||||
public SubtitleEntry(string text, float preferredDurationSeconds = 0f)
|
||||
{
|
||||
return Subtitles.TryGetValue(key, out var value) ? value : string.Empty;
|
||||
Text = text;
|
||||
PreferredDurationSeconds = preferredDurationSeconds;
|
||||
}
|
||||
|
||||
public static IEnumerable<string> GetAll()
|
||||
public string Text { get; }
|
||||
public float PreferredDurationSeconds { get; }
|
||||
}
|
||||
|
||||
public static class SubtitleIdentifiers
|
||||
{
|
||||
public static readonly IReadOnlyDictionary<SubtitleKey, SubtitleEntry> Subtitles =
|
||||
new ReadOnlyDictionary<SubtitleKey, SubtitleEntry>(
|
||||
new Dictionary<SubtitleKey, SubtitleEntry>
|
||||
{
|
||||
// { SubtitleKey.IntroLine, new SubtitleEntry("Example subtitle.", 2.5f) },
|
||||
});
|
||||
|
||||
public static bool TryGet(SubtitleKey key, out SubtitleEntry entry)
|
||||
{
|
||||
return Subtitles.Values;
|
||||
return Subtitles.TryGetValue(key, out entry);
|
||||
}
|
||||
|
||||
public static string GetText(SubtitleKey key)
|
||||
{
|
||||
return Subtitles.TryGetValue(key, out var entry) ? entry.Text : string.Empty;
|
||||
}
|
||||
|
||||
public static float GetPreferredDuration(SubtitleKey key)
|
||||
{
|
||||
return Subtitles.TryGetValue(key, out var entry) ? entry.PreferredDurationSeconds : 0f;
|
||||
}
|
||||
|
||||
public static IEnumerable<SubtitleKey> GetAllKeys()
|
||||
{
|
||||
return Subtitles.Keys;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@ namespace BriarQueen.Data.Identifiers
|
||||
ReturnToPreviousLevel,
|
||||
UsingItemsTogether,
|
||||
HideHUD,
|
||||
ExitItems,
|
||||
MultipleUseItems,
|
||||
DarkRooms,
|
||||
Codex,
|
||||
HiddenItems,
|
||||
ResetPuzzles,
|
||||
LeavingPuzzles,
|
||||
Tools,
|
||||
ItemsAway,
|
||||
ItemCycling,
|
||||
@@ -25,7 +25,7 @@ namespace BriarQueen.Data.Identifiers
|
||||
{
|
||||
{
|
||||
TutorialPopupID.ReturnToPreviousLevel,
|
||||
"Click the bottom corners to return to the previous area."
|
||||
"Click the lower corners to return to the previous area."
|
||||
},
|
||||
{
|
||||
TutorialPopupID.UsingItemsTogether,
|
||||
@@ -35,13 +35,9 @@ namespace BriarQueen.Data.Identifiers
|
||||
TutorialPopupID.HideHUD,
|
||||
"Press '{Hide_HUD}' to hide the HUD."
|
||||
},
|
||||
{
|
||||
TutorialPopupID.ExitItems,
|
||||
"Press '{Right_Click}' to exit the current interaction."
|
||||
},
|
||||
{
|
||||
TutorialPopupID.MultipleUseItems,
|
||||
"Some items can be used multiple times, but they may wear out."
|
||||
"Some items can be used more than once, but they may wear out."
|
||||
},
|
||||
{
|
||||
TutorialPopupID.DarkRooms,
|
||||
@@ -49,7 +45,7 @@ namespace BriarQueen.Data.Identifiers
|
||||
},
|
||||
{
|
||||
TutorialPopupID.Codex,
|
||||
"The Codex is used to collect any documents you encounter. Press '{Codex}' to open it."
|
||||
"Documents you find are stored in the Codex. Press '{Codex}' to open it."
|
||||
},
|
||||
{
|
||||
TutorialPopupID.HiddenItems,
|
||||
@@ -57,26 +53,31 @@ namespace BriarQueen.Data.Identifiers
|
||||
},
|
||||
{
|
||||
TutorialPopupID.ResetPuzzles,
|
||||
"Some puzzles can be reset."
|
||||
"Some puzzles can be reset if you get stuck."
|
||||
},
|
||||
{
|
||||
TutorialPopupID.LeavingPuzzles,
|
||||
"When you leave a puzzle, your progress is saved."
|
||||
},
|
||||
|
||||
{
|
||||
TutorialPopupID.Tools,
|
||||
"You'll find tools as you explore. Try them on different objects. Press '{Show_Tools}' to view your tools."
|
||||
"You'll find tools as you explore. Try them on different objects. Press '{Show_Tools}' to open your tools."
|
||||
},
|
||||
{
|
||||
TutorialPopupID.ItemsAway,
|
||||
"Press '{Right_Click}' to put away the current item."
|
||||
"Press '{Right_Click}' to put away the selected item."
|
||||
},
|
||||
{
|
||||
TutorialPopupID.ItemCycling,
|
||||
"Press '{Previous_Item}' or '{Next_Item}' to cycle through your backpack."
|
||||
"Press '{Previous_Item}' or '{Next_Item}' to cycle through the items in your backpack."
|
||||
},
|
||||
{
|
||||
TutorialPopupID.ToolCycling,
|
||||
"Press '{Previous_Tool}' or '{Next_Tool}' to cycle through your tools."
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
public static IEnumerable<string> GetAllTexts() => AllPopups.Values;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,9 @@ namespace BriarQueen.Framework.Effects
|
||||
|
||||
[SerializeField]
|
||||
private bool _randomizeFlickerOffset = true;
|
||||
[SerializeField]
|
||||
|
||||
private bool _useStartingValues;
|
||||
|
||||
[Header("Tween")]
|
||||
[SerializeField]
|
||||
@@ -81,8 +84,12 @@ namespace BriarQueen.Framework.Effects
|
||||
}
|
||||
|
||||
CreateRuntimeMaterial();
|
||||
SetLightColor(_startingColor);
|
||||
SetIntensity(_startingIntensity);
|
||||
|
||||
if(_useStartingValues)
|
||||
{
|
||||
SetLightColor(_startingColor);
|
||||
SetIntensity(_startingIntensity);
|
||||
}
|
||||
|
||||
if (_randomizeFlickerOffset && _runtimeMaterial != null)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
using BriarQueen.Data.Identifiers;
|
||||
using BriarQueen.Framework.Events.System;
|
||||
|
||||
namespace BriarQueen.Framework.Events.Audio
|
||||
{
|
||||
public record VoicePlaybackFinishedEvent(
|
||||
VoiceKey VoiceKey,
|
||||
SubtitleKey SubtitleKey) : IEvent;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a5c701b2f1b56482cb423352feaecd6b
|
||||
@@ -0,0 +1,10 @@
|
||||
using BriarQueen.Data.Identifiers;
|
||||
using BriarQueen.Framework.Events.System;
|
||||
|
||||
namespace BriarQueen.Framework.Events.Audio
|
||||
{
|
||||
public record VoicePlaybackStartedEvent(
|
||||
VoiceKey VoiceKey,
|
||||
SubtitleKey SubtitleKey,
|
||||
float ClipLengthSeconds) : IEvent;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7b2afa1c54b63413293f433a9490a998
|
||||
@@ -0,0 +1,6 @@
|
||||
using BriarQueen.Framework.Events.System;
|
||||
|
||||
namespace BriarQueen.Framework.Events.UI
|
||||
{
|
||||
public record SubtitleDisplayChangedEvent(string Text, bool Visible) : IEvent;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d606965a42b7a4bc29e67528e42120e2
|
||||
@@ -45,7 +45,8 @@ namespace BriarQueen.Framework.Managers.Audio
|
||||
private AudioSource _musicSourceB;
|
||||
private AudioSource _voiceSource;
|
||||
|
||||
private string _activeVoiceSubtitleId;
|
||||
private VoiceKey _activeVoiceKey = VoiceKey.None;
|
||||
private SubtitleKey _activeSubtitleKey = SubtitleKey.None;
|
||||
private AudioFileSo _currentMusicTrack;
|
||||
|
||||
private CancellationTokenSource _musicDuckCts;
|
||||
@@ -140,7 +141,8 @@ namespace BriarQueen.Framework.Managers.Audio
|
||||
_musicSourceB = null;
|
||||
_voiceSource = null;
|
||||
_currentMusicTrack = null;
|
||||
_activeVoiceSubtitleId = null;
|
||||
_activeVoiceKey = VoiceKey.None;
|
||||
_activeSubtitleKey = SubtitleKey.None;
|
||||
_voiceFinishedPublished = false;
|
||||
Initialized = false;
|
||||
}
|
||||
@@ -205,8 +207,7 @@ namespace BriarQueen.Framework.Managers.Audio
|
||||
if (_audioMixer == null || string.IsNullOrWhiteSpace(parameter))
|
||||
return;
|
||||
|
||||
if (!_baseDb.TryGetValue(parameter, out var baseDb))
|
||||
baseDb = 0f;
|
||||
var baseDb = _baseDb.GetValueOrDefault(parameter, 0f);
|
||||
|
||||
var effective = baseDb;
|
||||
|
||||
@@ -340,10 +341,14 @@ namespace BriarQueen.Framework.Managers.Audio
|
||||
_voiceCts = new CancellationTokenSource();
|
||||
var token = _voiceCts.Token;
|
||||
|
||||
_activeVoiceSubtitleId = SubtitleIdentifiers.Get(audioData.MatchingSubtitleID);
|
||||
_activeVoiceKey = audioData.VoiceKey;
|
||||
_activeSubtitleKey = audioData.MatchingSubtitleID;
|
||||
_voiceFinishedPublished = false;
|
||||
|
||||
_eventCoordinator.Publish(new VoiceLineStartedEvent(_activeVoiceSubtitleId));
|
||||
_eventCoordinator.Publish(new VoicePlaybackStartedEvent(
|
||||
_activeVoiceKey,
|
||||
_activeSubtitleKey,
|
||||
audioData.Clip.length));
|
||||
|
||||
_voiceSource.clip = audioData.Clip;
|
||||
_voiceSource.pitch = audioData.Pitch;
|
||||
@@ -369,11 +374,16 @@ namespace BriarQueen.Framework.Managers.Audio
|
||||
{
|
||||
if (_voiceFinishedPublished) return;
|
||||
|
||||
if (!string.IsNullOrEmpty(_activeVoiceSubtitleId))
|
||||
_eventCoordinator.Publish(new VoiceLineFinishedEvent(_activeVoiceSubtitleId));
|
||||
if (_activeVoiceKey != VoiceKey.None || _activeSubtitleKey != SubtitleKey.None)
|
||||
{
|
||||
_eventCoordinator.Publish(new VoicePlaybackFinishedEvent(
|
||||
_activeVoiceKey,
|
||||
_activeSubtitleKey));
|
||||
}
|
||||
|
||||
_voiceFinishedPublished = true;
|
||||
_activeVoiceSubtitleId = null;
|
||||
_activeVoiceKey = VoiceKey.None;
|
||||
_activeSubtitleKey = SubtitleKey.None;
|
||||
}
|
||||
|
||||
public void StopVoice()
|
||||
@@ -666,4 +676,4 @@ namespace BriarQueen.Framework.Managers.Audio
|
||||
public float StartedAtUnscaled;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -449,13 +449,12 @@ namespace BriarQueen.Framework.Managers.Input
|
||||
private void OnPause(InputAction.CallbackContext ctx)
|
||||
{
|
||||
var isMainMenu = _gameService != null && _gameService.IsMainMenuSceneLoaded;
|
||||
if (isMainMenu || _isAnyUIOpen)
|
||||
if (isMainMenu)
|
||||
{
|
||||
_eventCoordinator?.PublishImmediate(new UIBackRequestedEvent());
|
||||
return;
|
||||
}
|
||||
|
||||
_isPaused = true;
|
||||
_eventCoordinator?.Publish(new PauseButtonClickedEvent());
|
||||
}
|
||||
|
||||
|
||||
@@ -163,6 +163,22 @@ namespace BriarQueen.Framework.Managers.Interaction
|
||||
Debug.Log($"[InteractManager] SetExclusiveRaycaster set to {raycaster.gameObject.name}.");
|
||||
}
|
||||
|
||||
public void ReleaseExclusiveRaycaster(GraphicRaycaster raycaster)
|
||||
{
|
||||
if (raycaster == null)
|
||||
return;
|
||||
|
||||
if (_exclusiveRaycaster != raycaster)
|
||||
return;
|
||||
|
||||
_exclusiveRaycaster = null;
|
||||
|
||||
if (_currentHovered != null)
|
||||
ClearHover().Forget();
|
||||
|
||||
Debug.Log($"[InteractManager] Released exclusive raycaster {raycaster.gameObject.name}.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear exclusive mode and return to using all registered raycasters.
|
||||
/// </summary>
|
||||
@@ -453,4 +469,4 @@ namespace BriarQueen.Framework.Managers.Interaction
|
||||
_selectedItem = evt.Item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,15 @@ namespace BriarQueen.Framework.Managers.Player.Data.Codex
|
||||
[Header("Codex")]
|
||||
[SerializeField]
|
||||
private CodexEntrySo _codexEntry;
|
||||
|
||||
[SerializeField]
|
||||
private bool _removeTrigger;
|
||||
|
||||
[Header("Events")]
|
||||
[SerializeField]
|
||||
private SFXKey _soundEffect;
|
||||
[SerializeField]
|
||||
private VoiceKey _voiceLine;
|
||||
|
||||
public override UICursorService.CursorStyle ApplicableCursorStyle => UICursorService.CursorStyle.Inspect;
|
||||
|
||||
public override string InteractableName =>
|
||||
@@ -40,12 +45,23 @@ namespace BriarQueen.Framework.Managers.Player.Data.Codex
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
PlayerManager.UnlockCodexEntry(_codexEntry);
|
||||
|
||||
if (_removeTrigger)
|
||||
{
|
||||
await Remove();
|
||||
}
|
||||
|
||||
if (_soundEffect != SFXKey.None)
|
||||
{
|
||||
AudioManager.Play(AudioNameIdentifiers.Get(_soundEffect));
|
||||
}
|
||||
|
||||
if (_voiceLine != VoiceKey.None)
|
||||
{
|
||||
AudioManager.Play(AudioNameIdentifiers.Get(_voiceLine));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateSaveGameOnRemoval()
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace BriarQueen.Framework.Managers.UI.Base
|
||||
{
|
||||
public interface IUIOverlayHost
|
||||
{
|
||||
bool CanSuspendFor(WindowType incomingWindowType);
|
||||
UniTask SuspendForOverlay();
|
||||
UniTask ResumeFromOverlay();
|
||||
}
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using PrimeTween;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BriarQueen.Framework.Managers.UI.Base
|
||||
{
|
||||
public enum UIPauseBehavior
|
||||
{
|
||||
TreatAsBackRequest,
|
||||
OpenPauseOverlay
|
||||
}
|
||||
|
||||
public interface IUIWindow
|
||||
{
|
||||
UniTask Show();
|
||||
UniTask Hide();
|
||||
|
||||
WindowType WindowType { get; }
|
||||
|
||||
UIPauseBehavior PauseBehavior { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace BriarQueen.Framework.Managers.UI.Base
|
||||
{
|
||||
PauseMenuWindow,
|
||||
SettingsWindow,
|
||||
CodexWindow
|
||||
CodexWindow,
|
||||
AshwickGateKeypadWindow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,12 +41,14 @@ namespace BriarQueen.Framework.Managers.UI
|
||||
|
||||
public bool Initialized { get; private set; }
|
||||
|
||||
private sealed record OverlayResumeContext(WindowType OverlayWindowType, IUIOverlayHost Host);
|
||||
|
||||
private IHud _hudContainer;
|
||||
private IPopup _infoPopup;
|
||||
private IPopup _tutorialPopup;
|
||||
private IScreenFader _screenFader;
|
||||
private IUIOverlayHost _mainMenuOverlayHost;
|
||||
private IUIOverlayHost _activeSettingsOverlayHost;
|
||||
private readonly Stack<OverlayResumeContext> _overlayResumeStack = new();
|
||||
|
||||
[Inject]
|
||||
public UIManager(
|
||||
@@ -127,6 +129,18 @@ namespace BriarQueen.Framework.Managers.UI
|
||||
window.Hide().Forget();
|
||||
}
|
||||
|
||||
public void UnregisterWindow(IUIWindow window)
|
||||
{
|
||||
if (window == null)
|
||||
return;
|
||||
|
||||
if (_windows.TryGetValue(window.WindowType, out var registered) && ReferenceEquals(registered, window))
|
||||
_windows.Remove(window.WindowType);
|
||||
|
||||
if (window is IUIOverlayHost overlayHost)
|
||||
RemoveOverlayResumeContextsForHost(overlayHost);
|
||||
}
|
||||
|
||||
public void RegisterHUD(IHud hudContainer)
|
||||
{
|
||||
_hudContainer = hudContainer;
|
||||
@@ -169,9 +183,7 @@ namespace BriarQueen.Framework.Managers.UI
|
||||
if (!ReferenceEquals(_mainMenuOverlayHost, host))
|
||||
return;
|
||||
|
||||
if (ReferenceEquals(_activeSettingsOverlayHost, host))
|
||||
_activeSettingsOverlayHost = null;
|
||||
|
||||
RemoveOverlayResumeContextsForHost(host);
|
||||
_mainMenuOverlayHost = null;
|
||||
}
|
||||
|
||||
@@ -186,6 +198,51 @@ namespace BriarQueen.Framework.Managers.UI
|
||||
return target != null && _windowStack.Contains(target);
|
||||
}
|
||||
|
||||
private void RemoveOverlayResumeContextsForHost(IUIOverlayHost host)
|
||||
{
|
||||
if (host == null || _overlayResumeStack.Count == 0)
|
||||
return;
|
||||
|
||||
var contextsToKeep = new List<OverlayResumeContext>();
|
||||
foreach (var context in _overlayResumeStack)
|
||||
{
|
||||
if (!ReferenceEquals(context.Host, host))
|
||||
contextsToKeep.Add(context);
|
||||
}
|
||||
|
||||
_overlayResumeStack.Clear();
|
||||
|
||||
for (var i = contextsToKeep.Count - 1; i >= 0; i--)
|
||||
_overlayResumeStack.Push(contextsToKeep[i]);
|
||||
}
|
||||
|
||||
private async UniTask<bool> TrySuspendActiveWindowFor(WindowType incomingWindowType)
|
||||
{
|
||||
if (ActiveWindow is IUIOverlayHost overlayHost &&
|
||||
overlayHost.CanSuspendFor(incomingWindowType))
|
||||
{
|
||||
await overlayHost.SuspendForOverlay();
|
||||
_overlayResumeStack.Push(new OverlayResumeContext(incomingWindowType, overlayHost));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async UniTask RestoreUnderlyingUi(WindowType closedWindowType)
|
||||
{
|
||||
if (_overlayResumeStack.Count > 0 &&
|
||||
_overlayResumeStack.Peek().OverlayWindowType == closedWindowType)
|
||||
{
|
||||
var resumeContext = _overlayResumeStack.Pop();
|
||||
await resumeContext.Host.ResumeFromOverlay();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ActiveWindow != null)
|
||||
await ActiveWindow.Show();
|
||||
}
|
||||
|
||||
private async UniTask ApplyHudVisibility(bool visible)
|
||||
{
|
||||
if (_disposed || _hudContainer == null)
|
||||
@@ -206,13 +263,25 @@ namespace BriarQueen.Framework.Managers.UI
|
||||
|
||||
private void OnPauseClickReceived(PauseButtonClickedEvent _)
|
||||
{
|
||||
if (_windowStack.Count > 0)
|
||||
if (ActiveWindow == null)
|
||||
{
|
||||
OpenWindow(WindowType.PauseMenuWindow);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ActiveWindow.WindowType == WindowType.PauseMenuWindow)
|
||||
{
|
||||
TryHandleBackRequest();
|
||||
return;
|
||||
}
|
||||
|
||||
OpenWindow(WindowType.PauseMenuWindow);
|
||||
if (ActiveWindow.PauseBehavior == UIPauseBehavior.OpenPauseOverlay)
|
||||
{
|
||||
OpenWindow(WindowType.PauseMenuWindow);
|
||||
return;
|
||||
}
|
||||
|
||||
TryHandleBackRequest();
|
||||
}
|
||||
|
||||
private void OnBackRequested(UIBackRequestedEvent _)
|
||||
@@ -350,28 +419,22 @@ namespace BriarQueen.Framework.Managers.UI
|
||||
if (_windowStack.Contains(window))
|
||||
return;
|
||||
|
||||
_activeSettingsOverlayHost = null;
|
||||
var suspended = false;
|
||||
|
||||
var openingSettingsOverPause =
|
||||
source == SettingsOpenSource.PauseMenu &&
|
||||
ActiveWindow?.WindowType == WindowType.PauseMenuWindow &&
|
||||
ActiveWindow is IUIOverlayHost;
|
||||
|
||||
var openingSettingsOverMainMenu =
|
||||
source == SettingsOpenSource.MainMenu &&
|
||||
_mainMenuOverlayHost != null;
|
||||
|
||||
if (openingSettingsOverPause)
|
||||
if (source == SettingsOpenSource.MainMenu &&
|
||||
_mainMenuOverlayHost != null &&
|
||||
_mainMenuOverlayHost.CanSuspendFor(WindowType.SettingsWindow))
|
||||
{
|
||||
_activeSettingsOverlayHost = (IUIOverlayHost)ActiveWindow;
|
||||
await _activeSettingsOverlayHost.SuspendForOverlay();
|
||||
await _mainMenuOverlayHost.SuspendForOverlay();
|
||||
_overlayResumeStack.Push(new OverlayResumeContext(WindowType.SettingsWindow, _mainMenuOverlayHost));
|
||||
suspended = true;
|
||||
}
|
||||
else if (openingSettingsOverMainMenu)
|
||||
else
|
||||
{
|
||||
_activeSettingsOverlayHost = _mainMenuOverlayHost;
|
||||
await _activeSettingsOverlayHost.SuspendForOverlay();
|
||||
suspended = await TrySuspendActiveWindowFor(WindowType.SettingsWindow);
|
||||
}
|
||||
else if (ActiveWindow != null)
|
||||
|
||||
if (!suspended && ActiveWindow != null)
|
||||
{
|
||||
await ActiveWindow.Hide();
|
||||
}
|
||||
@@ -407,7 +470,9 @@ namespace BriarQueen.Framework.Managers.UI
|
||||
if (_windowStack.Contains(window))
|
||||
return;
|
||||
|
||||
if (ActiveWindow != null)
|
||||
var suspended = await TrySuspendActiveWindowFor(windowType);
|
||||
|
||||
if (!suspended && ActiveWindow != null)
|
||||
await ActiveWindow.Hide();
|
||||
|
||||
_windowStack.Push(window);
|
||||
@@ -452,15 +517,7 @@ namespace BriarQueen.Framework.Managers.UI
|
||||
break;
|
||||
}
|
||||
|
||||
if (target.WindowType == WindowType.SettingsWindow && _activeSettingsOverlayHost != null)
|
||||
{
|
||||
await _activeSettingsOverlayHost.ResumeFromOverlay();
|
||||
_activeSettingsOverlayHost = null;
|
||||
}
|
||||
else if (ActiveWindow != null)
|
||||
{
|
||||
await ActiveWindow.Show();
|
||||
}
|
||||
await RestoreUnderlyingUi(target.WindowType);
|
||||
|
||||
NotifyUIStackChanged();
|
||||
}
|
||||
@@ -506,17 +563,8 @@ namespace BriarQueen.Framework.Managers.UI
|
||||
NotifyWindowStateChanged(top.WindowType, false);
|
||||
}
|
||||
|
||||
if (top != null &&
|
||||
top.WindowType == WindowType.SettingsWindow &&
|
||||
_activeSettingsOverlayHost != null)
|
||||
{
|
||||
await _activeSettingsOverlayHost.ResumeFromOverlay();
|
||||
_activeSettingsOverlayHost = null;
|
||||
}
|
||||
else if (ActiveWindow != null)
|
||||
{
|
||||
await ActiveWindow.Show();
|
||||
}
|
||||
if (top != null)
|
||||
await RestoreUnderlyingUi(top.WindowType);
|
||||
|
||||
NotifyUIStackChanged();
|
||||
}
|
||||
@@ -536,17 +584,12 @@ namespace BriarQueen.Framework.Managers.UI
|
||||
await _windowTransitionGate.WaitAsync();
|
||||
try
|
||||
{
|
||||
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();
|
||||
@@ -557,18 +600,7 @@ namespace BriarQueen.Framework.Managers.UI
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldResumeSettingsHost)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _activeSettingsOverlayHost.ResumeFromOverlay();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
_activeSettingsOverlayHost = null;
|
||||
_overlayResumeStack.Clear();
|
||||
|
||||
if (_tutorialPopup != null)
|
||||
{
|
||||
@@ -621,7 +653,7 @@ namespace BriarQueen.Framework.Managers.UI
|
||||
faderComponent.gameObject.SetActive(false);
|
||||
|
||||
_windowStack.Clear();
|
||||
_activeSettingsOverlayHost = null;
|
||||
_overlayResumeStack.Clear();
|
||||
_mainMenuOverlayHost = null;
|
||||
}
|
||||
|
||||
|
||||
8
Assets/Scripts/Framework/Services/Subtitles.meta
Normal file
8
Assets/Scripts/Framework/Services/Subtitles.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1973a703eae6841b6b53b6f33b951713
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
148
Assets/Scripts/Framework/Services/Subtitles/SubtitleService.cs
Normal file
148
Assets/Scripts/Framework/Services/Subtitles/SubtitleService.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using BriarQueen.Data.Identifiers;
|
||||
using BriarQueen.Framework.Coordinators.Events;
|
||||
using BriarQueen.Framework.Events.Audio;
|
||||
using BriarQueen.Framework.Events.UI;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using VContainer;
|
||||
|
||||
namespace BriarQueen.Framework.Services.Subtitles
|
||||
{
|
||||
public class SubtitleService : IDisposable
|
||||
{
|
||||
private readonly EventCoordinator _eventCoordinator;
|
||||
|
||||
private CancellationTokenSource _subtitleCts;
|
||||
private SubtitleKey _activeSubtitleKey = SubtitleKey.None;
|
||||
private string _currentText = string.Empty;
|
||||
|
||||
public bool IsVisible => _activeSubtitleKey != SubtitleKey.None && !string.IsNullOrWhiteSpace(_currentText);
|
||||
public string CurrentText => _currentText;
|
||||
|
||||
[Inject]
|
||||
public SubtitleService(EventCoordinator eventCoordinator)
|
||||
{
|
||||
_eventCoordinator = eventCoordinator;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_eventCoordinator.Subscribe<VoicePlaybackStartedEvent>(OnVoicePlaybackStarted);
|
||||
_eventCoordinator.Subscribe<VoicePlaybackFinishedEvent>(OnVoicePlaybackFinished);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_eventCoordinator.Unsubscribe<VoicePlaybackStartedEvent>(OnVoicePlaybackStarted);
|
||||
_eventCoordinator.Unsubscribe<VoicePlaybackFinishedEvent>(OnVoicePlaybackFinished);
|
||||
CancelCurrentSubtitle();
|
||||
ClearSubtitle();
|
||||
}
|
||||
|
||||
public void PlayScriptedSubtitle(SubtitleKey subtitleKey, float durationOverrideSeconds = 0f)
|
||||
{
|
||||
if (subtitleKey == SubtitleKey.None)
|
||||
{
|
||||
ClearScriptedSubtitle();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SubtitleIdentifiers.TryGet(subtitleKey, out var entry) || string.IsNullOrWhiteSpace(entry.Text))
|
||||
return;
|
||||
|
||||
var duration = durationOverrideSeconds > 0f
|
||||
? durationOverrideSeconds
|
||||
: entry.PreferredDurationSeconds;
|
||||
|
||||
ShowSubtitle(subtitleKey, entry.Text, duration).Forget();
|
||||
}
|
||||
|
||||
public void ClearScriptedSubtitle()
|
||||
{
|
||||
CancelCurrentSubtitle();
|
||||
ClearSubtitle();
|
||||
}
|
||||
|
||||
private void OnVoicePlaybackStarted(VoicePlaybackStartedEvent evt)
|
||||
{
|
||||
if (evt.SubtitleKey == SubtitleKey.None)
|
||||
return;
|
||||
|
||||
if (!SubtitleIdentifiers.TryGet(evt.SubtitleKey, out var entry) || string.IsNullOrWhiteSpace(entry.Text))
|
||||
return;
|
||||
|
||||
var duration = entry.PreferredDurationSeconds > 0f
|
||||
? entry.PreferredDurationSeconds
|
||||
: evt.ClipLengthSeconds;
|
||||
|
||||
ShowSubtitle(evt.SubtitleKey, entry.Text, duration).Forget();
|
||||
}
|
||||
|
||||
private void OnVoicePlaybackFinished(VoicePlaybackFinishedEvent evt)
|
||||
{
|
||||
if (evt.SubtitleKey == SubtitleKey.None)
|
||||
return;
|
||||
|
||||
if (_activeSubtitleKey != evt.SubtitleKey)
|
||||
return;
|
||||
|
||||
CancelCurrentSubtitle();
|
||||
ClearSubtitle();
|
||||
}
|
||||
|
||||
private async UniTaskVoid ShowSubtitle(SubtitleKey subtitleKey, string text, float durationSeconds)
|
||||
{
|
||||
CancelCurrentSubtitle();
|
||||
|
||||
_activeSubtitleKey = subtitleKey;
|
||||
_currentText = text;
|
||||
_subtitleCts = new CancellationTokenSource();
|
||||
|
||||
_eventCoordinator.PublishImmediate(new SubtitleDisplayChangedEvent(text, true));
|
||||
|
||||
var safeDuration = Math.Max(0f, durationSeconds);
|
||||
if (safeDuration <= 0f)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
await UniTask.Delay(
|
||||
TimeSpan.FromSeconds(safeDuration),
|
||||
DelayType.UnscaledDeltaTime,
|
||||
cancellationToken: _subtitleCts.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_activeSubtitleKey == subtitleKey)
|
||||
ClearSubtitle();
|
||||
}
|
||||
|
||||
private void CancelCurrentSubtitle()
|
||||
{
|
||||
if (_subtitleCts == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
_subtitleCts.Cancel();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
_subtitleCts.Dispose();
|
||||
_subtitleCts = null;
|
||||
}
|
||||
|
||||
private void ClearSubtitle()
|
||||
{
|
||||
_activeSubtitleKey = SubtitleKey.None;
|
||||
_currentText = string.Empty;
|
||||
_eventCoordinator.PublishImmediate(new SubtitleDisplayChangedEvent(string.Empty, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 61b6e034a002e4319a76ebe6bd90f50f
|
||||
@@ -92,15 +92,9 @@ namespace BriarQueen.Game.Items.Environment.General.Book
|
||||
_bookInterface.CanvasGroup.blocksRaycasts = true;
|
||||
_bookInterface.CanvasGroup.interactable = true;
|
||||
|
||||
ShowTutorialIfNeeded();
|
||||
UnlockCodexEntry();
|
||||
}
|
||||
|
||||
private void ShowTutorialIfNeeded()
|
||||
{
|
||||
TutorialService.DisplayTutorial(TutorialPopupID.ExitItems);
|
||||
}
|
||||
|
||||
private void UnlockCodexEntry()
|
||||
{
|
||||
PlayerManager.UnlockCodexEntry(CodexEntryIDs.Get(_documentEntryID));
|
||||
|
||||
@@ -16,6 +16,13 @@ namespace BriarQueen.Game.Items.Environment.General
|
||||
|
||||
[SerializeField]
|
||||
private bool _removeTrigger;
|
||||
|
||||
[Header("Events")]
|
||||
[SerializeField]
|
||||
private SFXKey _soundEffect;
|
||||
[SerializeField]
|
||||
private VoiceKey _voiceLine;
|
||||
|
||||
|
||||
public override UICursorService.CursorStyle ApplicableCursorStyle => UICursorService.CursorStyle.Inspect;
|
||||
public override string InteractableName => InteractableTooltip;
|
||||
@@ -29,6 +36,16 @@ namespace BriarQueen.Game.Items.Environment.General
|
||||
|
||||
if (_removeTrigger)
|
||||
await Remove();
|
||||
|
||||
if (_soundEffect != SFXKey.None)
|
||||
{
|
||||
AudioManager.Play(AudioNameIdentifiers.Get(_soundEffect));
|
||||
}
|
||||
|
||||
if (_voiceLine != VoiceKey.None)
|
||||
{
|
||||
AudioManager.Play(AudioNameIdentifiers.Get(_voiceLine));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,6 @@ namespace BriarQueen.Game.Levels.ChapterOne.Ashwick
|
||||
[SerializeField]
|
||||
private AshwickGate _ashwickGate;
|
||||
|
||||
[SerializeField]
|
||||
private TransitionZone _keypadZone;
|
||||
|
||||
[SerializeField]
|
||||
private TransitionZone _nextLevelZone;
|
||||
|
||||
@@ -36,22 +33,17 @@ namespace BriarQueen.Game.Levels.ChapterOne.Ashwick
|
||||
_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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using BriarQueen.Data.Identifiers;
|
||||
using BriarQueen.Data.IO.Saves;
|
||||
using BriarQueen.Framework.Events.Save;
|
||||
using BriarQueen.Framework.Events.UI;
|
||||
using BriarQueen.Framework.Managers.Levels.Data;
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace BriarQueen.Game.Levels.ChapterOne.Ashwick
|
||||
{
|
||||
public class Marketplace : BaseLevel
|
||||
{
|
||||
protected override UniTask PostActivateInternal()
|
||||
{
|
||||
if (SaveManager.GetLevelFlag(LevelFlag.MarketplaceFirstEntry))
|
||||
return UniTask.CompletedTask;
|
||||
|
||||
EventCoordinator.Publish(new DisplayInteractEvent(InteractEventIDs.Get(LevelInteractKey.MarketplaceFirstEntry)));
|
||||
|
||||
SaveManager.SetLevelFlag(LevelFlag.MarketplaceFirstEntry, true);
|
||||
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2d9beb847a184e2e897942fff18a396e
|
||||
timeCreated: 1778696777
|
||||
@@ -10,6 +10,7 @@ using BriarQueen.Framework.Managers.Levels;
|
||||
using BriarQueen.Framework.Managers.Player;
|
||||
using BriarQueen.Framework.Managers.UI;
|
||||
using BriarQueen.Framework.Services.Settings;
|
||||
using BriarQueen.Framework.Services.Subtitles;
|
||||
using BriarQueen.Framework.Services.Tutorials;
|
||||
using BriarQueen.Game.Cinematics;
|
||||
using Cysharp.Threading.Tasks;
|
||||
@@ -30,6 +31,7 @@ namespace BriarQueen.Game.Misc
|
||||
private readonly SettingsService _settingsService;
|
||||
private readonly SplashScreens _splashScreens;
|
||||
private readonly SteamManager _steamManager;
|
||||
private readonly SubtitleService _subtitleService;
|
||||
private readonly TutorialService _tutorialService;
|
||||
private readonly UIManager _uiManager;
|
||||
|
||||
@@ -45,6 +47,7 @@ namespace BriarQueen.Game.Misc
|
||||
SettingsService settingsService,
|
||||
SteamManager steamManager,
|
||||
SplashScreens splashScreens,
|
||||
SubtitleService subtitleService,
|
||||
TutorialService tutorialService)
|
||||
{
|
||||
_audioManager = audioManager;
|
||||
@@ -58,6 +61,8 @@ namespace BriarQueen.Game.Misc
|
||||
_settingsService = settingsService;
|
||||
_steamManager = steamManager;
|
||||
_splashScreens = splashScreens;
|
||||
_subtitleService = subtitleService;
|
||||
_tutorialService = tutorialService;
|
||||
}
|
||||
|
||||
public async UniTask StartAsync(CancellationToken cancellationToken)
|
||||
@@ -67,6 +72,9 @@ namespace BriarQueen.Game.Misc
|
||||
Debug.Log("[Bootstrap] Audio...");
|
||||
_audioManager.Initialize();
|
||||
|
||||
Debug.Log("[Bootstrap] Subtitles...");
|
||||
_subtitleService.Initialize();
|
||||
|
||||
Debug.Log("[Bootstrap] Settings...");
|
||||
await _settingsService.InitializeAsync();
|
||||
|
||||
@@ -99,4 +107,4 @@ namespace BriarQueen.Game.Misc
|
||||
Debug.Log("[Bootstrap] Called SplashScreens.Play()");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using BriarQueen.Framework.Managers.Levels.Data;
|
||||
using BriarQueen.Framework.Managers.Player.Data;
|
||||
using BriarQueen.Framework.Managers.UI;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -7,10 +8,13 @@ namespace BriarQueen.Game.Puzzles.ChapterOne.AshwickHallow
|
||||
{
|
||||
public class AshwickGate : BaseItem
|
||||
{
|
||||
[Header("Keypad")]
|
||||
[SerializeField] private AshwickGateKeypadPuzzle _keypadPuzzle;
|
||||
|
||||
public override string InteractableName => "Iron Gate";
|
||||
|
||||
public override UICursorService.CursorStyle ApplicableCursorStyle => UICursorService.CursorStyle.Interact;
|
||||
|
||||
public override UniTask OnInteract(ItemDataSo item = null)
|
||||
{
|
||||
if (!CheckEmptyHands())
|
||||
|
||||
@@ -5,7 +5,10 @@ using BriarQueen.Data.IO.Saves;
|
||||
using BriarQueen.Framework.Effects;
|
||||
using BriarQueen.Framework.Events.Save;
|
||||
using BriarQueen.Framework.Managers.Interaction;
|
||||
using BriarQueen.Framework.Managers.UI;
|
||||
using BriarQueen.Framework.Managers.UI.Base;
|
||||
using BriarQueen.Framework.Services.Puzzles.Base;
|
||||
using BriarQueen.Framework.Services.Tutorials;
|
||||
using BriarQueen.Game.Levels.ChapterOne.Ashwick;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using MemoryPack;
|
||||
@@ -24,7 +27,7 @@ namespace BriarQueen.Game.Puzzles.ChapterOne.AshwickHallow
|
||||
public string Digits;
|
||||
}
|
||||
|
||||
public class AshwickGateKeypadPuzzle : BasePuzzle, IPuzzleStateful
|
||||
public class AshwickGateKeypadPuzzle : BasePuzzle, IPuzzleStateful, IUIWindow, IUIBackHandler, IUIOverlayHost
|
||||
{
|
||||
private const string CorrectCode = "312";
|
||||
private const int RequiredDigits = 3;
|
||||
@@ -63,14 +66,25 @@ namespace BriarQueen.Game.Puzzles.ChapterOne.AshwickHallow
|
||||
private bool _isEvaluating;
|
||||
private bool _isOpen;
|
||||
private bool _raycasterRegistered;
|
||||
private bool _skipSaveOnHide;
|
||||
|
||||
private TutorialService _tutorialService;
|
||||
private UIManager _uiManager;
|
||||
|
||||
public override string PuzzleID => PuzzleIdentifiers.AllPuzzles[PuzzleKey.AshwickMarketGate];
|
||||
public bool IsCompleted => _isCompleted || SaveManager.GetLevelFlag(LevelFlag.AshwickGateOpen);
|
||||
public WindowType WindowType => WindowType.AshwickGateKeypadWindow;
|
||||
public UIPauseBehavior PauseBehavior => UIPauseBehavior.OpenPauseOverlay;
|
||||
|
||||
[Inject]
|
||||
public void ConstructKeypad(InteractManager interactManager)
|
||||
public void ConstructKeypad(
|
||||
InteractManager interactManager,
|
||||
UIManager uiManager,
|
||||
TutorialService tutorialService)
|
||||
{
|
||||
_interactManager = interactManager;
|
||||
_uiManager = uiManager;
|
||||
_tutorialService = tutorialService;
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
@@ -86,8 +100,14 @@ namespace BriarQueen.Game.Puzzles.ChapterOne.AshwickHallow
|
||||
SyncDisplay();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_uiManager?.RegisterWindow(this);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
_uiManager?.UnregisterWindow(this);
|
||||
UnbindButtons();
|
||||
TryUnregisterRaycaster();
|
||||
CancelPanelTween();
|
||||
@@ -98,10 +118,7 @@ namespace BriarQueen.Game.Puzzles.ChapterOne.AshwickHallow
|
||||
_isCompleted = SaveManager.GetLevelFlag(LevelFlag.AshwickGateOpen);
|
||||
_isOpen = false;
|
||||
_isEvaluating = false;
|
||||
|
||||
SetPanelState(0f, false, false);
|
||||
SyncDisplay();
|
||||
_statusGlow?.TurnOff().Forget();
|
||||
_skipSaveOnHide = false;
|
||||
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
@@ -113,12 +130,15 @@ namespace BriarQueen.Game.Puzzles.ChapterOne.AshwickHallow
|
||||
|
||||
public void Open()
|
||||
{
|
||||
OpenInternal().Forget();
|
||||
if (IsCompleted || _uiManager == null)
|
||||
return;
|
||||
|
||||
_uiManager.OpenWindow(WindowType);
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
CloseInternal(requestSave: true).Forget();
|
||||
_uiManager?.CloseWindow(WindowType);
|
||||
}
|
||||
|
||||
public UniTask<byte[]> CaptureState()
|
||||
@@ -159,12 +179,13 @@ namespace BriarQueen.Game.Puzzles.ChapterOne.AshwickHallow
|
||||
SyncDisplay();
|
||||
|
||||
SaveManager.SetPuzzleCompleted(PuzzleKey.AshwickMarketGate, true, requestSave: false);
|
||||
|
||||
await CloseInternal(requestSave: false);
|
||||
_skipSaveOnHide = true;
|
||||
_uiManager?.CloseWindow(WindowType);
|
||||
AudioManager.Play(AudioNameIdentifiers.Get(SFXKey.AshwickGateOpening));
|
||||
await _outskirts.OpenGate();
|
||||
}
|
||||
|
||||
private async UniTaskVoid OpenInternal()
|
||||
public async UniTask Show()
|
||||
{
|
||||
if (IsCompleted || _isEvaluating || _isOpen)
|
||||
return;
|
||||
@@ -174,8 +195,7 @@ namespace BriarQueen.Game.Puzzles.ChapterOne.AshwickHallow
|
||||
|
||||
SetPanelState(0f, false, true);
|
||||
SyncDisplay();
|
||||
TryRegisterRaycaster();
|
||||
_statusGlow?.TurnOff().Forget();
|
||||
EnsureExclusiveRaycaster();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -199,9 +219,10 @@ namespace BriarQueen.Game.Puzzles.ChapterOne.AshwickHallow
|
||||
}
|
||||
|
||||
SetPanelState(1f, true, true);
|
||||
_tutorialService?.DisplayTutorial(TutorialPopupID.LeavingPuzzles);
|
||||
}
|
||||
|
||||
private async UniTask CloseInternal(bool requestSave)
|
||||
public async UniTask Hide()
|
||||
{
|
||||
if (!_isOpen)
|
||||
return;
|
||||
@@ -239,8 +260,91 @@ namespace BriarQueen.Game.Puzzles.ChapterOne.AshwickHallow
|
||||
SetPanelState(0f, false, false);
|
||||
TryUnregisterRaycaster();
|
||||
|
||||
if (requestSave)
|
||||
if (!_skipSaveOnHide && !_isCompleted)
|
||||
EventCoordinator.PublishImmediate(new RequestGameSaveEvent());
|
||||
|
||||
_skipSaveOnHide = false;
|
||||
}
|
||||
|
||||
public bool HandleBackRequest()
|
||||
{
|
||||
Close();
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CanSuspendFor(WindowType incomingWindowType)
|
||||
{
|
||||
return incomingWindowType == WindowType.PauseMenuWindow;
|
||||
}
|
||||
|
||||
public async UniTask SuspendForOverlay()
|
||||
{
|
||||
if (!_isOpen)
|
||||
return;
|
||||
|
||||
ResetPanelTween();
|
||||
|
||||
if (_panelGroup != null)
|
||||
{
|
||||
_panelGroup.interactable = false;
|
||||
_panelGroup.blocksRaycasts = false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_panelSequence = Sequence.Create(useUnscaledTime: true)
|
||||
.Group(Tween.Alpha(_panelGroup, new TweenSettings<float>
|
||||
{
|
||||
startValue = _panelGroup != null ? _panelGroup.alpha : 1f,
|
||||
endValue = 0f,
|
||||
settings = _panelFadeTweenSettings
|
||||
}));
|
||||
|
||||
await _panelSequence.ToUniTask(cancellationToken: _panelCts.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_panelSequence = default;
|
||||
}
|
||||
|
||||
SetPanelState(0f, false, false);
|
||||
}
|
||||
|
||||
public async UniTask ResumeFromOverlay()
|
||||
{
|
||||
if (!_isOpen)
|
||||
return;
|
||||
|
||||
ResetPanelTween();
|
||||
EnsureExclusiveRaycaster();
|
||||
SetPanelState(0f, false, true);
|
||||
|
||||
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, !_isEvaluating, true);
|
||||
}
|
||||
|
||||
private void OnDigitPressed(int digit)
|
||||
@@ -370,14 +474,18 @@ namespace BriarQueen.Game.Puzzles.ChapterOne.AshwickHallow
|
||||
}
|
||||
}
|
||||
|
||||
private void TryRegisterRaycaster()
|
||||
private void EnsureExclusiveRaycaster()
|
||||
{
|
||||
if (_raycasterRegistered || _interactManager == null || _graphicRaycaster == null)
|
||||
if (_interactManager == null || _graphicRaycaster == null)
|
||||
return;
|
||||
|
||||
_interactManager.AddUIRaycaster(_graphicRaycaster);
|
||||
if (!_raycasterRegistered)
|
||||
{
|
||||
_interactManager.AddUIRaycaster(_graphicRaycaster);
|
||||
_raycasterRegistered = true;
|
||||
}
|
||||
|
||||
_interactManager.SetExclusiveRaycaster(_graphicRaycaster);
|
||||
_raycasterRegistered = true;
|
||||
}
|
||||
|
||||
private void TryUnregisterRaycaster()
|
||||
@@ -386,7 +494,7 @@ namespace BriarQueen.Game.Puzzles.ChapterOne.AshwickHallow
|
||||
return;
|
||||
|
||||
_interactManager.RemoveUIRaycaster(_graphicRaycaster);
|
||||
_interactManager.ClearExclusiveRaycaster();
|
||||
_interactManager.ReleaseExclusiveRaycaster(_graphicRaycaster);
|
||||
_raycasterRegistered = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ using BriarQueen.Framework.Services.Destruction;
|
||||
using BriarQueen.Framework.Services.Game;
|
||||
using BriarQueen.Framework.Services.Puzzles;
|
||||
using BriarQueen.Framework.Services.Settings;
|
||||
using BriarQueen.Framework.Services.Subtitles;
|
||||
using BriarQueen.Framework.Services.Tutorials;
|
||||
using BriarQueen.Game.Cinematics;
|
||||
using BriarQueen.Game.Misc;
|
||||
@@ -89,8 +90,9 @@ namespace BriarQueen.Game.Scopes
|
||||
builder.Register<SaveManager>(Lifetime.Singleton);
|
||||
builder.Register<SettingsService>(Lifetime.Singleton);
|
||||
builder.Register<SteamManager>(Lifetime.Singleton);
|
||||
builder.Register<SubtitleService>(Lifetime.Singleton);
|
||||
builder.Register<TutorialService>(Lifetime.Singleton);
|
||||
builder.Register<UIManager>(Lifetime.Singleton);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,6 +255,7 @@ namespace BriarQueen.UI.Codex
|
||||
}
|
||||
|
||||
public WindowType WindowType => WindowType.CodexWindow;
|
||||
public UIPauseBehavior PauseBehavior => UIPauseBehavior.TreatAsBackRequest;
|
||||
|
||||
// ── IUIWindow ─────────────────────────────────────────────────
|
||||
|
||||
@@ -263,7 +264,7 @@ namespace BriarQueen.UI.Codex
|
||||
ResetOperationCts();
|
||||
|
||||
gameObject.SetActive(true);
|
||||
TryRegisterRaycaster();
|
||||
EnsureExclusiveRaycaster();
|
||||
|
||||
if (_canvasGroup != null)
|
||||
{
|
||||
@@ -380,7 +381,7 @@ namespace BriarQueen.UI.Codex
|
||||
|
||||
// ── Raycaster ─────────────────────────────────────────────────
|
||||
|
||||
private void TryRegisterRaycaster()
|
||||
private void EnsureExclusiveRaycaster()
|
||||
{
|
||||
Debug.Log($"[CodexWindow] TryRegisterRaycaster " +
|
||||
|
||||
@@ -392,10 +393,15 @@ namespace BriarQueen.UI.Codex
|
||||
|
||||
Debug.Log("[CodexWindow] Try register raycaster.");
|
||||
|
||||
if (_raycasterRegistered || _interactManager == null || _graphicRaycaster == null) return;
|
||||
_interactManager.AddUIRaycaster(_graphicRaycaster);
|
||||
if (_interactManager == null || _graphicRaycaster == null) return;
|
||||
|
||||
if (!_raycasterRegistered)
|
||||
{
|
||||
_interactManager.AddUIRaycaster(_graphicRaycaster);
|
||||
_raycasterRegistered = true;
|
||||
}
|
||||
|
||||
_interactManager.SetExclusiveRaycaster(_graphicRaycaster);
|
||||
_raycasterRegistered = true;
|
||||
|
||||
Debug.Log("[CodexWindow] Registered raycaster.");
|
||||
}
|
||||
@@ -406,7 +412,7 @@ namespace BriarQueen.UI.Codex
|
||||
|
||||
if (!_raycasterRegistered || _interactManager == null || _graphicRaycaster == null) return;
|
||||
_interactManager.RemoveUIRaycaster(_graphicRaycaster);
|
||||
_interactManager.ClearExclusiveRaycaster();
|
||||
_interactManager.ReleaseExclusiveRaycaster(_graphicRaycaster);
|
||||
_raycasterRegistered = false;
|
||||
|
||||
Debug.Log("[CodexWindow] Raycaster unregistered.");
|
||||
|
||||
212
Assets/Scripts/UI/HUD/SubtitleUI.cs
Normal file
212
Assets/Scripts/UI/HUD/SubtitleUI.cs
Normal file
@@ -0,0 +1,212 @@
|
||||
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<SubtitleDisplayChangedEvent>(OnSubtitleDisplayChanged);
|
||||
SyncFromService();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
_eventCoordinator?.Unsubscribe<SubtitleDisplayChangedEvent>(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<float>
|
||||
{
|
||||
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<float>
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/UI/HUD/SubtitleUI.cs.meta
Normal file
2
Assets/Scripts/UI/HUD/SubtitleUI.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a7225bc6a3f524fb8b820162cd937c1e
|
||||
@@ -635,6 +635,11 @@ namespace BriarQueen.UI.Menus
|
||||
group.blocksRaycasts = inputEnabled;
|
||||
}
|
||||
|
||||
public bool CanSuspendFor(WindowType incomingWindowType)
|
||||
{
|
||||
return incomingWindowType == WindowType.SettingsWindow;
|
||||
}
|
||||
|
||||
public async UniTask SuspendForOverlay()
|
||||
{
|
||||
if (_mainMenuGroup == null)
|
||||
|
||||
@@ -79,18 +79,25 @@ namespace BriarQueen.UI.Menus
|
||||
|
||||
public bool IsModal => true;
|
||||
public WindowType WindowType => WindowType.PauseMenuWindow;
|
||||
|
||||
private void TryRegisterRaycaster()
|
||||
{
|
||||
if (_raycasterRegistered)
|
||||
return;
|
||||
public UIPauseBehavior PauseBehavior => UIPauseBehavior.TreatAsBackRequest;
|
||||
|
||||
public bool CanSuspendFor(WindowType incomingWindowType)
|
||||
{
|
||||
return incomingWindowType == WindowType.SettingsWindow;
|
||||
}
|
||||
|
||||
private void EnsureExclusiveRaycaster()
|
||||
{
|
||||
if (_interactManager == null || _graphicRaycaster == null)
|
||||
return;
|
||||
|
||||
_interactManager.AddUIRaycaster(_graphicRaycaster);
|
||||
if (!_raycasterRegistered)
|
||||
{
|
||||
_interactManager.AddUIRaycaster(_graphicRaycaster);
|
||||
_raycasterRegistered = true;
|
||||
}
|
||||
|
||||
_interactManager.SetExclusiveRaycaster(_graphicRaycaster);
|
||||
_raycasterRegistered = true;
|
||||
}
|
||||
|
||||
private void TryUnregisterRaycaster()
|
||||
@@ -102,7 +109,7 @@ namespace BriarQueen.UI.Menus
|
||||
return;
|
||||
|
||||
_interactManager.RemoveUIRaycaster(_graphicRaycaster);
|
||||
_interactManager.ClearExclusiveRaycaster();
|
||||
_interactManager.ReleaseExclusiveRaycaster(_graphicRaycaster);
|
||||
_raycasterRegistered = false;
|
||||
}
|
||||
|
||||
@@ -149,7 +156,7 @@ namespace BriarQueen.UI.Menus
|
||||
SetLevelName();
|
||||
|
||||
gameObject.SetActive(true);
|
||||
TryRegisterRaycaster();
|
||||
EnsureExclusiveRaycaster();
|
||||
|
||||
_canvasGroup.blocksRaycasts = false;
|
||||
_canvasGroup.interactable = false;
|
||||
@@ -233,6 +240,7 @@ namespace BriarQueen.UI.Menus
|
||||
public async UniTask ResumeFromOverlay()
|
||||
{
|
||||
StopAndResetCancellation();
|
||||
EnsureExclusiveRaycaster();
|
||||
|
||||
_buttonsGroup.blocksRaycasts = true;
|
||||
_buttonsGroup.interactable = true;
|
||||
|
||||
@@ -147,6 +147,7 @@ namespace BriarQueen.UI.Menus
|
||||
// ── IUIWindow ─────────────────────────────────────────────────
|
||||
public bool IsModal => true;
|
||||
public WindowType WindowType => WindowType.SettingsWindow;
|
||||
public UIPauseBehavior PauseBehavior => UIPauseBehavior.TreatAsBackRequest;
|
||||
|
||||
// ── Unity lifecycle ───────────────────────────────────────────
|
||||
private void Awake()
|
||||
@@ -155,9 +156,9 @@ namespace BriarQueen.UI.Menus
|
||||
if (_backButton != null) _backButton.onClick.AddListener(OnBackClicked);
|
||||
|
||||
// Individual buttons drive panel switching via SelectionRequested
|
||||
if (_gameCategoryButton != null) _gameCategoryButton.SelectionRequested += _ => SwitchCategory(Category.Game);
|
||||
if (_visualCategoryButton != null) _visualCategoryButton.SelectionRequested += _ => SwitchCategory(Category.Visual);
|
||||
if (_audioCategoryButton != null) _audioCategoryButton.SelectionRequested += _ => SwitchCategory(Category.Audio);
|
||||
if (_gameCategoryButton != null) _gameCategoryButton.SelectionRequested += OnGameCategorySelected;
|
||||
if (_visualCategoryButton != null) _visualCategoryButton.SelectionRequested += OnVisualCategorySelected;
|
||||
if (_audioCategoryButton != null) _audioCategoryButton.SelectionRequested += OnAudioCategorySelected;
|
||||
|
||||
HookSlider(_masterVolumeSlider, _masterVolumeText, v => _draftAudio.MasterVolume = v);
|
||||
HookSlider(_musicVolumeSlider, _musicVolumeText, v => _draftAudio.MusicVolume = v);
|
||||
@@ -210,9 +211,9 @@ namespace BriarQueen.UI.Menus
|
||||
if (_applyButton != null) _applyButton.onClick.RemoveListener(OnApplyClicked);
|
||||
if (_backButton != null) _backButton.onClick.RemoveListener(OnBackClicked);
|
||||
|
||||
if (_gameCategoryButton != null) _gameCategoryButton.SelectionRequested -= _ => SwitchCategory(Category.Game);
|
||||
if (_visualCategoryButton != null) _visualCategoryButton.SelectionRequested -= _ => SwitchCategory(Category.Visual);
|
||||
if (_audioCategoryButton != null) _audioCategoryButton.SelectionRequested -= _ => SwitchCategory(Category.Audio);
|
||||
if (_gameCategoryButton != null) _gameCategoryButton.SelectionRequested -= OnGameCategorySelected;
|
||||
if (_visualCategoryButton != null) _visualCategoryButton.SelectionRequested -= OnVisualCategorySelected;
|
||||
if (_audioCategoryButton != null) _audioCategoryButton.SelectionRequested -= OnAudioCategorySelected;
|
||||
|
||||
if (_popupDisplayDurationSlider != null)
|
||||
_popupDisplayDurationSlider.onValueChanged.RemoveListener(OnPopupDisplayDurationChanged);
|
||||
@@ -246,7 +247,7 @@ namespace BriarQueen.UI.Menus
|
||||
StopAndResetCancellation();
|
||||
|
||||
gameObject.SetActive(true);
|
||||
TryRegisterRaycaster();
|
||||
EnsureExclusiveRaycaster();
|
||||
|
||||
_canvasGroup.alpha = 1f;
|
||||
_canvasGroup.blocksRaycasts = false;
|
||||
@@ -368,6 +369,21 @@ namespace BriarQueen.UI.Menus
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnGameCategorySelected(AnimatedSelectionButton _)
|
||||
{
|
||||
SwitchCategory(Category.Game);
|
||||
}
|
||||
|
||||
private void OnVisualCategorySelected(AnimatedSelectionButton _)
|
||||
{
|
||||
SwitchCategory(Category.Visual);
|
||||
}
|
||||
|
||||
private void OnAudioCategorySelected(AnimatedSelectionButton _)
|
||||
{
|
||||
SwitchCategory(Category.Audio);
|
||||
}
|
||||
|
||||
// ── Category switching ────────────────────────────────────────
|
||||
|
||||
private void SwitchCategory(Category category)
|
||||
@@ -909,17 +925,18 @@ namespace BriarQueen.UI.Menus
|
||||
};
|
||||
}
|
||||
|
||||
private void TryRegisterRaycaster()
|
||||
private void EnsureExclusiveRaycaster()
|
||||
{
|
||||
if (_raycasterRegistered)
|
||||
return;
|
||||
|
||||
if (_interactManager == null || _graphicRaycaster == null)
|
||||
return;
|
||||
|
||||
_interactManager.AddUIRaycaster(_graphicRaycaster);
|
||||
if (!_raycasterRegistered)
|
||||
{
|
||||
_interactManager.AddUIRaycaster(_graphicRaycaster);
|
||||
_raycasterRegistered = true;
|
||||
}
|
||||
|
||||
_interactManager.SetExclusiveRaycaster(_graphicRaycaster);
|
||||
_raycasterRegistered = true;
|
||||
}
|
||||
|
||||
private void TryUnregisterRaycaster()
|
||||
@@ -931,7 +948,7 @@ namespace BriarQueen.UI.Menus
|
||||
return;
|
||||
|
||||
_interactManager.RemoveUIRaycaster(_graphicRaycaster);
|
||||
_interactManager.ClearExclusiveRaycaster();
|
||||
_interactManager.ReleaseExclusiveRaycaster(_graphicRaycaster);
|
||||
_raycasterRegistered = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,9 @@ namespace BriarQueen.UI.Scopes
|
||||
|
||||
[SerializeField]
|
||||
private InventoryBar _inventoryBar;
|
||||
|
||||
[SerializeField]
|
||||
private SubtitleUI _subtitleUI;
|
||||
|
||||
|
||||
protected override void Configure(IContainerBuilder builder)
|
||||
@@ -70,6 +73,9 @@ namespace BriarQueen.UI.Scopes
|
||||
|
||||
if (_inventoryBar != null)
|
||||
builder.RegisterComponent(_inventoryBar);
|
||||
|
||||
if (_subtitleUI != null)
|
||||
builder.RegisterComponent(_subtitleUI);
|
||||
|
||||
|
||||
builder.RegisterBuildCallback(container =>
|
||||
@@ -101,4 +107,4 @@ namespace BriarQueen.UI.Scopes
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user