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
{
///
/// 收集进度展示Cell
///
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;
/// 进度条 anchoredPosition.y 补间。
private Tween _progressRectYTween;
/// 领奖动效进行中: 只刷新文案/奖励图标,不 Kill、不强行写条,避免打断「当前→满→领奖→归零→剩余」。
private bool _claimSequenceInProgress;
private void Awake()
{
_potRefreshAnimation = gameObject.GetComponent();
_progressRect = gameObject.FindChildGameObject("img_content").GetComponent();
//_progressText = gameObject.FindChildGameObject("progress_text").GetComponent();
_progressIcon = gameObject.FindChildGameObject("img_icon").GetComponent();
_reward = gameObject.FindChildGameObject("reward").GetComponent();
}
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;
}
/// 按 0~1 进度设置锚点 Y(线性映射起点/终点)
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;
/// ≥90% 且未满本档显示,本档已满(100%)隐藏。
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;
}
///
/// 未达领奖档时:Grant 已加数据但条仍停在旧位置,下一帧 Refresh 会直接对齐;此处补间到当前数据比例。
///
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;
}
}
/// 收集物飞向进度条时的落点(与收集配置 ID 对应)
public RectTransform GetProgressIconRect() =>
_progressIcon != null ? _progressIcon.rectTransform : null;
///
/// 初始化进度Cell
///
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;
}
/// 只更新数字/奖励/图标,不改进度条位置(用于领奖序列里补间前先同步文案)。
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);
}
///
/// 更新显示
///
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();
}
/// 批量领奖表现开始:锁住进度条刷新,避免虚拟多档与真实数据不一致时 抢写。
public void EnterClaimPresentationMode()
{
_claimSequenceInProgress = true;
}
/// 批量领奖表现结束:恢复刷新并同步最终数据到 UI。
public void ExitClaimPresentationMode()
{
_claimSequenceInProgress = false;
_progressRectYTween = null;
UpdateDisplay();
}
///
/// 第一档:条补间到满 → 勾选(数据尚未扣档)。单砸飞入回溯逻辑与原先整段领奖一致。
///
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);
}
}
///
/// 满条后可选显示庆祝根(子物体动效自控显隐),再按配置等待,之后由调用方播勾选;根节点在整段勾选 finally 里统一关。
///
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));
}
///
/// 后续档(数据仍未扣):按虚拟 current/target 对齐文案与条起点,再涨到满并播勾选。
///
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);
}
}
///
/// 所有档扣档并入暂存之后:罐子刷新 → 条归零 → 按真实进度补间溢出。
///
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);
}
}
///
/// 当前档位收集奖励预览(道具图标)。须与掉落解析结果一致,否则仍显示上一态易被误认为「飞入没播」。
///
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();
List 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);
}
///
/// 设置进度图标
///
public void SetProgressIcon(string icon)
{
if (_progressIcon != null && icon != null)
{
GContext.container.Resolve().SetImageSprite(_progressIcon, icon);
_progressIcon.gameObject.SetActive(true);
}
}
}
}