200 lines
9.6 KiB
C#
200 lines
9.6 KiB
C#
#nullable enable
|
|
|
|
using System.Collections;
|
|
using ReplacedPerson.Core;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
|
|
namespace ReplacedPerson.Runtime
|
|
{
|
|
[DisallowMultipleComponent]
|
|
public sealed class ReplacedSlotResolutionMotion : MonoBehaviour
|
|
{
|
|
private Image? _background;
|
|
private TMP_Text? _mainText;
|
|
private RectTransform? _leftToken;
|
|
private RectTransform? _rightToken;
|
|
private CanvasGroup? _leftGroup;
|
|
private CanvasGroup? _rightGroup;
|
|
private TMP_Text? _leftLabel;
|
|
private TMP_Text? _rightLabel;
|
|
private Coroutine? _animation;
|
|
private Color _baseColor;
|
|
private float _baseFontSize;
|
|
|
|
public bool IsPlaying => _animation != null;
|
|
|
|
private void Awake()
|
|
{
|
|
_background = GetComponent<Image>();
|
|
_mainText = transform.Find("Text")?.GetComponent<TMP_Text>();
|
|
if (_background != null) _baseColor = _background.color;
|
|
if (_mainText != null) _baseFontSize = _mainText.fontSize;
|
|
CreateToken("PlayerToken", out _leftToken, out _leftGroup, out _leftLabel);
|
|
CreateToken("EnemyToken", out _rightToken, out _rightGroup, out _rightLabel);
|
|
SetTokensActive(false);
|
|
}
|
|
|
|
public void Play(ReplacedSlotResolution resolution, int localPlayer)
|
|
{
|
|
if (_animation != null) StopCoroutine(_animation);
|
|
_animation = StartCoroutine(Animate(resolution, localPlayer));
|
|
}
|
|
|
|
private IEnumerator Animate(ReplacedSlotResolution resolution, int localPlayer)
|
|
{
|
|
if (_background == null || _mainText == null || _leftToken == null || _rightToken == null ||
|
|
_leftGroup == null || _rightGroup == null || _leftLabel == null || _rightLabel == null)
|
|
yield break;
|
|
|
|
var localIsOne = localPlayer == 0;
|
|
var localType = localIsOne ? resolution.PlayerOneType : resolution.PlayerTwoType;
|
|
var enemyType = localIsOne ? resolution.PlayerTwoType : resolution.PlayerOneType;
|
|
var localRoll = localIsOne ? resolution.PlayerOneRoll : resolution.PlayerTwoRoll;
|
|
var enemyRoll = localIsOne ? resolution.PlayerTwoRoll : resolution.PlayerOneRoll;
|
|
var localValue = localIsOne ? resolution.PlayerOneValue : resolution.PlayerTwoValue;
|
|
var enemyValue = localIsOne ? resolution.PlayerTwoValue : resolution.PlayerOneValue;
|
|
var localRerolled = localIsOne ? resolution.PlayerOneRerolled : resolution.PlayerTwoRerolled;
|
|
var enemyRerolled = localIsOne ? resolution.PlayerTwoRerolled : resolution.PlayerOneRerolled;
|
|
var localDamage = localIsOne ? resolution.DamageToPlayerOne : resolution.DamageToPlayerTwo;
|
|
var enemyDamage = localIsOne ? resolution.DamageToPlayerTwo : resolution.DamageToPlayerOne;
|
|
|
|
SetTokensActive(true);
|
|
_leftGroup.alpha = 1f;
|
|
_rightGroup.alpha = 1f;
|
|
_leftLabel.text = "我方\n" + TypeName(localType);
|
|
_rightLabel.text = "敌方\n" + TypeName(enemyType);
|
|
_leftToken.GetComponent<Image>().color = TypeColor(localType);
|
|
_rightToken.GetComponent<Image>().color = TypeColor(enemyType);
|
|
_mainText.text = $"槽 {resolution.SlotIndex + 1}\n对位";
|
|
_leftToken.anchoredPosition = new Vector2(-280, 0);
|
|
_rightToken.anchoredPosition = new Vector2(280, 0);
|
|
transform.localScale = Vector3.one;
|
|
|
|
_mainText.fontSize = Mathf.Max(_baseFontSize, 23f);
|
|
const float approachDuration = .28f;
|
|
var elapsed = 0f;
|
|
while (elapsed < approachDuration)
|
|
{
|
|
elapsed += Time.unscaledDeltaTime;
|
|
var t = Mathf.SmoothStep(0, 1, Mathf.Clamp01(elapsed / approachDuration));
|
|
_leftToken.anchoredPosition = Vector2.Lerp(new Vector2(-280, 0), new Vector2(-82, 0), t);
|
|
_rightToken.anchoredPosition = Vector2.Lerp(new Vector2(280, 0), new Vector2(82, 0), t);
|
|
yield return null;
|
|
}
|
|
|
|
transform.localScale = Vector3.one * 1.045f;
|
|
_mainText.text = $"槽 {resolution.SlotIndex + 1}\n投掷中";
|
|
_leftLabel.fontSize = 21;
|
|
_rightLabel.fontSize = 21;
|
|
yield return new WaitForSecondsRealtime(.04f);
|
|
transform.localScale = Vector3.one;
|
|
for (var frame = 0; frame < 8; frame++)
|
|
{
|
|
var leftFace = (frame * 5 + 1) % 6 + 1;
|
|
var rightFace = (frame * 3 + 4) % 6 + 1;
|
|
_leftLabel.text = $"我方\nD6 {leftFace}";
|
|
_rightLabel.text = $"敌方\nD6 {rightFace}";
|
|
_leftToken.localRotation = Quaternion.Euler(0, 0, frame % 2 == 0 ? -2f : 2f);
|
|
_rightToken.localRotation = Quaternion.Euler(0, 0, frame % 2 == 0 ? 2f : -2f);
|
|
yield return new WaitForSecondsRealtime(.075f);
|
|
}
|
|
|
|
_leftToken.localRotation = Quaternion.identity;
|
|
_rightToken.localRotation = Quaternion.identity;
|
|
_leftLabel.text = $"我方\nD6 {Roll(localRoll, localRerolled)}";
|
|
_rightLabel.text = $"敌方\nD6 {Roll(enemyRoll, enemyRerolled)}";
|
|
_mainText.fontSize = Mathf.Max(_baseFontSize, 27f);
|
|
_mainText.text = $"槽 {resolution.SlotIndex + 1}\n骰点 {localRoll} : {enemyRoll}";
|
|
yield return new WaitForSecondsRealtime(.75f);
|
|
var flash = localDamage > 0 ? new Color(.55f, .10f, .09f, 1f) : enemyDamage > 0 ? new Color(.45f, .32f, .10f, 1f) : new Color(.24f, .29f, .27f, 1f);
|
|
_background.color = flash;
|
|
_leftLabel.text = $"我方\n{TypeName(localType)} {localValue}";
|
|
_rightLabel.text = $"敌方\n{TypeName(enemyType)} {enemyValue}";
|
|
_mainText.fontSize = Mathf.Max(_baseFontSize, 21f);
|
|
_mainText.text = $"槽 {resolution.SlotIndex + 1}\n{TypeName(localType)} {localValue} : {enemyValue} {TypeName(enemyType)}\n{resolution.Summary}";
|
|
yield return new WaitForSecondsRealtime(.55f);
|
|
|
|
elapsed = 0f;
|
|
const float fadeDuration = .22f;
|
|
while (elapsed < fadeDuration)
|
|
{
|
|
elapsed += Time.unscaledDeltaTime;
|
|
var t = Mathf.Clamp01(elapsed / fadeDuration);
|
|
_leftGroup.alpha = 1f - t;
|
|
_rightGroup.alpha = 1f - t;
|
|
_background.color = Color.Lerp(flash, _baseColor, t);
|
|
yield return null;
|
|
}
|
|
_background.color = _baseColor;
|
|
_mainText.fontSize = _baseFontSize;
|
|
transform.localScale = Vector3.one;
|
|
SetTokensActive(false);
|
|
_animation = null;
|
|
}
|
|
|
|
private void CreateToken(string tokenName, out RectTransform token, out CanvasGroup group, out TMP_Text label)
|
|
{
|
|
var tokenObject = new GameObject(tokenName, typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(CanvasGroup));
|
|
token = tokenObject.GetComponent<RectTransform>();
|
|
token.SetParent(transform, false);
|
|
token.anchorMin = token.anchorMax = new Vector2(.5f, .5f);
|
|
token.sizeDelta = new Vector2(142, 86);
|
|
tokenObject.GetComponent<Image>().raycastTarget = false;
|
|
group = tokenObject.GetComponent<CanvasGroup>();
|
|
group.blocksRaycasts = false;
|
|
|
|
var labelObject = new GameObject("Label", typeof(RectTransform), typeof(CanvasRenderer), typeof(TextMeshProUGUI));
|
|
var labelRect = labelObject.GetComponent<RectTransform>();
|
|
labelRect.SetParent(token, false);
|
|
labelRect.anchorMin = Vector2.zero;
|
|
labelRect.anchorMax = Vector2.one;
|
|
labelRect.offsetMin = new Vector2(8, 5);
|
|
labelRect.offsetMax = new Vector2(-8, -5);
|
|
label = labelObject.GetComponent<TextMeshProUGUI>();
|
|
label.font = _mainText != null ? _mainText.font : null;
|
|
label.fontSize = 20;
|
|
label.alignment = TextAlignmentOptions.Center;
|
|
label.color = new Color(.96f, .94f, .88f, 1f);
|
|
label.raycastTarget = false;
|
|
}
|
|
|
|
private void SetTokensActive(bool active)
|
|
{
|
|
if (_leftToken != null) _leftToken.gameObject.SetActive(active);
|
|
if (_rightToken != null) _rightToken.gameObject.SetActive(active);
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
if (_animation != null) StopCoroutine(_animation);
|
|
_animation = null;
|
|
if (_background != null) _background.color = _baseColor;
|
|
if (_mainText != null) _mainText.fontSize = _baseFontSize;
|
|
transform.localScale = Vector3.one;
|
|
if (_leftToken != null) _leftToken.localRotation = Quaternion.identity;
|
|
if (_rightToken != null) _rightToken.localRotation = Quaternion.identity;
|
|
SetTokensActive(false);
|
|
}
|
|
|
|
private static string Roll(int value, bool rerolled) => rerolled ? value + "(重掷)" : value.ToString();
|
|
|
|
private static string TypeName(ReplacedEffectType type) => type switch
|
|
{
|
|
ReplacedEffectType.Attack => "攻击",
|
|
ReplacedEffectType.Dodge => "闪避",
|
|
ReplacedEffectType.Counter => "反击",
|
|
_ => "未知"
|
|
};
|
|
|
|
private static Color TypeColor(ReplacedEffectType type) => type switch
|
|
{
|
|
ReplacedEffectType.Attack => new Color(.63f, .13f, .11f, .98f),
|
|
ReplacedEffectType.Dodge => new Color(.18f, .31f, .29f, .98f),
|
|
ReplacedEffectType.Counter => new Color(.42f, .31f, .16f, .98f),
|
|
_ => new Color(.22f, .23f, .23f, .98f)
|
|
};
|
|
}
|
|
}
|