using System; using Core; using Core.SaveLoad; using Data.CardSystem; using UnityEngine; using UnityEngine.SceneManagement; 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; [Header("Dev Options")] [SerializeField] private UnityEngine.UI.Button devOptionsButton; [SerializeField] private GameObject mainOptionsContainer; [SerializeField] private GameObject devOptionsContainer; // After UIPageController (50) public override int ManagedAwakePriority => 55; private new void Awake() { base.Awake(); // CRITICAL: Register with LifecycleManager! // Set instance immediately so it's available before OnManagedAwake() is called _instance = this; // Ensure we have a CanvasGroup for transitions if (canvasGroup == null) canvasGroup = GetComponent(); if (canvasGroup == null) canvasGroup = gameObject.AddComponent(); // Set initial state canvasGroup.alpha = 0f; canvasGroup.interactable = false; canvasGroup.blocksRaycasts = false; gameObject.SetActive(false); } protected override void OnManagedAwake() { // Subscribe to scene-dependent events - must be in OnManagedAwake, not OnSceneReady // because PauseMenu is in DontDestroyOnLoad and OnSceneReady only fires once if (SceneManagerService.Instance != null) { 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; } // Set initial state based on current scene SetPauseMenuByLevel(SceneManager.GetActiveScene().name); Logging.Debug("[PauseMenu] Subscribed to SceneManagerService events"); } protected override void OnSceneReady() { // This only fires once for DontDestroyOnLoad objects, so we handle scene loads in OnManagedAwake } protected override void OnDestroy() { base.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) { // When a new scene loads, ensure pause menu is removed from UIPageController stack // and properly hidden, regardless of pause state if (UIPageController.Instance != null && UIPageController.Instance.CurrentPage == this) { UIPageController.Instance.PopPage(); } // Ensure pause state is cleared if (GameManager.Instance != null && GameManager.Instance.IsPaused) { EndPauseSideEffects(); } // Hide the menu UI if (pauseMenuPanel != null) pauseMenuPanel.SetActive(false); if (canvasGroup != null) { canvasGroup.alpha = 0f; canvasGroup.interactable = false; canvasGroup.blocksRaycasts = false; } gameObject.SetActive(false); Logging.Debug($"[PauseMenu] Cleaned up pause menu state 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() { // Pop from UIPageController stack before switching scenes if (UIPageController.Instance != null && UIPageController.Instance.CurrentPage == this) { UIPageController.Instance.PopPage(); } // Ensure pause state is cleared if (GameManager.Instance != null && GameManager.Instance.IsPaused) { EndPauseSideEffects(); } // 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() { // Clear all save data for the current gameplay level before reloading if (SaveLoadManager.Instance != null && SceneManagerService.Instance != null) { string currentLevel = SceneManagerService.Instance.CurrentGameplayScene; if (!string.IsNullOrEmpty(currentLevel)) { SaveLoadManager.Instance.ClearLevelData(currentLevel); Logging.Debug($"[PauseMenu] Cleared save data for current level: {currentLevel}"); } } // Now reload the current scene with fresh state - skipSave=true prevents re-saving cleared data var progress = new Progress(p => Logging.Debug($"Loading progress: {p * 100:F0}%")); await SceneManagerService.Instance.ReloadCurrentScene(progress, autoHideLoadingScreen: true, skipSave: true); } /// /// 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); } #region Dev Options /// /// Shows dev options panel and hides main options /// public void ShowDevOptions() { if (mainOptionsContainer != null) mainOptionsContainer.SetActive(false); if (devOptionsContainer != null) devOptionsContainer.SetActive(true); Logging.Debug("[PauseMenu] Showing dev options"); } /// /// Hides dev options panel and shows main options /// public void HideDevOptions() { if (devOptionsContainer != null) devOptionsContainer.SetActive(false); if (mainOptionsContainer != null) mainOptionsContainer.SetActive(true); Logging.Debug("[PauseMenu] Hiding dev options"); } /// /// Dev option: Completely wipes all save data and reloads the current level /// public async void DevResetAndReload() { Logging.Debug("[PauseMenu] Dev Reset: Clearing all save data and reloading level"); // Clear the card collection if (CardSystemManager.Instance != null) { CardSystemManager.Instance.ClearAllCollectionData(); Logging.Debug("[PauseMenu] Cleared card collection"); } // Clear all save data from memory if (SaveLoadManager.Instance != null && SaveLoadManager.Instance.currentSaveData != null) { SaveLoadManager.Instance.currentSaveData.participantStates.Clear(); SaveLoadManager.Instance.currentSaveData.unlockedMinigames.Clear(); Logging.Debug("[PauseMenu] Cleared all save data from memory"); } // Delete the save file string saveFolder = System.IO.Path.Combine(Application.persistentDataPath, "GameSaves"); if (System.IO.Directory.Exists(saveFolder)) { try { string[] files = System.IO.Directory.GetFiles(saveFolder); foreach (string file in files) { System.IO.File.Delete(file); Logging.Debug($"[PauseMenu] Deleted save file: {file}"); } } catch (Exception ex) { Logging.Warning($"[PauseMenu] Failed to delete some save files: {ex.Message}"); } } // Now reload the current scene - skipSave=true prevents re-saving the cleared data var progress = new Progress(p => Logging.Debug($"Loading progress: {p * 100:F0}%")); await SceneManagerService.Instance.ReloadCurrentScene(progress, autoHideLoadingScreen: true, skipSave: true); } /// /// Dev option: Gives the player 3 booster packs /// public void DevGiveBoosters() { if (CardSystemManager.Instance != null) { CardSystemManager.Instance.AddBoosterPack(3); Logging.Debug("[PauseMenu] Dev: Granted 3 booster packs"); } else { Logging.Warning("[PauseMenu] Dev: CardSystemManager not available"); } } #endregion } }