using Core; using Core.Lifecycle; using UI.Core; using Cinematics; using UnityEngine; using UnityEngine.Playables; using UnityEngine.UI; using System.Linq; using System.Collections.Generic; using System; namespace UI { /// /// Manages player HUD elements and their visibility. /// Works alongside UIPageController (must be on same GameObject). /// Automatically hides HUD when pages open, shows when all pages close. /// public class PlayerHudManager : ManagedBehaviour { public enum UIMode { Overworld, Puzzle, Minigame, HideAll }; /// /// Context object for managing temporary HUD element visibility. /// Automatically restores previous visibility state when disposed. /// Usage: using (var ctx = PlayerHudManager.Instance.ShowElementTemporarily(myButton)) { ... } /// public class HudVisibilityContext : IDisposable { private readonly GameObject _element; private readonly bool _previousState; private bool _disposed; internal HudVisibilityContext(GameObject element, bool show) { _element = element; _previousState = element.activeSelf; _element.SetActive(show); } public void Dispose() { if (_disposed) return; _disposed = true; if (_element != null) { _element.SetActive(_previousState); } } } /// /// Multi-element visibility context for managing multiple HUD elements at once. /// Automatically restores previous visibility states when disposed. /// public class MultiHudVisibilityContext : IDisposable { private readonly List<(GameObject element, bool previousState)> _elements; private bool _disposed; internal MultiHudVisibilityContext(IEnumerable elements, bool show) { _elements = new List<(GameObject, bool)>(); foreach (var element in elements) { if (element != null) { _elements.Add((element, element.activeSelf)); element.SetActive(show); } } } public void Dispose() { if (_disposed) return; _disposed = true; foreach (var (element, previousState) in _elements) { if (element != null) { element.SetActive(previousState); } } } } public UIMode currentUIMode; [Header("HUD Management")] [SerializeField] private Transform hudButtonsContainer; // Parent object containing all HUD buttons [Header("Cinematic References")] public GameObject landscapeObject; public GameObject portraitObject; public GameObject cinematicsParentObject; public GameObject CinematicBackground; [Header("HUD Elements")] public GameObject eagleEye; public GameObject ramaSjangButton; public GameObject scrabBookButton; [HideInInspector] public Image cinematicSprites; [HideInInspector] public Image cinematicBackgroundSprites; [HideInInspector] public GameObject currentCinematicPlayer; [HideInInspector] public PlayableDirector playableDirector; private static PlayerHudManager _instance; public static PlayerHudManager Instance => _instance; private UIPageController _uiPageController; private AppSwitcher _appSwitcherComponent; internal override void OnManagedAwake() { if (Instance != null) { Destroy(this); return; } // Set instance immediately (early initialization) _instance = this; // Get UIPageController on same GameObject _uiPageController = GetComponent(); if (_uiPageController == null) { UnityEngine.Debug.LogError("[PlayerHudManager] UIPageController not found on same GameObject!"); } InitializeReferences(); } internal override void OnManagedStart() { // Subscribe to UIPageController page changes for auto HUD management if (_uiPageController != null) { _uiPageController.OnPageChanged += HandlePageStackChanged; } // Subscribe to CinematicsManager events for HUD management during cinematics if (CinematicsManager.Instance != null) { CinematicsManager.Instance.OnCinematicStarted += HandleCinematicStarted; CinematicsManager.Instance.OnCinematicStopped += HandleCinematicStopped; // Check if a cinematic is already playing if (CinematicsManager.Instance.IsCinematicPlaying) { HideAllHud(); Logging.Debug("[PlayerHudManager] Cinematic already playing on init, hiding HUD"); } } // Subscribe to scene load events to adjust HUD based on scene if (SceneManagerService.Instance != null) { SceneManagerService.Instance.SceneLoadCompleted += NewSceneLoaded; } // If in editor - initialize HUD based on current scene #if UNITY_EDITOR if (SceneManagerService.Instance.CurrentGameplayScene == "StartingScene") { // TODO: Hide all UI until cinematics have played NewSceneLoaded("AppleHillsOverworld"); } else if (SceneManagerService.Instance.CurrentGameplayScene != null) { NewSceneLoaded(SceneManagerService.Instance.CurrentGameplayScene); } #endif } internal override void OnManagedDestroy() { // Unsubscribe from events if (_uiPageController != null) { _uiPageController.OnPageChanged -= HandlePageStackChanged; } if (CinematicsManager.Instance != null) { CinematicsManager.Instance.OnCinematicStarted -= HandleCinematicStarted; CinematicsManager.Instance.OnCinematicStopped -= HandleCinematicStopped; } if (SceneManagerService.Instance != null) { SceneManagerService.Instance.SceneLoadCompleted -= NewSceneLoaded; } } private void InitializeReferences() { currentCinematicPlayer = landscapeObject; playableDirector = cinematicsParentObject.GetComponent(); cinematicSprites = currentCinematicPlayer.GetComponent(); cinematicBackgroundSprites = CinematicBackground.GetComponent(); } private void UpateCinematicReferences(GameObject newCinematicPlayer) { currentCinematicPlayer = newCinematicPlayer; cinematicSprites = currentCinematicPlayer.GetComponent(); } public void SetPortraitMode(bool portraitModeEnable) { if (portraitModeEnable) { UpateCinematicReferences(portraitObject); } else { UpateCinematicReferences(landscapeObject); } } public void NewSceneLoaded(string sceneName) { switch (sceneName) { case "AppleHillsOverworld": currentUIMode = UIMode.Overworld; break; case "Quarry": currentUIMode = UIMode.Puzzle; break; case "DivingForPictures" or "CardQualityControl" or "BirdPoop": currentUIMode = UIMode.Minigame; break; } ShowAllHud(); } /// /// Hides all HUD button elements /// public void HideAllHud() { SetHudVisibility(false); } /// /// Shows all HUD button elements /// public void ShowAllHud() { SetHudVisibility(true); } /// /// Hides all HUD elements except the specified exceptions /// /// GameObjects to keep visible public void HideAllHudExcept(params GameObject[] exceptions) { if (hudButtonsContainer == null) { Logging.Warning("[PlayerHudManager] HUD buttons container not assigned"); return; } HashSet exceptionSet = new HashSet(exceptions); foreach (Transform child in hudButtonsContainer) { bool shouldShow = exceptionSet.Contains(child.gameObject); child.gameObject.SetActive(shouldShow); } Logging.Debug($"[PlayerHudManager] Hidden HUD except {exceptions.Length} exceptions"); } /// /// Common method to set visibility of all HUD elements /// private void SetHudVisibility(bool visible) { if (hudButtonsContainer == null) { Logging.Warning("[PlayerHudManager] HUD buttons container not assigned"); return; } // Set visibility for all HUD children foreach (Transform child in hudButtonsContainer) { child.gameObject.SetActive(visible); } ApplyUIModeOverrides(visible); Logging.Debug($"[PlayerHudManager] {(visible ? "Shown" : "Hidden")} all HUD elements"); } /// /// Applies UI mode-specific visibility rules (e.g., hiding eagleEye in certain modes) /// private void ApplyUIModeOverrides(bool visible) { switch (currentUIMode) { case UIMode.Overworld: if (visible) { eagleEye.SetActive(false); } break; case UIMode.Puzzle: if (visible) { ramaSjangButton.SetActive(false); } break; case UIMode.Minigame: if (visible) { eagleEye.SetActive(false); ramaSjangButton.SetActive(false); scrabBookButton.SetActive(false); } break; case UIMode.HideAll: break; } } /// /// Automatically manages HUD visibility based on page stack state /// private void HandlePageStackChanged(UIPage currentPage) { if (_uiPageController == null) return; // Use LINQ Count() on IEnumerable PageStack property int stackCount = _uiPageController.PageStack.Count(); if (stackCount == 1 && currentPage != null) { // First page just opened - hide HUD HideAllHud(); Logging.Debug("[PlayerHudManager] Page opened, hiding HUD"); } else if (stackCount == 0) { // Last page closed - show HUD ShowAllHud(); Logging.Debug("[PlayerHudManager] All pages closed, showing HUD"); } // If stackCount > 1, we're navigating between pages, keep HUD hidden } /// /// Called when a cinematic starts playing /// private void HandleCinematicStarted() { HideAllHud(); Logging.Debug("[PlayerHudManager] Cinematic started, hiding HUD"); } /// /// Called when a cinematic stops playing /// private void HandleCinematicStopped() { ShowAllHud(); Logging.Debug("[PlayerHudManager] Cinematic stopped, showing HUD"); } /// /// Convenience method to push a page from prefab using the UIPageController /// public void PushPageFromPrefab(GameObject pagePrefab) { if (_uiPageController != null) { _uiPageController.PushPageFromPrefab(pagePrefab); } else { UnityEngine.Debug.LogError("[PlayerHudManager] Cannot push page - UIPageController not found!"); } } #region HUD Element Getters public GameObject GetEagleEye() => eagleEye; public GameObject GetRamaSjangButton() => ramaSjangButton; public GameObject GetScrabookButton() => scrabBookButton; #endregion #region Context-Based Visibility Management /// /// Temporarily shows a HUD element. Returns a context object that restores the previous state when disposed. /// Usage: using (var ctx = PlayerHudManager.Instance.ShowElementTemporarily(myButton)) { /* element is visible */ } /// public HudVisibilityContext ShowElementTemporarily(GameObject element) { if (element == null) { Logging.Warning("[PlayerHudManager] Attempted to show null element"); return null; } return new HudVisibilityContext(element, true); } /// /// Temporarily hides a HUD element. Returns a context object that restores the previous state when disposed. /// Usage: using (var ctx = PlayerHudManager.Instance.HideElementTemporarily(myButton)) { /* element is hidden */ } /// public HudVisibilityContext HideElementTemporarily(GameObject element) { if (element == null) { Logging.Warning("[PlayerHudManager] Attempted to hide null element"); return null; } return new HudVisibilityContext(element, false); } /// /// Temporarily shows multiple HUD elements. Returns a context object that restores all previous states when disposed. /// Usage: using (var ctx = PlayerHudManager.Instance.ShowElementsTemporarily(button1, button2, button3)) { /* elements are visible */ } /// public MultiHudVisibilityContext ShowElementsTemporarily(params GameObject[] elements) { return new MultiHudVisibilityContext(elements, true); } /// /// Temporarily hides multiple HUD elements. Returns a context object that restores all previous states when disposed. /// Usage: using (var ctx = PlayerHudManager.Instance.HideElementsTemporarily(button1, button2, button3)) { /* elements are hidden */ } /// public MultiHudVisibilityContext HideElementsTemporarily(params GameObject[] elements) { return new MultiHudVisibilityContext(elements, false); } #endregion } }