Files
ft/Client/Assets/Scripts/UI/JigsawPuzzle/JigsawPuzzlePanel.cs
2026-06-29 21:18:33 +08:00

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
}
}