using System.Collections; using AibisDream; using AibisDream.FixSystem; using AibisDream.Framework; using AibisDream.Kit; using DG.Tweening; using UnityEngine; namespace AibisDream.MiniGame.Tarot { public class TarotManager : MonoBehaviour { [Header("View")] [Tooltip("Root view object (toggled on/off)")] [SerializeField] private GameObject tarotView; [Header("Sprites")] [SerializeField] private Sprite frontSprite; [SerializeField] private Sprite backSprite; [Header("Background Dim")] [Tooltip("塔罗出现时的背景压暗强度。")] [SerializeField] [Range(0f, 1f)] private float backgroundDimAlpha = 0.18f; [Tooltip("背景压暗淡入淡出的时长。")] [SerializeField] [Min(0f)] private float backgroundDimFadeDuration = 0.25f; [Header("Dialogue Bubbles")] [Tooltip("退出塔罗时恢复的气泡槽位;不填则尝试 Outside / BodyModule。")] [SerializeField] private BubbleSlotGroup restoreBubbleSlotGroup; private TarotDeck _deck; private bool _isCardSelected; private int _selectedIndex = -1; private BubbleSlotGroupData _savedBubbleData; private bool _bubbleSwapActive; private SpriteRenderer _backgroundDimmer; private Sprite _backgroundDimSprite; private Tween _backgroundDimTween; public TarotDeck Deck => _deck; public bool IsCardSelected => _isCardSelected; public int SelectedIndex => _selectedIndex; public TarotCard SelectedCard => _deck != null ? _deck.SelectedCard : null; private void Start() { FixSystemCenter.SystemDic.Register(this); FindDeck(); FindView(); if (tarotView != null) tarotView.SetActive(false); } private void FindDeck() { if (_deck != null) return; _deck = GetComponentInChildren(true); if (_deck == null) Debug.LogWarning("[TarotManager] TarotDeck not found in children", this); } private void FindView() { if (tarotView != null) return; var t = transform.Find("TarotView"); if (t != null) tarotView = t.gameObject; } #region View Control public void OpenView() { FindView(); if (tarotView != null) tarotView.SetActive(true); gameObject.SetActive(true); } public void CloseView() { if (tarotView != null) tarotView.SetActive(false); } #endregion #region Tarot Flow /// null 时使用 TarotDeck 上配置的淡入时长 public IEnumerator ShowDeck(int cardCount, float? fadeInDuration = null) { _isCardSelected = false; _selectedIndex = -1; OpenView(); FindDeck(); if (_deck == null) { Debug.LogError("[TarotManager] Cannot show deck: TarotDeck is null", this); yield break; } _deck.OnCardSelected -= OnCardSelected; _deck.OnCardSelected += OnCardSelected; TryEnterTarotBubbleMode(); ShowBackgroundDim(); if (backSprite == null) Debug.LogWarning( "[TarotManager] Back Sprite 未赋值,流程生成的牌将沿用预制体 SpriteRenderer 上的图;若预制体也为空则会不可见。请在 Inspector 指定 Back / Front Sprite。", this); if (frontSprite == null) Debug.LogWarning("[TarotManager] Front Sprite 未赋值,<> 翻正面时可能无图。", this); _deck.Setup(cardCount, frontSprite, backSprite); yield return _deck.FadeInStack(fadeInDuration); } public IEnumerator SpreadCards() { if (_deck == null) yield break; yield return _deck.SpreadCards(); } public void EnableSelection() { _isCardSelected = false; _selectedIndex = -1; _deck?.SetInteractable(true); } public IEnumerator WaitForSelection() { EnableSelection(); while (!_isCardSelected) yield return null; } public IEnumerator FlipSelectedCard(TarotOrientation orientation, float duration = 0.5f) { var card = _deck?.SelectedCard; if (card == null) { Debug.LogWarning("[TarotManager] No card selected to flip", this); yield break; } yield return card.Flip(orientation, duration); } public IEnumerator RotateSelectedCard(TarotOrientation orientation, float duration = 0.7f) { var card = _deck?.SelectedCard; if (card == null) { Debug.LogWarning("[TarotManager] No card selected to rotate", this); yield break; } yield return card.SetOrientationAnimated(orientation, duration); } public IEnumerator HideAll(float duration = 0.3f) { HideBackgroundDim(duration); if (_deck != null) yield return _deck.HideAll(duration); CloseView(); RestoreTarotBubbleMode(); } #endregion private void OnCardSelected(TarotCard card) { _isCardSelected = true; _selectedIndex = card.Index; YarnVariableStorage.Instance?.SetValue("$tarotSelectedIndex", (float)card.Index); AudioManager.Instance?.PlaySfx("event:/FollowInput/cardDraw"); } private void OnDestroy() { if (_deck != null) _deck.OnCardSelected -= OnCardSelected; _backgroundDimTween?.Kill(); if (_backgroundDimSprite != null) Destroy(_backgroundDimSprite); RestoreTarotBubbleMode(); } private void ShowBackgroundDim() { EnsureBackgroundDimmer(); if (_backgroundDimmer == null) return; UpdateBackgroundDimmerBounds(); _backgroundDimmer.gameObject.SetActive(true); _backgroundDimTween?.Kill(); _backgroundDimTween = _backgroundDimmer .DOFade(backgroundDimAlpha, backgroundDimFadeDuration) .SetEase(Ease.OutQuad); } private void HideBackgroundDim(float duration) { if (_backgroundDimmer == null) return; _backgroundDimTween?.Kill(); _backgroundDimTween = _backgroundDimmer .DOFade(0f, duration) .SetEase(Ease.InQuad) .OnComplete(() => { if (_backgroundDimmer != null) _backgroundDimmer.gameObject.SetActive(false); }); } private void EnsureBackgroundDimmer() { if (_backgroundDimmer != null) return; var dimmerObject = new GameObject("TarotBackgroundDimmer"); dimmerObject.transform.SetParent(transform, false); _backgroundDimmer = dimmerObject.AddComponent(); _backgroundDimSprite = Sprite.Create( Texture2D.whiteTexture, new Rect(0f, 0f, 1f, 1f), new Vector2(0.5f, 0.5f), 1f); _backgroundDimSprite.name = "TarotBackgroundDimSprite"; _backgroundDimmer.sprite = _backgroundDimSprite; _backgroundDimmer.sortingLayerName = "Background"; _backgroundDimmer.sortingOrder = -1; _backgroundDimmer.color = new Color(0f, 0f, 0f, 0f); dimmerObject.SetActive(false); } private void UpdateBackgroundDimmerBounds() { var camera = Camera.main; if (camera == null || _backgroundDimmer == null) return; if (camera.orthographic) { float height = camera.orthographicSize * 2f; float width = height * camera.aspect; _backgroundDimmer.transform.position = new Vector3( camera.transform.position.x, camera.transform.position.y, 0f); _backgroundDimmer.transform.localScale = new Vector3(width, height, 1f); return; } float distance = Mathf.Abs(camera.transform.position.z); Vector3 bottomLeft = camera.ViewportToWorldPoint(new Vector3(0f, 0f, distance)); Vector3 topRight = camera.ViewportToWorldPoint(new Vector3(1f, 1f, distance)); _backgroundDimmer.transform.position = (bottomLeft + topRight) * 0.5f; _backgroundDimmer.transform.localScale = new Vector3( Mathf.Abs(topRight.x - bottomLeft.x), Mathf.Abs(topRight.y - bottomLeft.y), 1f); } private void TryEnterTarotBubbleMode() { if (!BubbleSlotKit.Instance.TryGet(BubbleSlotEnum.Tarot, out _)) return; var snapshot = CollectRestoreSnapshot(); if (snapshot.bubbleSlots == null || snapshot.bubbleSlots.Length == 0) { Debug.LogWarning( "[TarotManager] 已配置 Tarot 气泡但无法取得有效的恢复快照(请指定 restoreBubbleSlotGroup 或保证 Outside/BodyModule 气泡已注册),跳过气泡切换。", this); return; } _savedBubbleData = snapshot; _bubbleSwapActive = true; BubbleSlotKit.Instance.LoadBubbles(BubbleSlotEnum.Tarot); } private void RestoreTarotBubbleMode() { if (!_bubbleSwapActive) return; _bubbleSwapActive = false; DialogUIManager.Instance.LoadBubbles(_savedBubbleData); } private BubbleSlotGroupData CollectRestoreSnapshot() { if (restoreBubbleSlotGroup != null) return restoreBubbleSlotGroup.CollectData(); if (BubbleSlotKit.Instance.TryGet(BubbleSlotEnum.Outside, out _)) return BubbleSlotKit.Instance.CollectData(BubbleSlotEnum.Outside); if (BubbleSlotKit.Instance.TryGet(BubbleSlotEnum.BodyModule, out _)) return BubbleSlotKit.Instance.CollectData(BubbleSlotEnum.BodyModule); return default; } } }