Files
2026-08-02 17:55:26 +08:00

374 lines
10 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections;
using System.Collections.Generic;
using AibisDream.Utility;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.UI;
namespace AibisDream.UI
{
public enum TerminalPage
{
None,
Start,
Setting,
Chapter,
}
enum TerminalBootPhase
{
None,
PressAnyKey,
PlayingIntro,
PostIntroPause,
}
/// <summary>
/// 主菜单终端 Panel 壳:管理 Start / Chapter / Setting 三个 Page。
/// 冷启动时先走 Intro TimelinePress Any Key → 播放 → 停顿 → Start);同 session 内回主菜单跳过 Intro。
/// </summary>
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;
[SerializeField] private Button surveyButton;
[SerializeField] private Button wishlistButton;
[SerializeField] private Button communityButton;
private readonly Stack<TerminalPage> _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;
private void Awake()
{
RegisterLinkButtons();
}
private void OnDestroy()
{
UnregisterLinkButtons();
}
public void Initialize()
{
startPage?.SetOwner(this);
settingPage?.SetOwner(this);
chapterPage?.SetOwner(this);
if (!HasRequiredPages())
{
Debug.LogError("[TerminalPanel] Missing required pages.");
}
Close();
}
private void RegisterLinkButtons()
{
if (surveyButton != null)
surveyButton.onClick.AddListener(PlaytestLinkUtility.OpenSurvey);
if (wishlistButton != null)
wishlistButton.onClick.AddListener(PlaytestLinkUtility.OpenWishlist);
if (communityButton != null)
communityButton.onClick.AddListener(PlaytestLinkUtility.OpenCommunity);
}
private void UnregisterLinkButtons()
{
if (surveyButton != null)
surveyButton.onClick.RemoveListener(PlaytestLinkUtility.OpenSurvey);
if (wishlistButton != null)
wishlistButton.onClick.RemoveListener(PlaytestLinkUtility.OpenWishlist);
if (communityButton != null)
communityButton.onClick.RemoveListener(PlaytestLinkUtility.OpenCommunity);
}
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.None);
}
private IEnumerator PlayIntroAndRevealStart()
{
_bootPhase = TerminalBootPhase.PlayingIntro;
introDirector.Play();
TerminalUIAudio.NotifyMainMenuPhase(TerminalMainMenuPhase.Boot);
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.TryReturnToMainMenu();
}
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();
}
}
}