Files
ft/Client/Assets/Scripts/EventSmash/UIPanel/EventSmashProgressCell.cs
2026-06-29 21:18:33 +08:00

478 lines
19 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using cfg;
using GameCore;
using asap.core;
using EventSmash.Manager;
using DG.Tweening;
namespace EventSmash.UIPanel
{
/// <summary>
/// 收集进度展示Cell
/// </summary>
public class EventSmashProgressCell : MonoBehaviour
{
#region
private RectTransform _progressRect;
private TMP_Text _progressText;
private Image _progressIcon;
private RewardItemNew _reward;
private Animation _potRefreshAnimation;
#endregion
[Header("进度(按 Y 映射,不再使用 fillAmount")]
[Tooltip("不勾选:起点 y=0终点 y=当前进度 Rect 的 rect.height勾选使用下方两个自定义值")]
[SerializeField] private bool _useCustomProgressAnchoredY;
[Tooltip("仅勾选「自定义 Y」时生效进度 0 时的 anchoredPosition.y")]
[SerializeField] private float _progressAnchoredYWhenEmpty;
[Tooltip("仅勾选「自定义 Y」时生效进度满时的 anchoredPosition.y")]
[SerializeField] private float _progressAnchoredYWhenFull;
[Header("收集进度上涨(与 img_content 移动同时)")]
[Tooltip("拖特效根节点(如 fx_ui_eventsmashscrap_collect_add_01与进度条补间同启同停未赋值则仅补间")]
[SerializeField] private GameObject _progressIncreaseFxRoot;
[Header("进度节点显隐(按本档 current/target")]
[SerializeField][Range(0,1)]private float NearFullShowRatio = 0.8f;
[Tooltip("未赋值则忽略。收集进度 ≥90% 且未满本档时显示;达到本档 100%(可领档)时隐藏。")]
[SerializeField] private GameObject _visibleFrom90PercentUntilFullObject;
[Header("收集达标:满条 → [可选] ")]
[Tooltip("满条后先 SetActive(true),子节点可用 Animation/Timeline 自控显隐;不赋值则跳过")]
[SerializeField] private GameObject _fullBarCelebrationRoot;
[Tooltip("开始勾选ImageCheck前等待的秒数有庆祝根时在其 SetActive(true) 之后计时。0 表示不额外等待")]
[SerializeField] private float _fullBarWaitBeforeCheckSeconds;
[Tooltip("罐子/进度区域刷新动效,空则跳过")]
[SerializeField] private string _potRefreshAnimClipName = "EventSmashScrapPiplineCheckRefresh";
[Tooltip("刷新动画开始播放后,等待该秒数即继续进度条归零/溢出补间,不必等动画播完。负数=仍按片段全长等待(旧行为)")]
[SerializeField] private float _potRefreshProceedAfterSeconds = 0.3f;
[Header("进度条移动速度")]
[SerializeField] private float _overflowFillDuration = 0.35f;
private int _collectionId;
private EventSmashManager _manager;
private EventSmashData _data;
/// <summary>进度条 anchoredPosition.y 补间。</summary>
private Tween _progressRectYTween;
/// <summary>领奖动效进行中:<see cref="UpdateDisplay"/> 只刷新文案/奖励图标,不 Kill、不强行写条避免打断「当前→满→领奖→归零→剩余」。</summary>
private bool _claimSequenceInProgress;
private void Awake()
{
_potRefreshAnimation = gameObject.GetComponent<Animation>();
_progressRect = gameObject.FindChildGameObject("img_content").GetComponent<RectTransform>();
//_progressText = gameObject.FindChildGameObject("progress_text").GetComponent<TMP_Text>();
_progressIcon = gameObject.FindChildGameObject("img_icon").GetComponent<Image>();
_reward = gameObject.FindChildGameObject("reward").GetComponent<RewardItemNew>();
}
private void GetProgressYBounds(out float yEmpty, out float yFull)
{
if (_useCustomProgressAnchoredY)
{
yEmpty = _progressAnchoredYWhenEmpty;
yFull = _progressAnchoredYWhenFull;
return;
}
yEmpty = 0f;
yFull = _progressRect != null ? _progressRect.rect.height : 0f;
}
/// <summary>按 0~1 进度设置锚点 Y线性映射起点/终点)</summary>
private void SetProgressVisualRatio(float ratioClamped01)
{
if (_progressRect == null)
return;
ratioClamped01 = Mathf.Clamp01(ratioClamped01);
GetProgressYBounds(out var y0, out var y1);
var ap = _progressRect.anchoredPosition;
ap.y = Mathf.Lerp(y0, y1, ratioClamped01);
_progressRect.anchoredPosition = ap;
}
private float GetProgressRatio(int current, int target) =>
target > 0 ? Mathf.Clamp01((float)current / target) : 0f;
/// <summary>≥90% 且未满本档显示本档已满100%)隐藏。</summary>
private void ApplyVisibleFrom90UntilFullObject(int current, int target)
{
var o = _visibleFrom90PercentUntilFullObject;
if (o == null)
return;
if (target <= 0)
{
o.SetActive(false);
return;
}
float ratio = GetProgressRatio(current, target);
bool show = ratio >= NearFullShowRatio && current < target;
o.SetActive(show);
}
private bool TryGetProgressVisualRatio(out float ratio01)
{
ratio01 = 0f;
if (_progressRect == null) return false;
GetProgressYBounds(out var y0, out var y1);
float denom = y1 - y0;
if (Mathf.Abs(denom) < 1e-4f)
return true;
ratio01 = Mathf.Clamp01((_progressRect.anchoredPosition.y - y0) / denom);
return true;
}
/// <summary>
/// 未达领奖档时Grant 已加数据但条仍停在旧位置,下一帧 Refresh 会直接对齐;此处补间到当前数据比例。
/// </summary>
public async Task PlayCollectionProgressVisualCatchUpAsync(EventSmashManager manager, EventSmashData data, int collectionId)
{
if (manager == null || data == null || _progressRect == null) return;
var (current, target) = manager.GetCollectionProgress(data, collectionId, out var collectionConfig);
if (target <= 0) return;
RefreshRewardTextAndIconOnly(collectionConfig, current, target);
if (!TryGetProgressVisualRatio(out var ratioNow)) return;
float ratioTarget = GetProgressRatio(current, target);
if (Mathf.Abs(ratioNow - ratioTarget) < 0.008f) return;
GetProgressYBounds(out var yEmpty, out var yFull);
float lo = Mathf.Min(yEmpty, yFull);
float hi = Mathf.Max(yEmpty, yFull);
_claimSequenceInProgress = true;
try
{
float fromY = Mathf.Clamp(_progressRect.anchoredPosition.y, lo, hi);
float toY = Mathf.Lerp(yEmpty, yFull, ratioTarget);
await TweenProgressAnchoredYAsync(fromY, toY, playIncreaseLeadIn: true);
}
finally
{
_claimSequenceInProgress = false;
_progressRectYTween = null;
}
}
/// <summary>收集物飞向进度条时的落点(与收集配置 ID 对应)</summary>
public RectTransform GetProgressIconRect() =>
_progressIcon != null ? _progressIcon.rectTransform : null;
/// <summary>
/// 初始化进度Cell
/// </summary>
public void Initialize(int collectionId, EventSmashManager manager, EventSmashData data)
{
_collectionId = collectionId;
_manager = manager;
_data = data;
UpdateDisplay();
}
private void KillProgressRectTween()
{
if (_progressRectYTween != null && _progressRectYTween.IsActive())
_progressRectYTween.Kill();
_progressRectYTween = null;
}
/// <summary>只更新数字/奖励/图标,不改进度条位置(用于领奖序列里补间前先同步文案)。</summary>
private void RefreshRewardTextAndIconOnly(EventSmashCollection collectionConfig, int current, int target)
{
if (_progressText != null)
_progressText.text = $"{current}/{target}";
UpdateRewardDisplay(collectionConfig);
var iconName = _data.MainConfig.GetCollectionIcon(_manager.Tables.TbEventSmashCollection, _collectionId);
SetProgressIcon(iconName);
ApplyVisibleFrom90UntilFullObject(current, target);
}
/// <summary>
/// 更新显示
/// </summary>
public void UpdateDisplay()
{
if (_manager == null || _data == null) return;
var (current, target) = _manager.GetCollectionProgress(_data, _collectionId, out var collectionConfig);
if (_claimSequenceInProgress)
{
RefreshRewardTextAndIconOnly(collectionConfig, current, target);
return;
}
KillProgressRectTween();
SetProgressVisualRatio(GetProgressRatio(current, target));
RefreshRewardTextAndIconOnly(collectionConfig, current, target);
}
private void SetProgressAnchoredY(float y)
{
if (_progressRect == null) return;
var p = _progressRect.anchoredPosition;
p.y = y;
_progressRect.anchoredPosition = p;
}
private async Task TweenProgressAnchoredYAsync(float fromY, float toY, bool playIncreaseLeadIn = false)
{
if (_progressRect == null) return;
KillProgressRectTween();
SetProgressAnchoredY(fromY);
if (Mathf.Abs(fromY - toY) < 0.5f)
return;
var fx = _progressIncreaseFxRoot;
bool syncFx = playIncreaseLeadIn && fx != null;
if (syncFx)
fx.SetActive(true);
void EndProgressTweenAndFx()
{
if (syncFx && fx != null)
fx.SetActive(false);
_progressRectYTween = null;
}
_progressRectYTween = DOTween
.To(() => _progressRect.anchoredPosition.y,
y =>
{
var p = _progressRect.anchoredPosition;
p.y = y;
_progressRect.anchoredPosition = p;
},
toY,
_overflowFillDuration)
.SetEase(Ease.OutQuad)
.OnKill(EndProgressTweenAndFx)
.OnComplete(EndProgressTweenAndFx);
await _progressRectYTween.AsyncWaitForCompletion();
}
/// <summary>批量领奖表现开始:锁住进度条刷新,避免虚拟多档与真实数据不一致时 <see cref="UpdateDisplay"/> 抢写。</summary>
public void EnterClaimPresentationMode()
{
_claimSequenceInProgress = true;
}
/// <summary>批量领奖表现结束:恢复刷新并同步最终数据到 UI。</summary>
public void ExitClaimPresentationMode()
{
_claimSequenceInProgress = false;
_progressRectYTween = null;
UpdateDisplay();
}
/// <summary>
/// 第一档:条补间到满 → 勾选(数据尚未扣档)。单砸飞入回溯逻辑与原先整段领奖一致。
/// </summary>
public async Task PlayCollectionFillAndCheckFirstTierAsync(int flyGrantCountForVisualRewind,
bool applySingleSmashFlyVisualRewind)
{
if (_manager == null || _data == null)
return;
if (!_manager.CanClaimCollectionTier(_data, _collectionId))
return;
GetProgressYBounds(out var yEmpty, out var yFull);
float lo = Mathf.Min(yEmpty, yFull);
float hi = Mathf.Max(yEmpty, yFull);
try
{
if (_progressRect != null && applySingleSmashFlyVisualRewind && flyGrantCountForVisualRewind > 0)
{
var (cur, tgt) = _manager.GetCollectionProgress(_data, _collectionId, out var cfg);
RefreshRewardTextAndIconOnly(cfg, cur, tgt);
if (tgt > 0 && cur >= flyGrantCountForVisualRewind)
{
float preRatio = Mathf.Clamp01((float)(cur - flyGrantCountForVisualRewind) / tgt);
SetProgressVisualRatio(preRatio);
}
}
if (_progressRect != null)
{
float startY = Mathf.Clamp(_progressRect.anchoredPosition.y, lo, hi);
await TweenProgressAnchoredYAsync(startY, yFull, playIncreaseLeadIn: true);
}
await RunFullBarCelebrationWaitBeforeCheckAsync();
}
finally
{
if (_fullBarCelebrationRoot != null)
_fullBarCelebrationRoot.SetActive(false);
}
}
/// <summary>
/// 满条后可选显示庆祝根(子物体动效自控显隐),再按配置等待,之后由调用方播勾选;根节点在整段勾选 finally 里统一关。
/// </summary>
private async Task RunFullBarCelebrationWaitBeforeCheckAsync()
{
float wait = Mathf.Max(0f, _fullBarWaitBeforeCheckSeconds);
if (_fullBarCelebrationRoot != null)
_fullBarCelebrationRoot.SetActive(true);
if (wait > 0f)
await Task.Delay(TimeSpan.FromSeconds(wait));
}
/// <summary>
/// 后续档(数据仍未扣):按虚拟 current/target 对齐文案与条起点,再涨到满并播勾选。
/// </summary>
public async Task PlayCollectionFillAndCheckSubsequentTierAsync(int displayCurrent, int displayRequirement,
EventSmashCollection collectionConfig)
{
if (_manager == null || _data == null || collectionConfig == null)
return;
if (displayRequirement <= 0)
return;
GetProgressYBounds(out var yEmpty, out var yFull);
float lo = Mathf.Min(yEmpty, yFull);
float hi = Mathf.Max(yEmpty, yFull);
try
{
RefreshRewardTextAndIconOnly(collectionConfig, displayCurrent, displayRequirement);
SetProgressVisualRatio(GetProgressRatio(displayCurrent, displayRequirement));
if (_progressRect != null)
{
float startY = Mathf.Clamp(_progressRect.anchoredPosition.y, lo, hi);
await TweenProgressAnchoredYAsync(startY, yFull, playIncreaseLeadIn: true);
}
await RunFullBarCelebrationWaitBeforeCheckAsync();
}
finally
{
if (_fullBarCelebrationRoot != null)
_fullBarCelebrationRoot.SetActive(false);
}
}
/// <summary>
/// 所有档扣档并入暂存之后:罐子刷新 → 条归零 → 按真实进度补间溢出。
/// </summary>
public async Task PlayCollectionPostGrantVisualAsync()
{
if (_manager == null || _data == null)
return;
if (_potRefreshAnimation != null && !string.IsNullOrEmpty(_potRefreshAnimClipName))
{
var potClip = _potRefreshAnimation[_potRefreshAnimClipName];
if (potClip == null)
{
Debug.LogWarning(
$"Animation clip '{_potRefreshAnimClipName}' not found on {_potRefreshAnimation.gameObject.name}");
}
else
{
_potRefreshAnimation.Play(_potRefreshAnimClipName);
if (_potRefreshProceedAfterSeconds < 0f)
await Task.Delay(TimeSpan.FromSeconds(potClip.length));
else
await Task.Delay(TimeSpan.FromSeconds(Mathf.Max(0f, _potRefreshProceedAfterSeconds)));
}
}
if (_progressRect == null)
return;
GetProgressYBounds(out var yEmpty, out var yFull);
var (currentAfter, targetAfter) = _manager.GetCollectionProgress(_data, _collectionId, out var collectionConfigAfter);
RefreshRewardTextAndIconOnly(collectionConfigAfter, currentAfter, targetAfter);
float remainderRatio = GetProgressRatio(currentAfter, targetAfter);
SetProgressAnchoredY(yEmpty);
if (remainderRatio > 0.001f)
{
float endY = Mathf.Lerp(yEmpty, yFull, remainderRatio);
await TweenProgressAnchoredYAsync(yEmpty, endY, playIncreaseLeadIn: true);
}
}
/// <summary>
/// 当前档位收集奖励预览(道具图标)。须与掉落解析结果一致,否则仍显示上一态易被误认为「飞入没播」。
/// </summary>
private void UpdateRewardDisplay(EventSmashCollection collectionConfig)
{
if (_reward == null) return;
if (collectionConfig == null || collectionConfig.CollectingRewardList == null ||
collectionConfig.CollectingRewardList.Count == 0)
{
_reward.gameObject.SetActive(false);
return;
}
var (round, _) = _data.CollectionProgressPairs.TryGetValue(_collectionId, out var pair)
? pair
: (0, 0);
int configCount = collectionConfig.CollectingRewardList.Count;
int rewardDropId = collectionConfig.CollectingRewardList[round % configCount];
if (rewardDropId <= 0)
{
_reward.gameObject.SetActive(false);
return;
}
var playerItem = GContext.container.Resolve<PlayerItemData>();
List<ItemData> itemDatas = playerItem.GetItemDataByDropIdLureInflation(_data.InflationRate, rewardDropId);
if (itemDatas == null || itemDatas.Count == 0)
itemDatas = playerItem.GetItemDataByDropIdLureInflation(null, rewardDropId);
if (itemDatas == null || itemDatas.Count == 0)
itemDatas = playerItem.GetItemDataByDropId(rewardDropId);
if (itemDatas != null && itemDatas.Count > 0)
{
_reward.InflationRate = _data.InflationRate;
_reward.SetData(itemDatas[0]);
_reward.gameObject.SetActive(true);
return;
}
_reward.gameObject.SetActive(false);
}
/// <summary>
/// 设置进度图标
/// </summary>
public void SetProgressIcon(string icon)
{
if (_progressIcon != null && icon != null)
{
GContext.container.Resolve<IUIService>().SetImageSprite(_progressIcon, icon);
_progressIcon.gameObject.SetActive(true);
}
}
}
}