using System; using Core; using UnityEngine; using UnityEngine.SceneManagement; using Bootstrap; using UI.Core; using Pixelplacement; namespace UI { public class PauseMenu : UIPage { private static PauseMenu _instance; /// /// Singleton instance of the PauseMenu. No longer creates an instance if one doesn't exist. /// public static PauseMenu Instance => _instance; [Header("UI References")] [SerializeField] private GameObject pauseMenuPanel; [SerializeField] private GameObject pauseButton; [SerializeField] private CanvasGroup canvasGroup; private void Awake() { _instance = this; // Ensure we have a CanvasGroup for transitions if (canvasGroup == null) canvasGroup = GetComponent(); if (canvasGroup == null) canvasGroup = gameObject.AddComponent(); canvasGroup.alpha = 0f; canvasGroup.interactable = false; canvasGroup.blocksRaycasts = false; gameObject.SetActive(false); // Register for post-boot initialization BootCompletionService.RegisterInitAction(InitializePostBoot); } private void InitializePostBoot() { // Subscribe to scene loaded events SceneManagerService.Instance.SceneLoadCompleted += SetPauseMenuByLevel; // Also react to global UI hide/show events from the page controller if (UIPageController.Instance != null) { UIPageController.Instance.OnAllUIHidden += HandleAllUIHidden; UIPageController.Instance.OnAllUIShown += HandleAllUIShown; } // SceneManagerService subscription moved to InitializePostBoot // Set initial state based on current scene SetPauseMenuByLevel(SceneManager.GetActiveScene().name); Logging.Debug("[PauseMenu] Subscribed to SceneManagerService events"); } private void OnDestroy() { // Unsubscribe when destroyed if (SceneManagerService.Instance != null) { SceneManagerService.Instance.SceneLoadCompleted -= SetPauseMenuByLevel; } if (UIPageController.Instance != null) { UIPageController.Instance.OnAllUIHidden -= HandleAllUIHidden; UIPageController.Instance.OnAllUIShown -= HandleAllUIShown; } } /// /// Sets the pause menu game object active or inactive based on the current level /// /// The name of the level/scene public void SetPauseMenuByLevel(string levelName) { HidePauseMenu(); // TODO: Implement level-based pause menu visibility logic if needed /*if (string.IsNullOrEmpty(levelName)) return; bool isStartingLevel = levelName.ToLower().Contains("startingscene"); if(isStartingLevel) HidePauseMenu(false); // Ensure menu is hidden when switching to a game level Logging.Debug($"[PauseMenu] Setting pause menu active: {!isStartingLevel} for scene: {levelName}");*/ } /// /// Shows the pause menu and hides the pause button. Sets input mode to UI. /// public void ShowPauseMenu() { if (UIPageController.Instance != null) { UIPageController.Instance.PushPage(this); } else { // Fallback if no controller, just show if (pauseMenuPanel != null) pauseMenuPanel.SetActive(true); gameObject.SetActive(true); BeginPauseSideEffects(); // no animation fallback if (canvasGroup != null) { canvasGroup.alpha = 1f; canvasGroup.interactable = true; canvasGroup.blocksRaycasts = true; } } } /// /// Hides the pause menu and shows the pause button. Sets input mode to Game. /// public void HidePauseMenu() { if (!GameManager.Instance.IsPaused) { // Ensure UI is hidden if somehow active without state if (pauseMenuPanel != null) pauseMenuPanel.SetActive(false); gameObject.SetActive(false); return; } if (UIPageController.Instance != null && UIPageController.Instance.CurrentPage == this) { UIPageController.Instance.PopPage(); } else { // Fallback if no controller, just hide if (pauseMenuPanel != null) pauseMenuPanel.SetActive(false); if (pauseButton != null) pauseButton.SetActive(true); if (canvasGroup != null) { canvasGroup.alpha = 0f; canvasGroup.interactable = false; canvasGroup.blocksRaycasts = false; } gameObject.SetActive(false); } } public void HidePauseMenuAndResumeGame() { HidePauseMenu(); EndPauseSideEffects(); } /// /// Resumes the game by hiding the pause menu. /// public void ResumeGame() { HidePauseMenuAndResumeGame(); } private void BeginPauseSideEffects() { if (pauseButton != null) pauseButton.SetActive(false); GameManager.Instance.RequestPause(this); Logging.Debug("[PauseMenu] Game Paused"); } private void EndPauseSideEffects() { if (pauseButton != null) pauseButton.SetActive(true); GameManager.Instance.ReleasePause(this); Logging.Debug("[PauseMenu] Game Resumed"); } protected override void DoTransitionIn(Action onComplete) { // Ensure the panel root is active if (pauseMenuPanel != null) pauseMenuPanel.SetActive(true); // Pause side effects should run immediately (hide button, set input mode, etc.). // The tween itself must run in unscaled time so it still animates while the game is paused. BeginPauseSideEffects(); if (canvasGroup != null) { canvasGroup.interactable = true; canvasGroup.blocksRaycasts = true; canvasGroup.alpha = 0f; // pass obeyTimescale = false so this tween runs even when Time.timeScale == 0 Tween.Value(0f, 1f, (v) => { Logging.Debug($"[PauseMenu] Tweening pause menu alpha: {v}"); canvasGroup.alpha = v; }, transitionDuration, 0f, Tween.EaseInOut, Tween.LoopType.None, null, () => { Logging.Debug("[PauseMenu] Finished tweening pause menu in."); onComplete?.Invoke(); }, false); } else { onComplete?.Invoke(); } } protected override void DoTransitionOut(Action onComplete) { if (canvasGroup != null) { canvasGroup.interactable = false; canvasGroup.blocksRaycasts = false; // Run out-tween in unscaled time as well so the fade completes while paused. Tween.Value(canvasGroup.alpha, 0f, v => canvasGroup.alpha = v, transitionDuration, 0f, Tween.EaseInOut, Tween.LoopType.None, null, () => { EndPauseSideEffects(); if (pauseMenuPanel != null) pauseMenuPanel.SetActive(false); onComplete?.Invoke(); }, false); } else { EndPauseSideEffects(); if (pauseMenuPanel != null) pauseMenuPanel.SetActive(false); onComplete?.Invoke(); } } /// /// Exits to the main menu scene. /// public async void ExitToAppleHills() { // Replace with the actual scene name as set in Build Settings var progress = new Progress(p => Logging.Debug($"Loading progress: {p * 100:F0}%")); await SceneManagerService.Instance.SwitchSceneAsync("AppleHillsOverworld", progress); } /// /// Exits the application. /// public void ExitGame() { #if UNITY_EDITOR UnityEditor.EditorApplication.isPlaying = false; #else Application.Quit(); #endif } public async void ReloadLevel() { var progress = new Progress(p => Logging.Debug($"Loading progress: {p * 100:F0}%")); await SceneManagerService.Instance.ReloadCurrentScene(progress); } /// /// Loads a level based on the selection from a dropdown menu. /// Connect this to a Dropdown's onValueChanged event and pass the selected option text. /// /// The selected level name or identifier from the dropdown public async void LoadLevel(int levelSelection) { // Hide the pause menu before loading a new level HidePauseMenuAndResumeGame(); // Replace with the actual scene name as set in Build Settings var progress = new Progress(p => Logging.Debug($"Loading progress: {p * 100:F0}%")); switch (levelSelection) { case 0: await SceneManagerService.Instance.SwitchSceneAsync("AppleHillsOverworld", progress); break; case 1: await SceneManagerService.Instance.SwitchSceneAsync("Quarry", progress); break; case 2: await SceneManagerService.Instance.SwitchSceneAsync("DivingForPictures", progress); break; } } // Handlers for UI controller hide/show events — make these safe with respect to transitions and pause state private void HandleAllUIHidden() { var parent = transform.parent?.gameObject ?? gameObject; // If we're currently transitioning, wait for the transition out to complete before deactivating if (_isTransitioning) { Action handler = null; handler = () => { OnTransitionOutCompleted -= handler; parent.SetActive(false); }; OnTransitionOutCompleted += handler; return; } // If this page is visible, request a proper hide so transition/out side-effects run (e.g. releasing pause) if (_isVisible) { HidePauseMenu(); return; } // Otherwise it's safe to immediately deactivate parent.SetActive(false); } private void HandleAllUIShown() { var parent = transform.parent?.gameObject ?? gameObject; // Just ensure the parent is active. Do not force pause or transitions here. parent.SetActive(true); } } }