Files
ichni_Official/Assets/Scripts/UI/ChapterSelection/ChapterSelectionUI.cs

247 lines
9.1 KiB
C#
Raw Normal View History

2025-06-09 04:52:14 -04:00
using System;
2025-06-06 10:14:55 -04:00
using System.Collections;
using System.Collections.Generic;
2025-06-09 04:52:14 -04:00
using DG.Tweening;
2025-06-06 10:14:55 -04:00
using Ichni.Menu;
using Ichni.Story;
2025-06-09 04:52:14 -04:00
using Sirenix.OdinInspector;
2026-03-14 03:13:10 -04:00
using SLSUtilities.WwiseAssistance;
2025-06-06 10:14:55 -04:00
using TMPro;
using UnityEngine;
2025-06-14 14:42:49 -04:00
using UnityEngine.Serialization;
2025-06-06 10:14:55 -04:00
using UnityEngine.UI;
namespace Ichni.UI
{
public class ChapterSelectionUI : MonoBehaviour
{
public RectTransform content;
2025-06-09 04:52:14 -04:00
public LayoutElement layoutElement;
2025-08-11 14:04:06 -04:00
public Button expandButton;
2025-06-14 14:42:49 -04:00
public Button enterStorylineButton;
public Button enterSongSelectionButton;
2025-07-08 14:28:40 -04:00
public ChapterSelectionUnit connectedChapter;
2025-06-14 14:42:49 -04:00
public string chapterName;
/// <summary>
/// 当前章节的运行时锁定状态。仅用于 UI 表现;真正进入剧情或选曲前仍会重新调用
/// <see cref="UnlockSaveModule.CanAccessChapter"/>,不能把本字段作为授权依据。
/// </summary>
public bool isLocked;
2025-06-09 04:52:14 -04:00
public bool isExpanded;
public bool isDuringAnimation;
[Title("闭合内容")]
public Image bottomTip;
public Image upperTip;
public Image leftProgressBarBase;
public Image leftProgressBarFill;
2025-06-06 10:14:55 -04:00
public Image avatar;
2025-06-09 04:52:14 -04:00
public Image avatarMask;
public TMP_Text decorationChapterText0;
public TMP_Text decorationChapterText1;
2025-08-22 14:54:40 -04:00
public TMP_Text outerSongProgressText;
2025-06-09 04:52:14 -04:00
public RectTransform titleRect;
public TMP_Text titleText;
2025-08-22 14:54:40 -04:00
[Title("展开内容")]
2025-06-09 04:52:14 -04:00
public RectTransform expansionBackground;
2025-08-22 14:54:40 -04:00
public Image expansionRipple;
public Material rippleMaterial;
public Sequence rippleSequence;
2025-06-09 04:52:14 -04:00
public RectTransform expansionFunctions;
2025-08-22 14:54:40 -04:00
2025-06-09 04:52:14 -04:00
public RectTransform expansionInfos;
2025-08-22 14:54:40 -04:00
public TMP_Text innerSongProgressText;
public TMP_Text fullComboText;
public TMP_Text allPerfectText;
public TMP_Text beatmapProgressText;
2025-06-14 14:42:49 -04:00
public void Initialize(ChapterSelectionUnit chapter)
{
2025-07-08 14:28:40 -04:00
connectedChapter = chapter;
2025-08-22 14:54:40 -04:00
expansionRipple.material = new Material(rippleMaterial);
RefreshUnlockState();
2025-07-08 14:28:40 -04:00
2025-06-14 14:42:49 -04:00
expandButton.onClick.AddListener(() =>
2025-06-09 04:52:14 -04:00
{
if (isDuringAnimation)
{
return;
}
if (isExpanded)
{
2025-06-14 14:42:49 -04:00
Shrink();
2025-06-09 04:52:14 -04:00
}
else
{
Expand();
}
});
2025-06-14 14:42:49 -04:00
enterStorylineButton.onClick.AddListener(() =>
{
if (isDuringAnimation)
{
return;
}
if (!CanEnterChapter())
{
return;
}
2025-06-14 14:42:49 -04:00
2025-08-11 14:04:06 -04:00
ChapterSelectionManager.instance.currentChapter = connectedChapter;
2026-03-14 03:13:10 -04:00
AudioManager.SetSwitch(connectedChapter.chapterSwitch);
2025-06-14 14:42:49 -04:00
ChapterSelectionManager.instance.chapterSelectionUIPage.FadeOut();
2026-07-05 16:08:23 -04:00
StoryManager.instance.OpenChapter(connectedChapter.chapterIndex);
2025-06-14 14:42:49 -04:00
StoryManager.instance.storyUIPage.FadeIn();
});
enterSongSelectionButton.onClick.AddListener(() =>
{
if (isDuringAnimation)
{
return;
}
if (!CanEnterChapter())
{
return;
}
2025-06-14 14:42:49 -04:00
2025-08-11 14:04:06 -04:00
ChapterSelectionManager.instance.currentChapter = connectedChapter;
2026-03-14 03:13:10 -04:00
AudioManager.SetSwitch(connectedChapter.chapterSwitch);
2025-06-14 14:42:49 -04:00
ChapterSelectionManager.instance.chapterSelectionUIPage.FadeOut();
SongSelectionManager.instance.songSelectionUIPage.FadeIn();
});
2025-08-22 14:54:40 -04:00
(int beatmapCount, int finishedSongCount, int finishedBeatmapCount, int fullComboCount, int allPerfectCount) = chapter.GetChapterSaveInfo();
float songProgressPercent = (float)finishedSongCount / chapter.songs.Count * 100f;
outerSongProgressText.text = songProgressPercent.ToString("F2") + "%";
innerSongProgressText.text = songProgressPercent.ToString("F2") + "%";
fullComboText.text = fullComboCount.ToString();
allPerfectText.text = allPerfectCount.ToString();
beatmapProgressText.text = $"{finishedBeatmapCount}/{beatmapCount}";
2025-06-09 04:52:14 -04:00
}
/// <summary>
/// 根据统一内容解锁服务刷新章节入口的可交互状态。
/// 当前 Prefab 尚无专用锁定美术,因此先禁用两个实际入口;后续补充锁图标时,
/// 只需读取 <see cref="isLocked"/>,无需复制解锁判断。
/// </summary>
private void RefreshUnlockState()
{
UnlockSaveModule unlockSaveModule = GameSaveManager.instance?.UnlockSaveModule;
isLocked = unlockSaveModule == null || !unlockSaveModule.CanAccessChapter(connectedChapter);
if (enterStorylineButton != null)
{
enterStorylineButton.interactable = !isLocked;
}
if (enterSongSelectionButton != null)
{
enterSongSelectionButton.interactable = !isLocked;
}
}
/// <summary>
/// 章节的最终入口检查。即使 UI Button 状态被其它脚本意外恢复,也不能绕过解锁规则。
/// </summary>
private bool CanEnterChapter()
{
RefreshUnlockState();
return !isLocked;
}
2025-06-09 04:52:14 -04:00
2025-06-14 14:42:49 -04:00
private void Expand()
2025-06-09 04:52:14 -04:00
{
isExpanded = true;
Sequence expandSequence = DOTween.Sequence();
2025-08-11 14:04:06 -04:00
expandSequence.Append(expansionBackground.DOSizeDelta(new Vector2(1147, 826), 0.3f)
2025-06-09 04:52:14 -04:00
.OnStart(() =>
{
expansionBackground.gameObject.SetActive(true);
}));
expandSequence.Join(DOTween.To(() => layoutElement.preferredWidth,
2025-08-11 14:04:06 -04:00
x => layoutElement.preferredWidth = x, 1147, 0.3f));
expandSequence.Join(avatarMask.rectTransform.DOSizeDelta(new Vector2(1147, 826), 0.3f));
expandSequence.Join(titleRect.DOSizeDelta(new Vector2(0, 100), 0.3f));
expandSequence.Append(expansionInfos.GetComponent<CanvasGroup>().DOFade(1, 0.3f)
2025-06-09 04:52:14 -04:00
.OnStart(()=>
{
expansionInfos.gameObject.SetActive(true);
}));
2025-08-11 14:04:06 -04:00
expandSequence.Join(expansionFunctions.GetComponent<CanvasGroup>().DOFade(1, 0.3f)
2025-06-09 04:52:14 -04:00
.OnStart(() =>
{
expansionFunctions.gameObject.SetActive(true);
}));
2025-06-06 10:14:55 -04:00
2025-08-11 14:04:06 -04:00
expandSequence.Join(bottomTip.DOFade(1f, 0.3f));
expandSequence.Join(upperTip.DOFade(1f, 0.3f));
2025-06-06 10:14:55 -04:00
2025-06-09 04:52:14 -04:00
expandSequence.OnStart(() => isDuringAnimation = true);
2025-08-22 14:54:40 -04:00
expandSequence.OnComplete(() =>
{
isDuringAnimation = false;
expansionRipple.material.SetFloat("_RippleTime", 0f);
rippleSequence = DOTween.Sequence();
rippleSequence.AppendInterval(5f);
rippleSequence.Append(expansionRipple.material.DOFloat(1, "_RippleTime", 2f));
rippleSequence.SetLoops(-1);
rippleSequence.Play();
});
2025-06-09 04:52:14 -04:00
expandSequence.Play();
}
2025-06-14 14:42:49 -04:00
private void Shrink()
2025-06-09 04:52:14 -04:00
{
isExpanded = false;
2025-08-22 14:54:40 -04:00
rippleSequence?.Kill(true);
2025-06-14 14:42:49 -04:00
Sequence shrinkSequence = DOTween.Sequence();
2025-06-09 04:52:14 -04:00
2025-08-11 14:04:06 -04:00
shrinkSequence.Append(bottomTip.DOFade(0f, 0.3f));
shrinkSequence.Join(upperTip.DOFade(0f, 0.3f));
2025-06-09 04:52:14 -04:00
2025-08-11 14:04:06 -04:00
shrinkSequence.Join(expansionInfos.GetComponent<CanvasGroup>().DOFade(0, 0.3f)
2025-06-09 04:52:14 -04:00
.OnComplete(()=>
{
expansionInfos.gameObject.SetActive(false);
}));
2025-08-11 14:04:06 -04:00
shrinkSequence.Join(expansionFunctions.GetComponent<CanvasGroup>().DOFade(0, 0.3f)
2025-06-09 04:52:14 -04:00
.OnComplete(() =>
{
expansionFunctions.gameObject.SetActive(false);
}));
2025-08-22 14:54:40 -04:00
shrinkSequence.Join(titleRect.DOSizeDelta(new Vector2(322, 100), 0.3f));
2025-06-09 04:52:14 -04:00
2025-08-11 14:04:06 -04:00
shrinkSequence.Append(expansionBackground.DOSizeDelta(new Vector2(322, 826), 0.3f)
2025-06-09 04:52:14 -04:00
.OnComplete(() =>
{
expansionBackground.gameObject.SetActive(false);
}));
2025-06-14 14:42:49 -04:00
shrinkSequence.Join(DOTween.To(() => layoutElement.preferredWidth,
2025-08-22 14:54:40 -04:00
x => layoutElement.preferredWidth = x, 322, 0.3f));
shrinkSequence.Join(avatarMask.rectTransform.DOSizeDelta(new Vector2(322, 826), 0.3f));
2025-06-09 04:52:14 -04:00
2025-06-14 14:42:49 -04:00
shrinkSequence.OnStart(() => isDuringAnimation = true);
shrinkSequence.OnComplete(() => isDuringAnimation = false);
2025-06-09 04:52:14 -04:00
2025-06-14 14:42:49 -04:00
shrinkSequence.Play();
2025-06-06 10:14:55 -04:00
}
}
}