1050 lines
43 KiB
C#
1050 lines
43 KiB
C#
using asap.core;
|
|
using cfg;
|
|
using DG.Tweening;
|
|
using EnhancedUI.EnhancedScroller;
|
|
using game;
|
|
using Game;
|
|
using GameCore;
|
|
using Newtonsoft.Json;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using TMPro;
|
|
using UniRx;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
using static EnhancedUI.EnhancedScroller.EnhancedScroller;
|
|
using Tween = DG.Tweening.Tween;
|
|
|
|
namespace Assets.Scripts.UI.JigsawPuzzle
|
|
{
|
|
public class JigsawPuzzlePanel : BasePanel, IEnhancedScrollerDelegate
|
|
{
|
|
#region UI_ELEMENTS
|
|
|
|
//top ui
|
|
private Button btn_questionmark;
|
|
|
|
private TextMeshProUGUI text_time;
|
|
private Image bar;
|
|
private TextMeshProUGUI text_progress;
|
|
private RewardItemNew p_current_reward;
|
|
private Image p_icon_piece;
|
|
private GameObject fx_ui_jigsawpuzzlebuilding_reward_light;
|
|
private GameObject fx_ui_jigsawpuzzlebuilding_light;
|
|
|
|
//center ui
|
|
private Toggle[] toggles;
|
|
|
|
private GameObject[] selecteds;
|
|
private JigsawPuzzleSlotsComponent slots;
|
|
private EnhancedScroller enhancedScroller;
|
|
private JigsawPuzzlePictureCell cellView;
|
|
private RewardFlyBatchController RewardFlyBatchCtrl;
|
|
|
|
//botom ui
|
|
private Button btn_spin;
|
|
|
|
private Button btn_add;
|
|
private Image p_icon_ticket;
|
|
private TextMeshProUGUI p_text_num_item;
|
|
private TextMeshProUGUI p_repeat_text_num;
|
|
private Image p_repeat_bar;
|
|
private Image p_repeat_icon_ticket;
|
|
private GameObject fx_ui_jigsawpuzzlebuilding_piece_light;
|
|
|
|
private Button btn_close;
|
|
|
|
private Button btn_repeat_piece;
|
|
private GameObject piece_tips;
|
|
private Button p_btn_close_tips;
|
|
#endregion UI_ELEMENTS
|
|
|
|
#region 动效参数
|
|
[Header("progress")]
|
|
[SerializeField, Min(0)] private float progressTime = 0.5f;
|
|
[SerializeField, Min(0)] private float repeatPieceProgressTime = 0.5f;
|
|
|
|
[Header("item")]
|
|
[SerializeField, Min(0)] private float itemDelayShow = 0.5f;
|
|
|
|
[Header("rewardFly")]
|
|
[SerializeField, Min(0)] private float rewardFlyStartIconSize = 180f;
|
|
|
|
[SerializeField, Min(0)] private float rewardFlyTargetIconSize = 100f;
|
|
[SerializeField, Min(0)] private float rewardFlyEndAwaitTime = 0.2f;
|
|
|
|
[Header("tab effect")]
|
|
[SerializeField, Min(0)] private float tabSwitchEachTime = 0.5f;
|
|
|
|
[SerializeField, Min(0)] private float tabSwitchEndAwaitTime = 0.2f;
|
|
[SerializeField] private Ease ease = Ease.InOutCirc;
|
|
|
|
[Header("button click")]
|
|
[SerializeField] private float cdTime= 0.5f;
|
|
#endregion 动效参数
|
|
|
|
private JigsawPuzzleManager _jigsawPuzzleManager;
|
|
private JigsawPuzzleManager JigsawPuzzleManager => _jigsawPuzzleManager ??= GContext.container.Resolve<JigsawPuzzleManager>();
|
|
private JigsawPuzzleData Data => JigsawPuzzleManager?.Data;
|
|
|
|
private float cellViewSize => cellView.GetComponent<RectTransform>()?.sizeDelta.x ?? 1020f;
|
|
|
|
private List<int> targetIndex;
|
|
|
|
private int delayShowPictureID = 0;
|
|
private int currentSelectedToggleIndex = 0;
|
|
private List<EventJigsawPuzzleReward> puzzleRewards;
|
|
private List<EventJigsawPuzzleReward> piecePuzzleRewards;
|
|
private int pieceAmount => cellView?.PieceCount ?? 6;
|
|
private string flyTrailName = "fx_ui_jigsawpuzzlebuilding_piece_flytrail0";
|
|
private IDeferredRewardStashService _service;
|
|
private IDeferredRewardStashService service => _service ??= GContext.container.Resolve<IDeferredRewardStashService>();
|
|
|
|
private const string GuideKey = "JigsawPuzzleGuide";
|
|
|
|
private float lastSpinClickTime;
|
|
private void Awake()
|
|
{
|
|
//top ui
|
|
btn_questionmark = this.gameObject.FindChildGameObject("p_panel_btn_questionmark").GetComponent<Button>();
|
|
btn_questionmark.onClick.AddListener(OnClick_btn_questionmark);
|
|
text_time = this.gameObject.FindChildGameObject(nameof(text_time)).GetComponent<TextMeshProUGUI>();
|
|
p_current_reward = this.gameObject.FindChildGameObject(nameof(p_current_reward)).GetComponent<RewardItemNew>();
|
|
bar = this.gameObject.FindChildGameObject(nameof(bar)).GetComponent<Image>();
|
|
text_progress = this.gameObject.FindChildGameObject(nameof(text_progress)).GetComponent<TextMeshProUGUI>();
|
|
p_icon_piece = this.gameObject.FindChildGameObject(nameof(p_icon_piece)).GetComponent<Image>();
|
|
fx_ui_jigsawpuzzlebuilding_reward_light = this.gameObject.FindChildGameObject(nameof(fx_ui_jigsawpuzzlebuilding_reward_light));
|
|
fx_ui_jigsawpuzzlebuilding_light= this.gameObject.FindChildGameObject(nameof(fx_ui_jigsawpuzzlebuilding_light));
|
|
//center ui
|
|
slots = this.GetComponentInChildren<JigsawPuzzleSlotsComponent>();
|
|
cellView = this.GetComponentInChildren<JigsawPuzzlePictureCell>();
|
|
enhancedScroller = this.gameObject.FindChildGameObject("p_picture").GetComponent<EnhancedScroller>();
|
|
var tab = this.gameObject.FindChildGameObject("tab");
|
|
toggles = tab.GetComponentsInChildren<Toggle>(true);
|
|
selecteds = new GameObject[toggles.Length];
|
|
for (int i = 0; i <toggles.Length; i++)
|
|
{
|
|
selecteds[i] = toggles[i].gameObject.FindChildGameObject($"tab{i+1}_sel");
|
|
}
|
|
|
|
RewardFlyBatchCtrl = this.gameObject.FindChildGameObject(nameof(RewardFlyBatchCtrl)).GetComponent<RewardFlyBatchController>();
|
|
|
|
//bottom ui
|
|
btn_spin = this.gameObject.FindChildGameObject(nameof(btn_spin)).GetComponentInChildren<Button>();
|
|
btn_spin.onClick.AddListener(OnClick_btn_spin);
|
|
btn_add = this.gameObject.FindChildGameObject(nameof(btn_add)).GetComponentInChildren<Button>();
|
|
btn_add.onClick.AddListener(OnClick_btn_add);
|
|
p_icon_ticket = this.gameObject.FindChildGameObject(nameof(p_icon_ticket)).GetComponent<Image>();
|
|
p_text_num_item = this.gameObject.FindChildGameObject(nameof(p_text_num_item)).GetComponent<TextMeshProUGUI>();
|
|
p_repeat_text_num = this.gameObject.FindChildGameObject(nameof(p_repeat_text_num)).GetComponent<TextMeshProUGUI>();
|
|
p_repeat_bar = this.gameObject.FindChildGameObject(nameof(p_repeat_bar)).GetComponent<Image>();
|
|
p_repeat_icon_ticket = this.gameObject.FindChildGameObject(nameof(p_repeat_icon_ticket)).GetComponent<Image>();
|
|
fx_ui_jigsawpuzzlebuilding_piece_light = this.gameObject.FindChildGameObject(nameof(fx_ui_jigsawpuzzlebuilding_piece_light));
|
|
|
|
btn_close = this.gameObject.FindChildGameObject(nameof(btn_close)).GetComponent<Button>();
|
|
|
|
btn_repeat_piece = this.gameObject.FindChildGameObject(nameof(btn_repeat_piece)).GetComponent<Button>();
|
|
btn_repeat_piece.onClick.AddListener(OnClick_btn_repeat_piece);
|
|
piece_tips = this.gameObject.FindChildGameObject(nameof(piece_tips));
|
|
p_btn_close_tips = this.gameObject.FindChildGameObject(nameof(p_btn_close_tips)).GetComponent<Button>();
|
|
p_btn_close_tips.onClick.AddListener(OnClick_p_btn_close_tips);
|
|
|
|
Observable.Interval(TimeSpan.FromSeconds(1f)).Subscribe(UpdateTimer).AddTo(disposables);
|
|
}
|
|
|
|
protected override void OnDestroy()
|
|
{
|
|
_processTaskCancellationTokenSource.Cancel();
|
|
_processTaskCancellationTokenSource.Dispose();
|
|
GContext.Publish(new EventMainBGM());
|
|
btn_questionmark.onClick.RemoveAllListeners();
|
|
btn_spin.onClick.RemoveAllListeners();
|
|
btn_repeat_piece.onClick.RemoveAllListeners();
|
|
p_btn_close_tips.onClick.RemoveAllListeners();
|
|
btn_add?.onClick?.RemoveAllListeners();
|
|
|
|
puzzleRewards?.Clear();
|
|
puzzleRewards = null;
|
|
piecePuzzleRewards?.Clear();
|
|
piecePuzzleRewards = null;
|
|
targetIndex?.Clear();
|
|
targetIndex = null;
|
|
_cancellationTokenSource?.Cancel();
|
|
_cancellationTokenSource?.Dispose();
|
|
_cancellationTokenSource = null;
|
|
_tasks.Clear();
|
|
_tasks = null;
|
|
base.OnDestroy();
|
|
}
|
|
|
|
private void UpdateTimer(long obj)
|
|
{
|
|
text_time.text = ConvertTools.ConvertTime2(JigsawPuzzleManager?.Data?.RemainingTime ?? default);
|
|
}
|
|
|
|
protected override void Start()
|
|
{
|
|
base.Start();
|
|
GContext.OnEvent<RewardPanelClose>().Subscribe(OnRewardPanelClose).AddTo(disposables);
|
|
GContext.Publish(new EventBGMSound(Data.EventMain.Bgm));
|
|
InitData();
|
|
InitUI();
|
|
enhancedScroller.Delegate = this;
|
|
enhancedScroller.ReloadData();
|
|
_ = SetSelectedToggle(0, isNeedAni: false);
|
|
UpdateTimer(0);
|
|
var guideValue = PlayerPrefs.GetInt($"{GuideKey}_{Data.EventID}");
|
|
if (guideValue == 0)
|
|
{
|
|
OnClick_btn_questionmark();
|
|
PlayerPrefs.SetInt($"{GuideKey}_{Data.EventID}", 1);
|
|
}
|
|
}
|
|
|
|
public void OnRewardPanelClose(RewardPanelClose e)
|
|
{
|
|
ShowItemCount();
|
|
}
|
|
|
|
private void InitData()
|
|
{
|
|
if (Data == null) return;
|
|
|
|
puzzleRewards = JigsawPuzzleManager.GetEventRewards();
|
|
piecePuzzleRewards = puzzleRewards.Where((x) => x.PictureID > 0)
|
|
.GroupBy(x=>x.PictureID)
|
|
.Select(g=>g.First()).ToList();
|
|
piecePuzzleRewards.Sort((x, y) => x.Quality.CompareTo(y.Quality));
|
|
if (piecePuzzleRewards.Count == Data.CompletedPicCount)
|
|
{
|
|
Data.CompletedRoundCount += 1;
|
|
puzzleRewards = JigsawPuzzleManager.GetEventRewards();
|
|
piecePuzzleRewards = puzzleRewards.Where((x) => x.PictureID > 0)
|
|
.GroupBy(x=>x.PictureID)
|
|
.Select(g=>g.First()).ToList();
|
|
piecePuzzleRewards.Sort((x, y) => x.Quality.CompareTo(y.Quality));
|
|
Data.RefreshCache();
|
|
JigsawPuzzleManager.SaveData();
|
|
}
|
|
if (Data.RoundConfig.PictureList.Count > Data.ProgressPairs.Count)
|
|
{
|
|
foreach (var id in Data.RoundConfig.PictureList)
|
|
{
|
|
if (!Data.ProgressPairs.ContainsKey(id))
|
|
Data.ProgressPairs.TryAdd(id, new List<int>());
|
|
}
|
|
}
|
|
slots.SetData(puzzleRewards);
|
|
}
|
|
|
|
private void InitUI()
|
|
{
|
|
text_time.text = string.Empty;
|
|
text_progress.text = string.Empty;
|
|
bar.fillAmount = 0;
|
|
fx_ui_jigsawpuzzlebuilding_reward_light.SetActive(false);
|
|
p_repeat_text_num.text = string.Empty;
|
|
p_text_num_item.text = string.Empty;
|
|
p_repeat_bar.fillAmount = 0;
|
|
p_current_reward.gameObject.SetActive(false);
|
|
cellView.gameObject.SetActive(false);
|
|
fx_ui_jigsawpuzzlebuilding_piece_light.SetActive(false);
|
|
piece_tips.SetActive(false);
|
|
|
|
if (toggles.Length != selecteds.Length)
|
|
{
|
|
Debug.LogError("tab normal gameobj length != selected gameobj length is true");
|
|
}
|
|
for (int i = 0; i < toggles.Length && i < selecteds.Length; i++)
|
|
{
|
|
var index = i;
|
|
var toggle = toggles[i];
|
|
if (i >= piecePuzzleRewards.Count)
|
|
{
|
|
toggle.gameObject.SetActive(false);
|
|
selecteds[i].gameObject.SetActive(false);
|
|
continue;
|
|
}
|
|
|
|
var reward = piecePuzzleRewards[i];
|
|
var image = toggle.gameObject.FindChildGameObject("piece").GetComponentInChildren<Image>();
|
|
_ = GContext.container.Resolve<IUIService>().SetImageSprite(image, $"{JigsawPuzzleManager.PriceImgName}{reward.Quality}");
|
|
toggle.isOn = false;
|
|
toggle.onValueChanged.RemoveAllListeners();
|
|
toggle.onValueChanged.AddListener((isOn) =>
|
|
{
|
|
if (isOn)
|
|
OnSelect(index);
|
|
});
|
|
var selectedImg = selecteds[i].FindChildGameObject("piece").GetComponentInChildren<Image>();
|
|
_ = GContext.container.Resolve<IUIService>().SetImageSprite(selectedImg, $"{JigsawPuzzleManager.PriceImgName}{reward.Quality}");
|
|
Data.TryGetProgressPairsValue(reward.PictureID, out var pieces);
|
|
selecteds[i].FindChildGameObject("done")?.SetActive(pieces.Count == pieceAmount);
|
|
toggle.gameObject.FindChildGameObject("done")?.SetActive(pieces.Count == pieceAmount);
|
|
}
|
|
_ = GContext.container.Resolve<IUIService>().SetImageSprite(p_repeat_icon_ticket, $"{JigsawPuzzleManager.PriceImgName}6");
|
|
|
|
if (Data == null) return;
|
|
|
|
var itemConfig = Data.Tables.TbItem.GetOrDefault(Data.EventMain.ItemID);
|
|
_ = GContext.container.Resolve<IUIService>().SetImageSprite(p_icon_ticket, itemConfig.Icon);
|
|
}
|
|
|
|
private void ShowUI()
|
|
{
|
|
if (Data == null) return;
|
|
|
|
ShowItemCount();
|
|
ShowRepeatProgress(Data.PieceRepeatAmount);
|
|
|
|
var puzzleReward = GetSelectedPuzzleReward();
|
|
if (puzzleReward == null) return;
|
|
|
|
_ = GContext.container.Resolve<IUIService>().SetImageSprite(p_icon_piece, $"{JigsawPuzzleManager.PriceImgName}{puzzleReward.Quality}");
|
|
|
|
var puzzlePicture = Data.Tables.TbEventJigsawPuzzlePicture.GetOrDefault(puzzleReward.PictureID);
|
|
if (puzzlePicture == null)
|
|
{
|
|
Debug.LogError($"{this.GetType().Name} 配置错误 puzzlePicture is null PictureID {puzzleReward.PictureID}");
|
|
}
|
|
var reward = GContext.container.Resolve<PlayerItemData>().GetItemDataByDropIdLureInflation(Data.InflationRate, puzzlePicture.DropID).FirstOrDefault();
|
|
p_current_reward.SetData(reward);
|
|
p_current_reward.gameObject.SetActive(true);
|
|
}
|
|
|
|
private void ShowItemCount()
|
|
{
|
|
var itemAmount = JigsawPuzzleManager.GetTokenCount();
|
|
p_text_num_item.text = $"{itemAmount}";
|
|
}
|
|
|
|
private void ShowRepeatProgress(int amount, bool isPlayAni = false)
|
|
{
|
|
if (isPlayAni)
|
|
{
|
|
p_repeat_bar.DOFillAmount((float)amount / Data.SameFragmentExchange, repeatPieceProgressTime);
|
|
}
|
|
else
|
|
p_repeat_bar.fillAmount = (float)amount / Data.SameFragmentExchange;
|
|
|
|
p_repeat_text_num.text = $"{amount}/{Data.SameFragmentExchange}";
|
|
}
|
|
|
|
private void ShowProgressUI(List<int> unlockPieces, bool isDelayShowProgress = false, bool isPlayAni = false)
|
|
{
|
|
var count = unlockPieces?.Count ?? 0;
|
|
if (isDelayShowProgress && count > 0)
|
|
count--;
|
|
|
|
bar.fillAmount = (float)count / pieceAmount;
|
|
text_progress.text = $"{count}/{pieceAmount}";
|
|
|
|
if (!isPlayAni)
|
|
return;
|
|
|
|
count++;
|
|
bar.DOFillAmount((float)count / pieceAmount, progressTime);
|
|
text_progress.text = $"{count}/{pieceAmount}";
|
|
}
|
|
private void ShowTabDone(int toggleIndex, bool isDone)
|
|
{
|
|
var doneSelectedObj = selecteds[toggleIndex].FindChildGameObject("done");
|
|
doneSelectedObj?.SetActive(doneSelectedObj.activeInHierarchy || isDone);
|
|
|
|
var doneObj = toggles[toggleIndex].gameObject.FindChildGameObject("done");
|
|
doneObj.SetActive(doneObj.activeInHierarchy || isDone);
|
|
}
|
|
#region toggle
|
|
|
|
private bool _toggleScrolling = false;
|
|
private async Task SetSelectedToggle(int index, bool isClick = false, bool isNeedAni = true, bool isShowProgress = true)
|
|
{
|
|
if (_toggleScrolling)
|
|
return;
|
|
if(!isClick)
|
|
_toggleScrolling = true; // 在方法开始时设置标志
|
|
if (index < 0 || index >= toggles.Length || index >= selecteds.Length)
|
|
{
|
|
_toggleScrolling = false;
|
|
return;
|
|
}
|
|
|
|
if (!isClick)
|
|
{
|
|
toggles[currentSelectedToggleIndex].isOn = false;
|
|
toggles[index].isOn = true;
|
|
}
|
|
selecteds[currentSelectedToggleIndex].SetActive(false);
|
|
selecteds[index].SetActive(true);
|
|
|
|
var time = Mathf.Abs(currentSelectedToggleIndex - index) * tabSwitchEachTime;
|
|
currentSelectedToggleIndex = index;
|
|
ShowUI();
|
|
if (isShowProgress)
|
|
{
|
|
var reward = piecePuzzleRewards[index];
|
|
Data.TryGetProgressPairsValue(reward.PictureID, out var pieces);
|
|
var isDone = pieces.Count == pieceAmount;
|
|
ShowTabDone(index, isDone);
|
|
ShowRewardState(isDone);
|
|
ShowProgressUI(pieces, false);
|
|
}
|
|
|
|
if (!isNeedAni)
|
|
{
|
|
_toggleScrolling = false;
|
|
return;
|
|
}
|
|
|
|
|
|
var positon = enhancedScroller.GetScrollPositionForDataIndex(index, CellViewPositionEnum.Before);
|
|
if (Mathf.Approximately(enhancedScroller.ScrollPosition, positon))
|
|
{
|
|
_toggleScrolling = false;
|
|
return;
|
|
}
|
|
|
|
|
|
// Debug.LogError($"SetSelectedToggle enhancedScroller _toggleScrolling {_toggleScrolling} index {index}");
|
|
_tween?.Kill(true);
|
|
_tween = enhancedScroller.Container.DOAnchorPos(new Vector2(-positon, 0), time)
|
|
.SetEase(ease);
|
|
await _tween.AsyncWaitForCompletion();
|
|
await Awaiters.Seconds(tabSwitchEndAwaitTime);
|
|
//Debug.LogError($"SetSelectedToggle enhancedScroller _toggleScrolling {_toggleScrolling} index {index}");
|
|
}
|
|
|
|
private Tween _tween;
|
|
private void ShowRewardState(bool isDone)
|
|
{
|
|
p_current_reward.SetReceived(isDone);
|
|
fx_ui_jigsawpuzzlebuilding_light.SetActive(!isDone);
|
|
}
|
|
|
|
public EventJigsawPuzzleReward GetSelectedPuzzleReward()
|
|
{
|
|
if (currentSelectedToggleIndex < 0 || currentSelectedToggleIndex >= piecePuzzleRewards.Count)
|
|
return null;
|
|
return piecePuzzleRewards[currentSelectedToggleIndex];
|
|
}
|
|
|
|
public void OnSelect(int index)
|
|
{
|
|
if (isPlaying)
|
|
return;
|
|
|
|
GContext.Publish(new EventUISound($"audio_ui_jigsawpuzzlebuilding_tab"));
|
|
_ = SetSelectedToggle(index, true);
|
|
}
|
|
|
|
#endregion toggle
|
|
|
|
#region button click
|
|
private async void OnClick_p_btn_close_tips()
|
|
{
|
|
var ani = piece_tips.GetComponent<Animation>();
|
|
if (ani)
|
|
{
|
|
var aniName = "tips_common_out";
|
|
var time = ani[aniName].length;
|
|
ani.Play(aniName);
|
|
await Awaiters.Seconds(time);
|
|
piece_tips.SetActive(false);
|
|
}
|
|
else
|
|
{
|
|
piece_tips.SetActive(false);
|
|
}
|
|
}
|
|
|
|
private void OnClick_btn_repeat_piece()
|
|
{
|
|
piece_tips.SetActive(true);
|
|
piece_tips.GetComponent<Animation>()?.Play("tips_common_show");
|
|
}
|
|
private void OnClick_btn_questionmark()
|
|
{
|
|
if (Data?.EventMain == null) return;
|
|
UITypes.JigsawPuzzleInfoPanel.SetType(Data.EventMain.InfoPanel);
|
|
_ = UIManager.Instance.ShowUILoad(UITypes.JigsawPuzzleInfoPanel);
|
|
}
|
|
|
|
private async void OnClick_btn_add()
|
|
{
|
|
var Data = JigsawPuzzleManager?.Data;
|
|
if (Data == null || Data.EventMain == null) return;
|
|
|
|
var packData = new EventNeoNormalPackData
|
|
{
|
|
EventId = Data.EventID,
|
|
PackId = Data.EventMain.PackPackID,
|
|
// Panel = Data.EventMain.PackPanel
|
|
};
|
|
await EventNeoNormalPackPanel.TryShowAsync(packData);
|
|
}
|
|
|
|
private bool isPlaying = false;
|
|
private CancellationTokenSource _cancellationTokenSource;
|
|
private Queue<Task<bool>> _tasks=new Queue<Task<bool>>();
|
|
private bool _isAwaiting;
|
|
private void OnClick_btn_spin()
|
|
{
|
|
if (Time.time - lastSpinClickTime < cdTime)
|
|
{
|
|
return;
|
|
}
|
|
lastSpinClickTime = Time.time;
|
|
RunSpineTask();
|
|
}
|
|
private CancellationTokenSource _processTaskCancellationTokenSource=new CancellationTokenSource();
|
|
private async Task ProcessTaskQueue()
|
|
{
|
|
try
|
|
{
|
|
while (_tasks.Count > 0)
|
|
{
|
|
_processTaskCancellationTokenSource.Token.ThrowIfCancellationRequested();
|
|
var task = _tasks.Dequeue();
|
|
_processTaskCancellationTokenSource.Token.ThrowIfCancellationRequested();
|
|
_isAwaiting = true;
|
|
try
|
|
{
|
|
await task;
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
// 单个任务的动画被取消(连点跳过),继续处理队列中的下一个任务
|
|
}
|
|
_processTaskCancellationTokenSource.Token.ThrowIfCancellationRequested();
|
|
}
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
// 整个队列被取消(面板销毁)
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogWarning($"Task queue processing error: {ex}");
|
|
}
|
|
finally
|
|
{
|
|
isPlaying = false;
|
|
_isAwaiting = false;
|
|
_tween?.Kill(true);
|
|
_toggleScrolling = false;
|
|
if (btn_close)
|
|
btn_close.enabled = true;
|
|
}
|
|
}
|
|
|
|
private async void RunSpineTask()
|
|
{
|
|
try
|
|
{
|
|
var itemAmount = JigsawPuzzleManager.GetTokenCount();
|
|
if (itemAmount <= 0)
|
|
{
|
|
OnClick_btn_add();
|
|
isPlaying = false;
|
|
return;
|
|
}
|
|
|
|
if (JigsawPuzzleManager.IsCompleteAllPuzzle())
|
|
return;
|
|
|
|
if (isPlaying)
|
|
{
|
|
_cancellationTokenSource?.Cancel();
|
|
_cancellationTokenSource?.Dispose();
|
|
}
|
|
|
|
isPlaying = true;
|
|
_cancellationTokenSource = new CancellationTokenSource();
|
|
|
|
JigsawPuzzleManager.RemoveToken(1);
|
|
ShowItemCount();
|
|
if (btn_close)
|
|
btn_close.enabled = false;
|
|
|
|
var newTask = PlayEffect(_cancellationTokenSource.Token);
|
|
_tasks.Enqueue(newTask);
|
|
|
|
if (!_isAwaiting)
|
|
{
|
|
await ProcessTaskQueue();
|
|
}
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogWarning(ex);
|
|
}
|
|
}
|
|
|
|
#endregion button click
|
|
|
|
#region OnClick_btn_spin PlayEffect
|
|
|
|
private async Task<bool> PlayEffect(CancellationToken cancellationToken)
|
|
{
|
|
//item
|
|
//Debug.LogError($"PlayEffect");
|
|
var reward = JigsawPuzzleManager.GetRandomReward();
|
|
var random = puzzleRewards.IndexOf(reward);
|
|
if (reward == null)
|
|
{
|
|
Debug.LogError($"PlayEffect reward is null");
|
|
return false;
|
|
}
|
|
|
|
if (reward.ItemDropID > 0)
|
|
{
|
|
TrackingPoint(Data.EventID, reward.Quality, Data.CompletedRoundCount,JsonConvert.SerializeObject(Data.ProgressPairs),false);
|
|
await ItemDropTask(cancellationToken, reward, random);
|
|
return false;
|
|
}
|
|
|
|
//piece
|
|
var isSuccess = Data.TryGetProgressPairsValue(reward.PictureID, out var pieceIndexs);
|
|
|
|
if (!isSuccess)
|
|
{
|
|
Debug.LogError($"{this.GetType().Name} picutreProgressData.Value is null");
|
|
return false;
|
|
}
|
|
//repeat
|
|
bool isShowReward = false;
|
|
int newPieceIndex = -1;
|
|
int pictureId = -1;
|
|
if (pieceIndexs.Count == pieceAmount)
|
|
{
|
|
fx_ui_jigsawpuzzlebuilding_piece_light.SetActive(false);
|
|
Data.PieceRepeatAmount += 1;
|
|
var fakePieceRepeatAmount = Data.PieceRepeatAmount;
|
|
CheckAndChangePiece(reward, out var picutreProgressData);
|
|
if (picutreProgressData.Value != null)
|
|
{
|
|
newPieceIndex = AddPiece(picutreProgressData.Key, picutreProgressData.Value, out isShowReward);
|
|
pictureId = picutreProgressData.Key;
|
|
}
|
|
TrackingPoint(Data.EventID, reward.Quality, Data.CompletedRoundCount,JsonConvert.SerializeObject(Data.ProgressPairs),true);
|
|
|
|
if (newPieceIndex <= -1)
|
|
{
|
|
//Debug.LogError($"PlayEffect newPieceIndex <= -1");
|
|
await slots.SlotsAni(random,cancellationToken);
|
|
await PlayRewardFly(reward, true);
|
|
fx_ui_jigsawpuzzlebuilding_piece_light.SetActive(true);
|
|
ShowRepeatProgress(fakePieceRepeatAmount, true);
|
|
slots.PlayIdleAni();
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
newPieceIndex = AddPiece(reward.PictureID, pieceIndexs, out isShowReward);
|
|
TrackingPoint(Data.EventID, reward.Quality, Data.CompletedRoundCount,JsonConvert.SerializeObject(Data.ProgressPairs),false);
|
|
}
|
|
//Debug.LogError($"PlayEffect newPieceIndex {newPieceIndex}");
|
|
await slots.SlotsAni(random,cancellationToken);
|
|
|
|
if (pictureId <= -1)
|
|
{
|
|
//Debug.LogError($"PlayEffect {reward.PictureID}");
|
|
await Awaiters.NextFrame;
|
|
await PlayRewardFly(reward);
|
|
}
|
|
else
|
|
{
|
|
//Debug.LogError($"PlayEffect reward repeat");
|
|
await PlayRewardFly(reward, true);
|
|
ShowRepeatProgress(2, true);
|
|
reward = puzzleRewards.Find(x => x.PictureID == pictureId);
|
|
await ShowPieceChangePanel(pictureId);
|
|
}
|
|
|
|
await SelectToggleByReward(reward, newPieceIndex);
|
|
slots.PlayIdleAni();
|
|
|
|
if (isShowReward)
|
|
await ShowRewardPanel(reward.PictureID);
|
|
|
|
return false;
|
|
}
|
|
|
|
private async Task ItemDropTask(CancellationToken cancellationToken, EventJigsawPuzzleReward reward, int random)
|
|
{
|
|
//Debug.LogError($"PlayEffect reward.ItemDropID>0 {reward.ItemDropID}");
|
|
ChangeSource changeSource = new ChangeSource(Data.EventID, "Event_Minigame1", "Event_Minigame1_Puzzle", "DirectReward");
|
|
|
|
GContext.Publish(new DeferredRewardStashService.EventStashDrop() { DropId = reward.ItemDropID, Inflate = Data.InflationRate, changeSource = changeSource });
|
|
await slots.SlotsAni(random,cancellationToken);
|
|
slots.PlayIdleAni();
|
|
await PlayRewardFlyItem(reward);
|
|
await Awaiters.Seconds(itemDelayShow);
|
|
service.Flush(RewardType.NormalOne);
|
|
GContext.Publish(new ShowData());
|
|
}
|
|
private int _showPieceChangePanelCount = 0;
|
|
private async Task ShowPieceChangePanel(int pictureId)
|
|
{
|
|
try
|
|
{
|
|
var uiType = new UIType("EventJigsawPuzzlePieceChangePopupPanel").SetName($"EventJigsawPuzzlePieceChangePopupPanel_{_showPieceChangePanelCount}");
|
|
var go = await UIManager.Instance.ShowUILoad(uiType);
|
|
var puzzleRewardConfig = piecePuzzleRewards.Find(x => x.PictureID == pictureId);
|
|
if (puzzleRewardConfig == null)
|
|
return;
|
|
|
|
var toggleIndex =piecePuzzleRewards.FindIndex(r=>r.PictureID==puzzleRewardConfig.PictureID) ;
|
|
RectTransform targetRect = GetToggleRect(puzzleRewardConfig, toggleIndex);
|
|
|
|
// 添加 null 检查
|
|
if (targetRect == null)
|
|
{
|
|
Debug.LogError($"{this.GetType().Name} ShowPieceChangePanel: targetRect is null, cannot show piece change panel");
|
|
return;
|
|
}
|
|
|
|
if(go==null)
|
|
return;
|
|
await go.GetOrAddComponent<JigsawPuzzlePieceChangedPanel>()?.ShowUI(puzzleRewardConfig.Quality, targetRect, toggleIndex, RewardFlyBatchCtrl,_showPieceChangePanelCount++);
|
|
await Awaiters.NextFrame;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogError(e);
|
|
}
|
|
}
|
|
|
|
private void CheckAndChangePiece(EventJigsawPuzzleReward reward,
|
|
out KeyValuePair<int, List<int>> picutreProgressData)
|
|
{
|
|
picutreProgressData = default;
|
|
if (Data.PieceRepeatAmount != Data.SameFragmentExchange)
|
|
return;
|
|
Data.PieceRepeatAmount = 0;
|
|
JigsawPuzzleManager.SaveData();
|
|
var otherPicture = Data.ProgressPairs.Where(x => x.Key != reward.PictureID
|
|
&& x.Value.Count < pieceAmount).ToList();
|
|
if (otherPicture.Count <= 0)
|
|
{
|
|
Debug.LogError($"已收集到全部 reward.PictureID {reward.PictureID} ProgressPairs {JsonConvert.SerializeObject(Data.ProgressPairs)}");
|
|
return;
|
|
}
|
|
picutreProgressData = otherPicture.First();
|
|
}
|
|
|
|
private int AddPiece(int pictureID, List<int> pieceIndexs,
|
|
out bool isShowReward)
|
|
{
|
|
targetIndex = new List<int> { 1, 2, 3, 4, 5, 6 };
|
|
|
|
// 添加数据验证:确保不会添加已存在的碎片
|
|
targetIndex.RemoveAll(x => pieceIndexs.Contains(x));
|
|
|
|
if (targetIndex.Count == 0)
|
|
{
|
|
Debug.LogError($"{this.GetType().Name} All pieces already unlocked for picture {pictureID}");
|
|
isShowReward = false;
|
|
return -1;
|
|
}
|
|
|
|
var randomIndex = UnityEngine.Random.Range(0, targetIndex.Count);
|
|
var newPieceIndex = targetIndex[randomIndex];
|
|
|
|
// 再次检查确保没有重复(防御性编程)
|
|
if (pieceIndexs.Contains(newPieceIndex))
|
|
{
|
|
Debug.LogError($"{this.GetType().Name} Critical: Duplicate piece {newPieceIndex} detected in AddPiece");
|
|
isShowReward = false;
|
|
return -1;
|
|
}
|
|
|
|
pieceIndexs.Add(newPieceIndex);
|
|
JigsawPuzzleManager.SaveData();
|
|
isShowReward = pieceIndexs.Count == pieceAmount;
|
|
CheckAndAddReward(pictureID, isShowReward);
|
|
return newPieceIndex;
|
|
}
|
|
|
|
private void CheckAndAddReward(int pictureID, bool isShowReward)
|
|
{
|
|
if (!isShowReward) return;
|
|
|
|
var pictureConfig = Data.Tables.TbEventJigsawPuzzlePicture.GetOrDefault(pictureID);
|
|
if (pictureConfig != null)
|
|
{
|
|
|
|
ChangeSource changeSource = new ChangeSource(Data.EventID, "Event_Minigame1", "Event_Minigame1_Puzzle", "PictureReward");
|
|
GContext.Publish(new DeferredRewardStashService.EventStashDrop() { DropId = pictureConfig.DropID, Inflate = Data.InflationRate, changeSource = changeSource });
|
|
}
|
|
|
|
if (piecePuzzleRewards.Count != Data.ProgressPairs.Count)
|
|
return;
|
|
|
|
if (piecePuzzleRewards.Count == Data.CompletedPicCount)
|
|
{
|
|
ChangeSource changeSource = new ChangeSource(Data.EventID, "Event_Minigame1", "Event_Minigame1_Puzzle", "GrandReward");
|
|
GContext.Publish(new DeferredRewardStashService.EventStashDrop() { DropId = Data.FinalReward, Inflate = Data.InflationRate, changeSource = changeSource });
|
|
}
|
|
}
|
|
|
|
private async Task ShowRewardPanel(int pictureID)
|
|
{
|
|
var pictureConfig = Data.Tables.TbEventJigsawPuzzlePicture.GetOrDefault(pictureID);
|
|
service.Flush(RewardType.Normal, false);
|
|
UITypes.EventJigsawPuzzleRewardPopupPanel.SetType(Data.EventMain.PictureRewardPanel);
|
|
var go = await UIManager.Instance.ShowUILoad(UITypes.EventJigsawPuzzleRewardPopupPanel);
|
|
if (piecePuzzleRewards.Count == Data.CompletedPicCount)
|
|
go.GetComponent<JigsawPuzzleRewardPanel>().SetData(pictureConfig, async () =>
|
|
{
|
|
await UIManager.Instance.ShowUILoad(UITypes.EventJigsawPuzzleFinalRewardPopupPanel);
|
|
UIManager.Instance.DestroyUI(UITypes.JigsawPuzzlePanel);
|
|
});
|
|
else
|
|
go.GetComponent<JigsawPuzzleRewardPanel>().SetData(pictureConfig);
|
|
}
|
|
|
|
private async Task SelectToggleByReward(EventJigsawPuzzleReward reward, int pieceIndex)
|
|
{
|
|
delayShowPictureID = reward.PictureID;
|
|
var toggleIndex = piecePuzzleRewards.FindIndex(r => r.PictureID == reward.PictureID);
|
|
Data.TryGetProgressPairsValue(reward.PictureID, out var pieces);
|
|
ShowProgressUI(pieces, true);
|
|
await SetSelectedToggle(toggleIndex, isShowProgress: false);
|
|
var cell = enhancedScroller.GetCellViewAtDataIndex(toggleIndex);
|
|
if (cell == null)
|
|
{
|
|
Debug.LogError($"{this.GetType().Name} cell is null toggleIndex {toggleIndex}");
|
|
return;
|
|
}
|
|
var pictureCell = (JigsawPuzzlePictureCell)cell;
|
|
if (pictureCell == null)
|
|
{
|
|
Debug.LogError($"{this.GetType().Name} pictureCell is null");
|
|
return;
|
|
}
|
|
|
|
await pictureCell.PlayFly(pieceIndex, pieces.Count == pieceAmount,
|
|
() =>
|
|
{
|
|
Data.TryGetProgressPairsValue(reward.PictureID, out var pieces);
|
|
var isDone = pieces.Count == pieceAmount;
|
|
ShowTabDone(toggleIndex, isDone);
|
|
ShowRewardState(isDone);
|
|
ShowProgressUI(pieces, true, true);
|
|
fx_ui_jigsawpuzzlebuilding_reward_light.SetActive(isDone);
|
|
_toggleScrolling = false;
|
|
});
|
|
|
|
delayShowPictureID = 0;
|
|
}
|
|
|
|
private async Task PlayRewardFly(EventJigsawPuzzleReward reward,bool isRepeat = false)
|
|
{
|
|
//Debug.LogError($"PlayRewardFly flyStartPoint {flyStartPoint==null}");
|
|
if (reward == null)
|
|
{
|
|
Debug.LogError($"{this.GetType().Name} reward is null ");
|
|
return;
|
|
}
|
|
var pictureName = $"{JigsawPuzzleManager.PriceImgName}{reward.Quality}";
|
|
RectTransform targetRect;
|
|
|
|
var toggleIndex = piecePuzzleRewards.FindIndex(r=>r.PictureID==reward.PictureID);
|
|
if (isRepeat)
|
|
targetRect = p_repeat_icon_ticket.rectTransform;
|
|
else
|
|
targetRect = GetToggleRect(reward, toggleIndex);
|
|
|
|
// 添加 null 检查
|
|
if (targetRect == null)
|
|
{
|
|
Debug.LogError($"{this.GetType().Name} PlayRewardFly: targetRect is null, aborting reward fly animation");
|
|
return;
|
|
}
|
|
|
|
if (RewardFlyBatchCtrl.animationParamList is not { Length: > 0 })
|
|
{
|
|
Debug.LogError($"{this.GetType().Name} RewardFlyBatchCtrl.animationParamList is null");
|
|
return;
|
|
}
|
|
var aniParam = this.RewardFlyBatchCtrl.animationParamList[0];
|
|
if (aniParam.FxSingle == null || aniParam.FxSingle.Length < 2)
|
|
{
|
|
Debug.LogError($"{this.GetType().Name} RewardFlyBatchCtrl.animationParamList aniParam.FxSingle is null");
|
|
return;
|
|
}
|
|
aniParam.FxSingle[1] = $"{flyTrailName}{toggleIndex + 1}";
|
|
|
|
//Debug.LogError($"playRewardFly targetRect {targetRect==null}");
|
|
var request = new BatchedRewardFlyRequest
|
|
{
|
|
Icon = new BatchedRewardFlyRequestIcon
|
|
{
|
|
iconName = pictureName
|
|
},
|
|
StartPoint = new BatchedRewardFlyStartPoint(slots.GetDefaultCellView_Piece()),
|
|
EndPoint = new BatchedRewardFlyEndPoint(targetRect.position, rewardFlyTargetIconSize, UIManager.Instance.transform.lossyScale.x),
|
|
IsDestinationRewardStash = true,
|
|
AnimationParamIndex = 0,
|
|
};
|
|
await this.RewardFlyBatchCtrl.OnRewardFlyRequestAsync(request);
|
|
await Awaiters.Seconds(rewardFlyEndAwaitTime);
|
|
}
|
|
private async Task PlayRewardFlyItem(EventJigsawPuzzleReward reward)
|
|
{
|
|
if (reward == null)
|
|
{
|
|
Debug.LogError($"{this.GetType().Name} reward is null ");
|
|
return;
|
|
}
|
|
if (RewardFlyBatchCtrl.animationParamList is not { Length: > 0 })
|
|
{
|
|
Debug.LogError($"{this.GetType().Name} RewardFlyBatchCtrl.animationParamList is null");
|
|
return;
|
|
}
|
|
var aniParam = this.RewardFlyBatchCtrl.animationParamList[0];
|
|
if (aniParam.FxSingle == null || aniParam.FxSingle.Length < 2)
|
|
{
|
|
Debug.LogError($"{this.GetType().Name} RewardFlyBatchCtrl.animationParamList aniParam.FxSingle is null");
|
|
return;
|
|
}
|
|
var rewardDatas = GContext.container.Resolve<PlayerItemData>()
|
|
.GetItemDataByDropIdLureInflation(Data.InflationRate, reward.ItemDropID);
|
|
var item=Data.Tables.TbItem.GetOrDefault(rewardDatas.FirstOrDefault()?.id??0);
|
|
var request = new BatchedRewardFlyRequest
|
|
{
|
|
Icon = new BatchedRewardFlyRequestIcon
|
|
{
|
|
itemID= rewardDatas.FirstOrDefault()?.id??0
|
|
},
|
|
StartPoint = new BatchedRewardFlyStartPoint(slots.GetDefaultCellView_Item()),
|
|
EndPoint = new BatchedRewardFlyEndPoint(slots.GetTargetPosition(item), rewardFlyTargetIconSize, UIManager.Instance.transform.lossyScale.x),
|
|
IsDestinationRewardStash = true,
|
|
AnimationParamIndex = 0,
|
|
};
|
|
await this.RewardFlyBatchCtrl.OnRewardFlyRequestAsync(request);
|
|
await Awaiters.Seconds(rewardFlyEndAwaitTime);
|
|
}
|
|
private RectTransform GetToggleRect(EventJigsawPuzzleReward reward, int toggleIndex)
|
|
{
|
|
// 检查数组是否为null
|
|
if (toggles == null)
|
|
{
|
|
Debug.LogError($"{this.GetType().Name} toggles array is null");
|
|
return null;
|
|
}
|
|
|
|
// 修复: 使用 >= 而不是 >,防止数组越界
|
|
if (toggleIndex < 0 || toggleIndex >= toggles.Length)
|
|
{
|
|
Debug.LogError($"{this.GetType().Name} toggle error {toggleIndex}, array length: {toggles.Length}");
|
|
return null;
|
|
}
|
|
|
|
// 检查 toggle 是否为 null
|
|
if (toggles[toggleIndex] == null)
|
|
{
|
|
Debug.LogError($"{this.GetType().Name} toggle at index {toggleIndex} is null");
|
|
return null;
|
|
}
|
|
|
|
// 检查 transform 是否有子对象
|
|
var toggleTransform = toggles[toggleIndex].transform;
|
|
if (toggleTransform == null || toggleTransform.childCount == 0)
|
|
{
|
|
Debug.LogError($"{this.GetType().Name} toggle at index {toggleIndex} has no children");
|
|
return null;
|
|
}
|
|
|
|
var targetRect = toggleTransform.GetChild(0).GetComponent<RectTransform>();
|
|
if (targetRect == null)
|
|
{
|
|
Debug.LogError($"{this.GetType().Name} targetRect is null at toggle index {toggleIndex}");
|
|
return null;
|
|
}
|
|
|
|
return targetRect;
|
|
}
|
|
|
|
public void TrackingPoint(int event_id, int result_quality,int round,string picture_complete_list,bool is_repeat)
|
|
{
|
|
#if AGG
|
|
using (var e = GEvent.GameEvent("event_jigsawpuzzle"))
|
|
{
|
|
e.AddContent(nameof(event_id), event_id)
|
|
.AddContent(nameof(result_quality), result_quality)
|
|
.AddContent(nameof(round), round)
|
|
.AddContent(nameof(picture_complete_list), picture_complete_list)
|
|
.AddContent(nameof(is_repeat), is_repeat);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#endregion OnClick_btn_spin PlayEffect
|
|
|
|
#region enhanced scroller
|
|
|
|
public int GetNumberOfCells(EnhancedScroller scroller)
|
|
{
|
|
return piecePuzzleRewards.Count;
|
|
}
|
|
|
|
public float GetCellViewSize(EnhancedScroller scroller, int DataIndex)
|
|
{
|
|
return cellViewSize;
|
|
}
|
|
|
|
public EnhancedScrollerCellView GetCellView(EnhancedScroller scroller, int DataIndex, int cellIndex)
|
|
{
|
|
var cell = scroller.GetCellView(cellView);
|
|
if (cell is not JigsawPuzzlePictureCell puzzleCell)
|
|
return null;
|
|
cell.gameObject.SetActive(true);
|
|
|
|
var pictureID = piecePuzzleRewards[DataIndex].PictureID;
|
|
Data.TryGetProgressPairsValue(pictureID, out var pieceIndexs);
|
|
var pictureName = Data.Tables.TbEventJigsawPuzzlePicture.GetOrDefault(pictureID).PictureName;
|
|
puzzleCell.SetData(pieceIndexs, pictureName, delayShowPictureID == pictureID);
|
|
puzzleCell.RefreshCellView();
|
|
return cell;
|
|
}
|
|
|
|
#endregion enhanced scroller
|
|
|
|
//#if UNITY_EDITOR
|
|
// // 在Inspector中显示调试信息
|
|
// void OnGUI()
|
|
// {
|
|
// if (!Application.isPlaying) return;
|
|
// GUILayout.BeginArea(new Rect(50, 200, 200, 200));
|
|
// GUILayout.Space(10);
|
|
// if (GUILayout.Button("清除记录"))
|
|
// {
|
|
// JigsawPuzzleManager.Data.RefreshCache();
|
|
// JigsawPuzzleManager.Data.FixedInitList?.Clear();
|
|
// JigsawPuzzleManager.SaveData();
|
|
// ShowUI();
|
|
// }
|
|
// GUILayout.EndArea();
|
|
// GUILayout.BeginArea(new Rect(250, 400, 400, 400));
|
|
// GUILayout.Space(10);
|
|
// if (GUILayout.Button("PlayPieceFly"))
|
|
// {
|
|
// var cell = enhancedScroller.GetCellViewAtDataIndex(0);
|
|
// var pictureCell = cell as JigsawPuzzlePictureCell;
|
|
// _ = pictureCell.PlayFly(1, false);
|
|
// }
|
|
|
|
// GUILayout.EndArea();
|
|
// }
|
|
//#endif
|
|
}
|
|
} |