318 lines
10 KiB
C#
318 lines
10 KiB
C#
using System.Collections;
|
|
using AibisDream;
|
|
using AibisDream.FixSystem;
|
|
using AibisDream.Framework;
|
|
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<TarotDeck>(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
|
|
|
|
/// <param name="fadeInDuration">null 时使用 TarotDeck 上配置的淡入时长</param>
|
|
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 未赋值,<<flip_tarot>> 翻正面时可能无图。", 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);
|
|
}
|
|
|
|
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<SpriteRenderer>();
|
|
_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;
|
|
}
|
|
}
|
|
}
|