Files
5.24BuYu/Assets/MyGame/aa/UI/Home/Sub/Activity/ChouJiangToggle.cs
T
2026-09-23 19:33:45 +08:00

296 lines
11 KiB
C#
Raw 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 Assets.MyGame.Scripts.Net.API.GameConfig_SubClass;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 抽奖 toggle 层(第二层):挂在总面板里的三个 toggle 面板(PT炮台 / CB翅膀 / ZS战神)其中一个上,
/// poolType 决定本面板显示哪组奖池;面板里有一堆 item(ChouJiangItem,第三层),
/// 分为三种 item 类型也就是 A/B/C 三个列表:
/// items_A(A级·大奖)/ items_B(B级·中奖)/ items_C(C级·小奖)
/// Init 时筛出本组奖池、同等级合并,每个奖品按 prizeId 后两位(itemId)找到自己的格子。
/// </summary>
public class ChouJiangToggle : MonoBehaviour
{
[Tooltip("本面板的奖池分组:1=炮台,2=战神,3=翅膀(总面板按 toggles 列表顺序自动赋值,一般不用手设)")]
public LotteryPoolType poolType = LotteryPoolType.炮台;
[Tooltip("A级(大奖)item,按展示顺序拖")]
public List<ChouJiangItem> items_A;
[Tooltip("B级(中奖)item,按展示顺序拖")]
public List<ChouJiangItem> items_B;
[Tooltip("C级(小奖)item,按展示顺序拖")]
public List<ChouJiangItem> items_C;
[Header("抽奖动效参数")]
[Tooltip("跑马灯圈数:跳完这些圈再停到目标格")]
public int effectLoops = 2;
[Tooltip("单步最快间隔(秒)")]
public float effectStepFast = 0.07f;
[Tooltip("单步最慢间隔(秒):开头/结尾慢,中间快")]
public float effectStepSlow = 0.30f;
private Coroutine effectCo; // 跑马灯协程
/// <summary>
/// 初始化:筛出本组(poolType)的全部奖池,同等级合并后,
/// A/B/C 级的奖品各自按 prizeId 后两位(itemId)找到自己的格子。
/// </summary>
public void Init(LotteryActivityConfig config)
{
// 先清空所有 item 的旧数据
ClearItems(items_A);
ClearItems(items_B);
ClearItems(items_C);
if (config == null || config.pools == null || config.pools.Count == 0)
{
Debug.LogWarning($"[ChouJiangToggle] 抽奖配置为空(poolType={poolType})");
return;
}
// 同组同等级可能配多条奖池,先按等级合并再分发
Dictionary<string, List<LotteryPrize>> prizesByLevel = new Dictionary<string, List<LotteryPrize>>();
foreach (LotteryPoolLevel pool in config.pools)
{
if (pool == null || pool.poolType != (int)poolType) continue;
string level = pool.level != null ? pool.level.ToUpperInvariant() : null;
if (level != "A" && level != "B" && level != "C")
{
Debug.LogWarning($"[ChouJiangToggle] 未知奖池等级 {pool.level}(只支持 A/B/C)");
continue;
}
List<LotteryPrize> list;
if (!prizesByLevel.TryGetValue(level, out list))
{
list = new List<LotteryPrize>();
prizesByLevel[level] = list;
}
if (pool.prizeList != null) list.AddRange(pool.prizeList);
}
// 兜底:没手填 itemId 的列表按拖入顺序自动编 1..n(手填过的列表不受影响)
AutoNumberItems(items_A);
AutoNumberItems(items_B);
AutoNumberItems(items_C);
FillLevel("A", items_A, prizesByLevel);
FillLevel("B", items_B, prizesByLevel);
FillLevel("C", items_C, prizesByLevel);
}
/// <summary>
/// 兜底编号:整列表都没手填 itemId(全为 0)时按列表拖入顺序自动编 1..n;
/// 只要有一个格子手填过编号,该列表就完全按手填的来。
/// </summary>
private static void AutoNumberItems(List<ChouJiangItem> items)
{
if (items == null || items.Count == 0) return;
bool anySet = false;
for (int i = 0; i < items.Count; i++)
{
if (items[i] != null && items[i].itemId != 0) { anySet = true; break; }
}
if (anySet) return;
for (int i = 0; i < items.Count; i++)
{
if (items[i] != null) items[i].itemId = i + 1;
}
Debug.Log($"[ChouJiangToggle] 列表未手填 itemId,按拖入顺序自动编号 1..{items.Count}");
}
/// <summary>按已抽奖项列表置灰本面板所有 item(奖励不重复)</summary>
public void RefreshDrawnState(List<string> drawnPrizeIds)
{
ApplyDrawn(items_A, drawnPrizeIds);
ApplyDrawn(items_B, drawnPrizeIds);
ApplyDrawn(items_C, drawnPrizeIds);
}
/// <summary>每个奖品按 prizeId 后两位找到自己的 item(itemId 对号入座)</summary>
private void FillLevel(string level, List<ChouJiangItem> items, Dictionary<string, List<LotteryPrize>> prizesByLevel)
{
if (items == null || items.Count == 0) return;
List<LotteryPrize> prizes;
prizesByLevel.TryGetValue(level, out prizes);
if (prizes == null || prizes.Count == 0)
{
Debug.LogWarning($"[ChouJiangToggle] {poolType} 组没有 {level} 级奖池");
return;
}
for (int i = 0; i < prizes.Count; i++)
{
int no = ParseItemId(prizes[i].prizeId);
ChouJiangItem item = FindItemById(items, no);
if (item == null)
{
Debug.LogWarning($"[ChouJiangToggle] {poolType} 组 {level} 级奖品 {prizes[i].prizeId}(编号 {no})找不到对应 itemId 的 item,跳过");
continue;
}
item.Init(prizes[i]);
}
}
/// <summary>取 prizeId 后两位作为位置编号(如 588203 → 3);解析失败返回 -1</summary>
private static int ParseItemId(string prizeId)
{
if (string.IsNullOrEmpty(prizeId) || prizeId.Length < 2) return -1;
int no;
if (!int.TryParse(prizeId.Substring(prizeId.Length - 2), out no)) return -1;
return no;
}
/// <summary>按位置编号找 item;找不到返回 null</summary>
private static ChouJiangItem FindItemById(List<ChouJiangItem> items, int itemId)
{
if (itemId < 0 || items == null) return null;
for (int i = 0; i < items.Count; i++)
{
if (items[i] != null && items[i].itemId == itemId) return items[i];
}
return null;
}
private void ApplyDrawn(List<ChouJiangItem> items, List<string> drawnPrizeIds)
{
if (items == null) return;
for (int i = 0; i < items.Count; i++)
{
if (items[i] == null || items[i].prize == null) continue;
string prizeId = items[i].prize.prizeId;
items[i].RefreshDrawnState(!string.IsNullOrEmpty(prizeId)
&& drawnPrizeIds != null && drawnPrizeIds.Contains(prizeId));
}
}
// ============================================================
#region 抽奖跑马灯动效
// ============================================================
/// <summary>
/// 播放抽奖动效:跑马灯在格子间 加速→匀速→减速 跳动,最后停在 prizeId 对应的格子上。
/// 方块是各格子自己的 prefab_DrawEffect(ChouJiangItem 上拖),经过/停留时点亮。
/// 完成后回调 onComplete;没有任何格子配方块/找不到目标格时立即回调。
/// </summary>
public void PlayDrawEffect(string prizeId, Action onComplete)
{
// 停掉上一次
if (effectCo != null) { StopCoroutine(effectCo); effectCo = null; }
List<ChouJiangItem> path = BuildEffectPath();
ChouJiangItem target = FindItemByPrizeId(prizeId);
bool anyEffect = false;
for (int i = 0; i < path.Count; i++)
{
if (path[i].prefab_DrawEffect != null) { anyEffect = true; break; }
}
if (!anyEffect || path.Count == 0 || target == null || !path.Contains(target))
{
onComplete?.Invoke();
return;
}
effectCo = StartCoroutine(IEPlayEffect(path, target, onComplete));
}
/// <summary>动效路径:A → B → C 三个列表的格子,各自按 itemId 升序(只含有奖品且未抽中的格子——已经抽到的不再跳)</summary>
private List<ChouJiangItem> BuildEffectPath()
{
List<ChouJiangItem> path = new List<ChouJiangItem>();
AppendPath(items_A, path);
AppendPath(items_B, path);
AppendPath(items_C, path);
return path;
}
private static void AppendPath(List<ChouJiangItem> items, List<ChouJiangItem> path)
{
if (items == null) return;
List<ChouJiangItem> sorted = new List<ChouJiangItem>();
for (int i = 0; i < items.Count; i++)
{
ChouJiangItem it = items[i];
if (it != null && it.prize != null && !it.IsDrawn) sorted.Add(it); // 已抽中(置灰)的跳过
}
sorted.Sort((a, b) => a.itemId.CompareTo(b.itemId));
path.AddRange(sorted);
}
/// <summary>按 prizeId 找本面板的 item</summary>
private ChouJiangItem FindItemByPrizeId(string prizeId)
{
if (string.IsNullOrEmpty(prizeId)) return null;
ChouJiangItem r = FindInList(items_A, prizeId);
if (r == null) r = FindInList(items_B, prizeId);
if (r == null) r = FindInList(items_C, prizeId);
return r;
}
private static ChouJiangItem FindInList(List<ChouJiangItem> items, string prizeId)
{
if (items == null) return null;
for (int i = 0; i < items.Count; i++)
{
if (items[i] != null && items[i].prize != null && items[i].prize.prizeId == prizeId)
return items[i];
}
return null;
}
private IEnumerator IEPlayEffect(List<ChouJiangItem> path, ChouJiangItem target, Action onComplete)
{
int count = path.Count;
int targetIdx = path.IndexOf(target);
int cur = 0;
// 全部先熄灭(含上一次中奖格上亮着的方块),点亮起点
for (int i = 0; i < count; i++) path[i].ShowEffect(false);
path[0].ShowEffect(true);
// 总步数 = 圈数 × 格子数 + 到目标格的距离,保证最后一步正好落在目标格
int totalSteps = effectLoops * count + (targetIdx - cur + count) % count;
for (int step = 0; step < totalSteps; step++)
{
float p = (float)step / totalSteps;
yield return new WaitForSeconds(GetStepDuration(p));
path[cur].ShowEffect(false); // 熄灭当前格
cur = (cur + 1) % count;
path[cur].ShowEffect(true); // 点亮下一格
}
// 停在目标格上停留一下再回调(中奖格的方块保持亮着)
yield return new WaitForSeconds(0.6f);
onComplete?.Invoke();
}
/// <summary>单步时长:前 25% 加速 → 中间匀速 → 后 30% 减速(先快后慢的抽奖手感)</summary>
private float GetStepDuration(float p)
{
if (p < 0.25f) return Mathf.Lerp(effectStepSlow, effectStepFast, p / 0.25f);
if (p < 0.70f) return effectStepFast;
return Mathf.Lerp(effectStepFast, effectStepSlow, (p - 0.70f) / 0.30f);
}
#endregion
private void ClearItems(List<ChouJiangItem> items)
{
if (items == null) return;
for (int i = 0; i < items.Count; i++)
{
if (items[i] != null) items[i].Init(null);
}
}
}