using UnityEngine; namespace NightclubArcadia.UI.Menu { /// /// Opens and closes the player menu from Tab (PlayerMenuInput) or an /// external Open() call (portrait click). Holds a PlayerControlLock so /// opening the menu during dialogue does not re-enable movement when the /// menu is closed — the dialogue holder remains in the lock set. /// public sealed class PlayerMenuController : MonoBehaviour { [SerializeField] GameObject menuRoot; [SerializeField] PlayerMenuInput menuInput; [SerializeField] PlayerControlLock controlLock; [SerializeField] MenuTabBar tabBar; bool isOpen; void OnEnable() { SubscribeToMenuInput(); } void Start() { // Re-run unconditionally: when this component is created by // UILayerBootstrap, AddComponent fires OnEnable *before* menuInput // has been assigned, so the OnEnable subscription is a no-op. // Subscribing again here (idempotent via -= then +=) is what makes // Tab work on both the bootstrap and the authored-prefab paths. SubscribeToMenuInput(); if (controlLock == null) { controlLock = FindFirstObjectByType(); } if (menuRoot != null) { PanelVisibility.Hide(menuRoot); } } /// /// Resolves if unassigned and (re)subscribes. /// Safe to call repeatedly — the -= before += keeps it single-shot. /// void SubscribeToMenuInput() { if (menuInput == null) { menuInput = FindFirstObjectByType(); } if (menuInput != null) { menuInput.MenuToggleRequested -= Toggle; menuInput.MenuToggleRequested += Toggle; } } void OnDisable() { if (menuInput != null) { menuInput.MenuToggleRequested -= Toggle; } if (isOpen) { // Ensure we release the lock if destroyed while open. CloseInternal(notifyState: false); } } public void Toggle() { if (isOpen) { Close(); } else { Open(); } } public void Open() { if (isOpen) { return; } isOpen = true; if (menuRoot != null) { PanelVisibility.Show(menuRoot); } UIStateController.Instance?.SetMenuOpen(true); controlLock?.Acquire(this); tabBar?.SelectTab(0); } public void Close() { if (!isOpen) { return; } CloseInternal(notifyState: true); } void CloseInternal(bool notifyState) { isOpen = false; if (menuRoot != null) { PanelVisibility.Hide(menuRoot); } if (notifyState) { UIStateController.Instance?.SetMenuOpen(false); } // Tab during dialogue works and is the whole point of the // ref-counted lock: the dialogue holder is still in the set, so // closing the menu leaves the player locked and the conversation // intact. controlLock?.Release(this); } } }