using System; using System.Collections.Generic; using System.Threading; using BriarQueen.Framework.Services.Game; using BriarQueen.Game.Cinematics.Data; using Cysharp.Threading.Tasks; using NaughtyAttributes; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; using VContainer; namespace BriarQueen.Game.Cinematics { public class SplashScreens : MonoBehaviour { [SerializeField] [ReorderableList] private List _splashScreens = new(); [SerializeField] private Image _activeSplashScreen; [Header("Playback")] [Tooltip("Small fade for polish. Set to 0 for hard cuts.")] [SerializeField] private float _fadeDuration = 0.15f; [SerializeField] private bool _skipInvalidPanels = true; private CancellationTokenSource _cts; private GameService _gameService; private bool _isPlaying; [Inject] public void Construct(GameService gameService) { _gameService = gameService; } private void OnDisable() { Stop(); } public void Play() { if (_isPlaying) return; Debug.Log("[SplashScreens] Playing splash screens."); _isPlaying = true; _cts?.Dispose(); _cts = new CancellationTokenSource(); RunSequence(_cts.Token).Forget(); } public void Stop() { if (!_isPlaying) return; _isPlaying = false; try { _cts?.Cancel(); } catch { } finally { _cts?.Dispose(); _cts = null; } } private async UniTaskVoid RunSequence(CancellationToken token) { try { Debug.Log("[SplashScreens] Splash screen sequence started."); if (_activeSplashScreen == null) { Debug.LogWarning("[SplashScreens] Active splash Image is not assigned."); await EndSplashScreens(); return; } var cg = _activeSplashScreen.GetComponent(); if (cg == null) cg = _activeSplashScreen.gameObject.AddComponent(); cg.alpha = 0f; _activeSplashScreen.enabled = true; if (_splashScreens == null || _splashScreens.Count == 0) { Debug.LogWarning("[SplashScreens] No splash panels configured."); await EndSplashScreens(); return; } for (var i = 0; i < _splashScreens.Count; i++) { token.ThrowIfCancellationRequested(); var panel = _splashScreens[i]; if (panel == null || panel.CinematicImage == null) { if (_skipInvalidPanels) continue; Debug.LogWarning($"[SplashScreens] Splash panel at index {i} is null or missing image."); continue; } _activeSplashScreen.sprite = panel.CinematicImage; if (_fadeDuration > 0f) await FadeTo(cg, 1f, _fadeDuration, token); else cg.alpha = 1f; var hold = Mathf.Max(0f, panel.DisplayTime); if (hold > 0f) await UniTask.Delay(TimeSpan.FromSeconds(hold), cancellationToken: token); if (_fadeDuration > 0f) await FadeTo(cg, 0f, _fadeDuration, token); else cg.alpha = 0f; } await EndSplashScreens(); } catch (OperationCanceledException) { } catch (Exception ex) { Debug.LogError($"[SplashScreens] Sequence failed: {ex}"); try { await EndSplashScreens(); } catch { } } finally { _isPlaying = false; } } private async UniTask FadeTo(CanvasGroup cg, float target, float duration, CancellationToken token) { var start = cg.alpha; if (Mathf.Approximately(start, target)) { cg.alpha = target; return; } var t = 0f; while (t < duration) { token.ThrowIfCancellationRequested(); t += Time.deltaTime; var p = Mathf.Clamp01(t / duration); cg.alpha = Mathf.Lerp(start, target, p); await UniTask.NextFrame(token); } cg.alpha = target; } private async UniTask EndSplashScreens() { if (_gameService != null) { await _gameService.LoadUIAndMainMenuScene(); } else { Debug.LogWarning("[SplashScreens] GameService is null; cannot load UI/MainMenu scene."); } const string bootstrapSceneName = "00_Bootstrap"; var scene = SceneManager.GetSceneByName(bootstrapSceneName); if (scene.IsValid() && scene.isLoaded) await SceneManager.UnloadSceneAsync(bootstrapSceneName); } } }