454 lines
16 KiB
C#
454 lines
16 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public class PlayerHudManager : ManagedBehaviour
|
|
{
|
|
public enum UIMode { Overworld, Puzzle, Minigame, HideAll };
|
|
|
|
/// <summary>
|
|
/// Context object for managing temporary HUD element visibility.
|
|
/// Automatically restores previous visibility state when disposed.
|
|
/// Usage: using (var ctx = PlayerHudManager.Instance.ShowElementTemporarily(myButton)) { ... }
|
|
/// </summary>
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Multi-element visibility context for managing multiple HUD elements at once.
|
|
/// Automatically restores previous visibility states when disposed.
|
|
/// </summary>
|
|
public class MultiHudVisibilityContext : IDisposable
|
|
{
|
|
private readonly List<(GameObject element, bool previousState)> _elements;
|
|
private bool _disposed;
|
|
|
|
internal MultiHudVisibilityContext(IEnumerable<GameObject> 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;
|
|
|
|
private new void Awake()
|
|
{
|
|
base.Awake();
|
|
if (Instance != null)
|
|
{
|
|
Destroy(this);
|
|
return;
|
|
}
|
|
_instance = this;
|
|
|
|
// Get UIPageController on same GameObject
|
|
_uiPageController = GetComponent<UIPageController>();
|
|
if (_uiPageController == null)
|
|
{
|
|
UnityEngine.Debug.LogError("[PlayerHudManager] UIPageController not found on same GameObject!");
|
|
}
|
|
|
|
InitializeReferences();
|
|
}
|
|
|
|
protected override void OnManagedAwake()
|
|
{
|
|
// 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");
|
|
}
|
|
}
|
|
|
|
if (SceneManagerService.Instance != null)
|
|
{
|
|
SceneManagerService.Instance.SceneLoadCompleted += NewSceneLoaded;
|
|
}
|
|
if (SceneManagerService.Instance.CurrentGameplayScene == "AppleHillsOverworld")
|
|
{
|
|
NewSceneLoaded("AppleHillsOverworld");
|
|
}
|
|
if (SceneManagerService.Instance.CurrentGameplayScene == "StartingScene")
|
|
{
|
|
// TODO: Hide all UI until cinematics have played
|
|
NewSceneLoaded("AppleHillsOverworld");
|
|
}
|
|
}
|
|
|
|
protected override void OnDestroy()
|
|
{
|
|
base.OnDestroy();
|
|
|
|
// 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<PlayableDirector>();
|
|
cinematicSprites = currentCinematicPlayer.GetComponent<Image>();
|
|
cinematicBackgroundSprites = CinematicBackground.GetComponent<Image>();
|
|
}
|
|
|
|
private void UpateCinematicReferences(GameObject newCinematicPlayer)
|
|
{
|
|
currentCinematicPlayer = newCinematicPlayer;
|
|
cinematicSprites = currentCinematicPlayer.GetComponent<Image>();
|
|
}
|
|
|
|
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":
|
|
currentUIMode = UIMode.Minigame;
|
|
break;
|
|
}
|
|
|
|
ShowAllHud();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Hides all HUD button elements
|
|
/// </summary>
|
|
public void HideAllHud()
|
|
{
|
|
SetHudVisibility(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shows all HUD button elements
|
|
/// </summary>
|
|
public void ShowAllHud()
|
|
{
|
|
SetHudVisibility(true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Hides all HUD elements except the specified exceptions
|
|
/// </summary>
|
|
/// <param name="exceptions">GameObjects to keep visible</param>
|
|
public void HideAllHudExcept(params GameObject[] exceptions)
|
|
{
|
|
if (hudButtonsContainer == null)
|
|
{
|
|
Logging.Warning("[PlayerHudManager] HUD buttons container not assigned");
|
|
return;
|
|
}
|
|
|
|
HashSet<GameObject> exceptionSet = new HashSet<GameObject>(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");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Common method to set visibility of all HUD elements
|
|
/// </summary>
|
|
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");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Applies UI mode-specific visibility rules (e.g., hiding eagleEye in certain modes)
|
|
/// </summary>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Automatically manages HUD visibility based on page stack state
|
|
/// </summary>
|
|
private void HandlePageStackChanged(UIPage currentPage)
|
|
{
|
|
if (_uiPageController == null) return;
|
|
|
|
// Use LINQ Count() on IEnumerable<UIPage> 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
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when a cinematic starts playing
|
|
/// </summary>
|
|
private void HandleCinematicStarted()
|
|
{
|
|
HideAllHud();
|
|
Logging.Debug("[PlayerHudManager] Cinematic started, hiding HUD");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when a cinematic stops playing
|
|
/// </summary>
|
|
private void HandleCinematicStopped()
|
|
{
|
|
ShowAllHud();
|
|
Logging.Debug("[PlayerHudManager] Cinematic stopped, showing HUD");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Convenience method to push a page from prefab using the UIPageController
|
|
/// </summary>
|
|
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
|
|
|
|
/// <summary>
|
|
/// 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 */ }
|
|
/// </summary>
|
|
public HudVisibilityContext ShowElementTemporarily(GameObject element)
|
|
{
|
|
if (element == null)
|
|
{
|
|
Logging.Warning("[PlayerHudManager] Attempted to show null element");
|
|
return null;
|
|
}
|
|
return new HudVisibilityContext(element, true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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 */ }
|
|
/// </summary>
|
|
public HudVisibilityContext HideElementTemporarily(GameObject element)
|
|
{
|
|
if (element == null)
|
|
{
|
|
Logging.Warning("[PlayerHudManager] Attempted to hide null element");
|
|
return null;
|
|
}
|
|
return new HudVisibilityContext(element, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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 */ }
|
|
/// </summary>
|
|
public MultiHudVisibilityContext ShowElementsTemporarily(params GameObject[] elements)
|
|
{
|
|
return new MultiHudVisibilityContext(elements, true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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 */ }
|
|
/// </summary>
|
|
public MultiHudVisibilityContext HideElementsTemporarily(params GameObject[] elements)
|
|
{
|
|
return new MultiHudVisibilityContext(elements, false);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|
|
|