using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Playables; namespace AibisDream.UI { public enum TerminalPage { None, Start, Setting, Chapter, } enum TerminalBootPhase { None, PressAnyKey, PlayingIntro, PostIntroPause, } /// /// 主菜单终端 Panel 壳:管理 Start / Chapter / Setting 三个 Page。 /// 冷启动时先走 Intro Timeline(Press Any Key → 播放 → 停顿 → Start);同 session 内回主菜单跳过 Intro。 /// public class TerminalPanel : MonoBehaviour, IUIPanel, ITerminalPanelHost { [SerializeField] private TerminalHeaderView header; [SerializeField] private TerminalStartPanel startPage; [SerializeField] private TerminalSettingPanel settingPage; [SerializeField] private TerminalChapterPanel chapterPage; [SerializeField] private PlayableDirector introDirector; [SerializeField] private float postIntroPause = 0.5f; [SerializeField] private GameObject menuFrame; private readonly Stack _backStack = new(); private TerminalPage _currentPage = TerminalPage.None; private bool _introCompletedThisSession; private TerminalBootPhase _bootPhase = TerminalBootPhase.None; public bool IsOpen => gameObject.activeSelf && (_bootPhase != TerminalBootPhase.None || _currentPage != TerminalPage.None); public bool IsCloseable => false; public TerminalPage CurrentPage => _currentPage; public bool IsInGameContext => false; public void Initialize() { startPage?.SetOwner(this); settingPage?.SetOwner(this); chapterPage?.SetOwner(this); if (!HasRequiredPages()) { Debug.LogError("[TerminalPanel] Missing required pages."); } Close(); } public bool HasRequiredPages() { return startPage != null && settingPage != null && chapterPage != null; } public void Show() { if (_introCompletedThisSession) { TerminalUIAudio.NotifyMainMenuPhase(TerminalMainMenuPhase.Interactive); Open(TerminalPage.Start); return; } if (introDirector == null) { Debug.LogError("[TerminalPanel] introDirector is not assigned. Opening Start directly."); _introCompletedThisSession = true; TerminalUIAudio.NotifyMainMenuPhase(TerminalMainMenuPhase.Interactive); Open(TerminalPage.Start); return; } BeginBootSequence(); } public void Hide() { Close(); } private void Update() { if (_bootPhase != TerminalBootPhase.PressAnyKey) { return; } if (!TryGetBootInput()) { return; } TerminalUIAudio.NotifyMainMenuPhase(TerminalMainMenuPhase.Interactive); StartCoroutine(PlayIntroAndRevealStart()); } private static bool TryGetBootInput() { if (Input.GetMouseButtonDown(0)) { return true; } return Input.anyKeyDown; } private void BeginBootSequence() { gameObject.SetActive(true); _backStack.Clear(); _currentPage = TerminalPage.None; HideAllPages(); _bootPhase = TerminalBootPhase.PressAnyKey; TerminalUIAudio.NotifyMainMenuPhase(TerminalMainMenuPhase.Boot); } private IEnumerator PlayIntroAndRevealStart() { _bootPhase = TerminalBootPhase.PlayingIntro; introDirector.Play(); while (introDirector.state == PlayState.Playing) { yield return null; } _bootPhase = TerminalBootPhase.PostIntroPause; if (postIntroPause > 0f) { yield return new WaitForSecondsRealtime(postIntroPause); } _introCompletedThisSession = true; _bootPhase = TerminalBootPhase.None; menuFrame.SetActive(true); Open(TerminalPage.Start); } public void Open(TerminalPage page) { if (!HasRequiredPages() || page == TerminalPage.None) { return; } if (!IsOpen) { _backStack.Clear(); } gameObject.SetActive(true); SwitchTo(page, pushCurrent: false); } public void SwitchTo(TerminalPage page, bool pushCurrent = true) { if (page == TerminalPage.None || page == _currentPage) { return; } if (pushCurrent && _currentPage != TerminalPage.None) { _backStack.Push(_currentPage); } var previous = _currentPage; _currentPage = page; if (previous == TerminalPage.None) { ShowPage(_currentPage); return; } HidePage(previous, () => ShowPage(_currentPage)); } public void Back() { if (_backStack.Count > 0) { var previous = _backStack.Pop(); var current = _currentPage; _currentPage = previous; HidePage(current, () => ShowPage(_currentPage)); return; } if (_currentPage != TerminalPage.Start) { SwitchTo(TerminalPage.Start, pushCurrent: false); } } public bool HandleEscape() { if (!gameObject.activeSelf) { return false; } if (_bootPhase != TerminalBootPhase.None) { return true; } if (_currentPage == TerminalPage.None) { return false; } if (_currentPage == TerminalPage.Start) { return true; } Back(); return true; } public void CloseThen(Action action) { Close(); action?.Invoke(); } public void Close() { if (introDirector != null) { introDirector.Stop(); } HideAllPages(); _backStack.Clear(); _currentPage = TerminalPage.None; _bootPhase = TerminalBootPhase.None; gameObject.SetActive(false); } public void CloseToMainMenu() { Close(); GameManager.Instance.QuitGame(); } public void SetHeader(string title, string status = "") { header?.SetText(title, status); } private void ShowPage(TerminalPage page) { switch (page) { case TerminalPage.Start: startPage?.Show(); break; case TerminalPage.Setting: settingPage?.Show(); break; case TerminalPage.Chapter: chapterPage?.Show(); break; } } private void HidePage(TerminalPage page, Action onComplete = null) { switch (page) { case TerminalPage.Start: HidePage(startPage, onComplete); break; case TerminalPage.Setting: HidePage(settingPage, onComplete); break; case TerminalPage.Chapter: HidePage(chapterPage, onComplete); break; default: onComplete?.Invoke(); break; } } private static void HidePage(MonoBehaviour page, Action onComplete) { if (page == null) { onComplete?.Invoke(); return; } switch (page) { case TerminalStartPanel startPanel: startPanel.Hide(onComplete); break; case TerminalSettingPanel settingPanel: settingPanel.Hide(onComplete); break; case TerminalChapterPanel chapterPanel: chapterPanel.Hide(onComplete); break; default: page.gameObject.SetActive(false); onComplete?.Invoke(); break; } } private void HideAllPages() { startPage?.Hide(); settingPage?.Hide(); chapterPage?.Hide(); } } }