using System; using System.Collections; using AibisDream.Framework; using AibisDream.Kit; using AibisDream.SaveSystem; using UnityEngine; using UnityEngine.Playables; using UnityEngine.ResourceManagement.AsyncOperations; using Yarn.Unity; namespace AibisDream { public class SubwayCenter : Singleton, ISceneDialogueGate { #region 部分索引 public SpriteRenderer newsSpriteRenderer; public PlayableDirector arriveStation; [Tooltip("车厢加速度驱动;到站时触发把手制动摆动")] public SubwayCarMotion carMotion; public static StateMachine StateMachine { get; private set; } public bool IsDialogueReady { get; private set; } private string _currentNewsSpriteId; #endregion public override void OnSingletonInit() { StateMachine = new StateMachine(CreateState); IsDialogueReady = false; StartCoroutine(InitStateMachineRoutine()); } private IEnumerator InitStateMachineRoutine() { IsDialogueReady = false; yield return StateMachine.Init(OutSideState.Subway); IsDialogueReady = true; } public override void OnSingletonDestroy() { IsDialogueReady = false; } private static IState CreateState(OutSideState nextState, string arg = "") { IState res = nextState switch { OutSideState.Subway => new SubwayState(), OutSideState.SubwayTV => new SubwayTVState(), _ => throw new ArgumentOutOfRangeException(nameof(nextState), nextState, null) }; res.SetArgs(arg); return res; } public IEnumerator ArriveAtStation() { float duration = arriveStation != null && arriveStation.playableAsset != null ? (float)arriveStation.playableAsset.duration : 8.5f; if (carMotion == null) { carMotion = FindFirstObjectByType(); } if (carMotion != null) { carMotion.BeginBraking(duration); } if (arriveStation != null) { arriveStation.Play(); } yield return new WaitForSeconds(duration); } public IEnumerator SwitchNews(string spriteId) { if (string.IsNullOrWhiteSpace(spriteId)) { Debug.LogWarning("[SubwayCenter] spriteId 为空,无法切换新闻图片。"); yield break; } string addressableKey = $"Sprite/News[{spriteId}]"; var handle = ResourceSystem.LoadAsync(addressableKey); yield return handle; if (handle.Status != AsyncOperationStatus.Succeeded || handle.Result == null) { Debug.LogError($"[SubwayCenter] 加载新闻图片失败: {addressableKey}"); yield break; } if (newsSpriteRenderer == null) { Debug.LogWarning("[SubwayCenter] newsSpriteRenderer 未赋值,无法切换新闻图片。"); yield break; } newsSpriteRenderer.sprite = handle.Result; _currentNewsSpriteId = spriteId; } /// Captures the macro subway scene snapshot. public SubwaySnapshotDto CaptureSnapshot() { if (StateMachine == null || !StateMachine.HasState) { return null; } return new SubwaySnapshotDto { state = StateMachine.CurState.ToString(), args = "", newsSpriteId = _currentNewsSpriteId }; } /// Restores the macro subway scene snapshot by jumping directly to the target state. public IEnumerator RestoreSnapshot(SubwaySnapshotDto dto) { if (dto == null || string.IsNullOrEmpty(dto.state)) { yield break; } if (!Enum.TryParse(dto.state, out var state)) { Debug.LogWarning($"[SubwayCenter] Could not parse OutSideState: {dto.state}"); yield break; } yield return StateMachine.SwitchStateImmediate(state, dto.args ?? ""); if (!string.IsNullOrEmpty(dto.newsSpriteId)) { yield return SwitchNews(dto.newsSpriteId); } } } public struct SubwayState : IState { public OutSideState GetState => OutSideState.Subway; public IEnumerator Enter() { yield return CameraKit.Instance.SwitchCamera(CameraEnum.Subway); DialogUIManager.Instance.LoadBubbles(BubbleSlotKit.Instance.CollectData(BubbleSlotEnum.Subway)); } public IEnumerator EnterImmediate() { yield return CameraKit.Instance.SwitchCamera(CameraEnum.Subway, true); DialogUIManager.Instance.LoadBubbles(BubbleSlotKit.Instance.CollectData(BubbleSlotEnum.Subway)); } } public struct SubwayTVState : IState { public OutSideState GetState => OutSideState.SubwayTV; public IEnumerator Enter() { yield return CameraKit.Instance.SwitchCamera(CameraEnum.SubwayTV); // 要加载气泡吗? DialogUIManager.Instance.LoadBubbles(BubbleSlotKit.Instance.CollectData(BubbleSlotEnum.SubwayTV)); } public IEnumerator EnterImmediate() { yield return CameraKit.Instance.SwitchCamera(CameraEnum.SubwayTV, true); DialogUIManager.Instance.LoadBubbles(BubbleSlotKit.Instance.CollectData(BubbleSlotEnum.SubwayTV)); } } public enum OutSideState { Subway, SubwayTV } public static class SubwayYarnCommand { [YarnCommand("switch_outside_to")] public static IEnumerator SwitchOutsideTo(string targetStateStr) { if (!Enum.TryParse(targetStateStr, out OutSideState targetState)) { Debug.Log($"{targetStateStr}不在OutSideState枚举中"); yield break; } yield return SubwayCenter.StateMachine.SwitchState(targetState); yield return null; } [YarnCommand("arrive_at_station")] public static IEnumerator ArriveAtStation() { return SubwayCenter.Instance.ArriveAtStation(); } [YarnCommand("switch_news")] public static IEnumerator SwitchNews(string spriteId) { if (SubwayCenter.Instance == null) { Debug.LogError("[SubwayYarnCommand] SubwayCenter 实例不存在。"); yield break; } yield return SubwayCenter.Instance.SwitchNews(spriteId); } } }