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();
}
}
}