using Assets.MyGame.Scripts.Net.API.GameConfig_SubClass; using Assets.MyGame.Scripts.Net.APIResponses.Enum; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; /// /// 单独锁定面板(SuoDing.prefab)。 /// ⭐ 本阶段只做界面: /// - 单列表不分类,只显示 黄金鱼/特殊鱼/Boss鱼/特殊Boss(不含普通鱼) /// - 每个 item 带 Toggle(勾选=作为锁定目标) /// - 两层排序:①鱼类型 特殊Boss > Boss鱼 > 特殊鱼 > 黄金鱼;②同类型内按最大爆奖倍数(与 item 显示同源)从大到小 /// - 全选/取消全选 两个按钮 /// - GetSelectedFishNumbers() 暴露选中列表,后续锁定功能从这里对接 /// public class SuoDingPanel : SubUIBase { [Header("Item 模板(TuJianItem+Toggle,可挂在模板自身或子节点;模板未激活也行,克隆后会自动激活)")] public GameObject prefab_Item; [Header("列表父节点(不分类,所有条目都放这里)")] public Transform parent_List; [Header("按钮")] public Button btn_SelectAll; // 全选 public Button btn_DeselectAll; // 取消全选 [Header("滚动视图")] public ScrollRect scrollRect; /// fishNumber → 条目(TuJianItem 显示 + Toggle 勾选) private readonly Dictionary entries = new Dictionary(); /// 当前勾选的鱼编号集合(Toggle 的 onValueChanged 实时维护,锁定系统 O(1) 查询) private readonly HashSet selectedSet = new HashSet(); private class Entry { public string fishNumber; public FishTypeEnum fishType; public TuJianItem item; public Toggle toggle; } public override void Awake() { base.Awake(); if (btn_SelectAll != null) btn_SelectAll.onClick.AddListener(SelectAll); if (btn_DeselectAll != null) btn_DeselectAll.onClick.AddListener(DeselectAll); BuildList(); // ⭐ 默认全部不选:不勾选 = 不限制(正常锁定),玩家想锁哪条就勾哪条 DeselectAll(); } protected override void OnShow(object args = null) { base.OnShow(args); // 打开时滚动到最上方 // ⭐ 只重置纵向(verticalNormalizedPosition): // normalizedPosition 会同时写 x 轴(=0 横向滚到最左),ScrollRect 会直接改写 // content(列表父对象)的 anchoredPosition.x,导致父对象 X 从 0 被移走 if (scrollRect != null) scrollRect.verticalNormalizedPosition = 1f; } protected override void OnHide() { base.OnHide(); // ⭐ 关闭面板 = 应用勾选结果:通知主面板打开锁定并按勾选白名单自动锁定 // (parentUIBase 就是挂着本面板的主面板,各游戏主面板都继承 GameMainPanel_FishBase) if (uIManager != null && uIManager.parentUIBase is GameMainPanel_FishBase main) main.ApplySuoDingSelection(); } // ==================== 列表构建 ==================== private void BuildList() { if (prefab_Item == null || parent_List == null) return; // ① 收集本房间可出鱼编号(鱼阵条目解析出阵内实际鱼),去重 var fishNumbers = new HashSet(); var spawnList = CreateFishManager.Instance != null ? CreateFishManager.Instance.spawnList : null; if (spawnList != null) { foreach (var spawn in spawnList) { if (spawn.type == 2) { // 鱼阵条目:fishNumber 存的是鱼阵编号,解析出阵内实际生成的鱼 if (CreateFishManager.Instance.FishArrayDatas.TryGetValue(spawn.fishNumber, out var yz)) TryAddFishNumber(fishNumbers, yz.fishNumber); } else { TryAddFishNumber(fishNumbers, spawn.fishNumber); } } } // ② 过滤:只保留 黄金鱼/特殊鱼/Boss鱼/特殊Boss(不显示普通鱼) // 同时带上"最大爆奖倍数"(与 item 上显示的数字同源,第二层排序用) var filtered = new List<(string fishNumber, FishTypeEnum fishType, double multiple)>(); foreach (var fn in fishNumbers) { if (!GameManager.Instance.dic_fishTypeConfigs.TryGetValue(fn, out var cfg)) continue; var t = (FishTypeEnum)cfg.fishType; if (t == FishTypeEnum.普通鱼) continue; filtered.Add((fn, t, GetShowMultiple(cfg.fishMultipleDesc, cfg.fishScore))); } // ③ 两层排序(与 XJMH 锁定面板同口径): // 第一层:鱼类型 特殊Boss > Boss鱼 > 特殊鱼 > 黄金鱼 // 第二层:同类型内按"最大爆奖倍数"从大到小;倍数相同再按鱼号 filtered.Sort((a, b) => { int pa = GetTypeSortOrder(a.fishType); int pb = GetTypeSortOrder(b.fishType); if (pa != pb) return pa.CompareTo(pb); int sc = b.multiple.CompareTo(a.multiple); // ⭐ 从大到小 if (sc != 0) return sc; return CompareFishNumber(a.fishNumber, b.fishNumber); }); // ④ 实例化条目 foreach (var (fishNumber, fishType, _) in filtered) CreateItem(fishNumber, fishType); } /// /// 取 item 上显示给玩家的"最大爆奖倍数"(排序用,保证顺序和 item 上的数字一致): /// 优先解析服务端文案 fishMultipleDesc(如"最高500倍"、"1.5万倍"), /// 解析不到数字时回退用 fishScore。 /// ⭐ public static:XJMH 锁定面板(PW_SetLock)也用同一份口径排序。 /// public static double GetShowMultiple(string fishMultipleDesc, long fishScore) { double v = ParseNumber(fishMultipleDesc); return v > 0 ? v : fishScore; } /// 从文案里解析第一个数字(支持小数与"万/亿"单位;解析不到返回 0) private static double ParseNumber(string s) { if (string.IsNullOrEmpty(s)) return 0; int i = 0; while (i < s.Length && (s[i] < '0' || s[i] > '9')) i++; // 跳过前缀(如"最高") if (i >= s.Length) return 0; int start = i; bool dot = false; while (i < s.Length && ((s[i] >= '0' && s[i] <= '9') || (s[i] == '.' && !dot))) { if (s[i] == '.') dot = true; i++; } double v; if (!double.TryParse(s.Substring(start, i - start), System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out v)) return 0; if (i < s.Length) { if (s[i] == '万') v *= 10000; else if (s[i] == '亿') v *= 100000000; } return v; } private void TryAddFishNumber(HashSet set, string fishNumber) { if (!string.IsNullOrEmpty(fishNumber)) set.Add(fishNumber); } /// 显示排序:特殊Boss(0) > Boss鱼(1) > 特殊鱼(2) > 黄金鱼(3),与自动锁定优先级一致 private static int GetTypeSortOrder(FishTypeEnum t) { switch (t) { case FishTypeEnum.特殊Boss: return 0; case FishTypeEnum.Boss鱼: return 1; case FishTypeEnum.特殊鱼: return 2; case FishTypeEnum.黄金鱼: return 3; default: return int.MaxValue; } } private static int CompareFishNumber(string a, string b) { if (int.TryParse(a, out var na) && int.TryParse(b, out var nb)) return na.CompareTo(nb); return string.CompareOrdinal(a, b); } private void CreateItem(string fishNumber, FishTypeEnum fishType) { // 模板是面板下的未激活场景对象,克隆后要手动激活 var go = Instantiate(prefab_Item, parent_List); go.SetActive(true); go.name = $"SuoDingItem_{fishNumber}"; // TuJianItem / Toggle 可挂在模板自身或子节点(两种摆法都兼容) var item = go.GetComponentInChildren(true); var toggle = go.GetComponentInChildren(true); if (item == null) return; // 黄金鱼显示固定倍数,特殊鱼/Boss/特殊Boss显示"最高X倍"(与图鉴一致) item.InItItem(new RoomFishDetailConfig { fishNumber = fishNumber }, fishType == FishTypeEnum.黄金鱼); if (toggle != null) { // ⭐ 多选:清掉可能误挂的排他 ToggleGroup(注册进组后全选会被组吞成单选) if (toggle.group != null) toggle.group = null; // ⭐ 勾选状态实时同步到 selectedSet(全选/取消全选/手动点击都会触发 onValueChanged) toggle.onValueChanged.AddListener(on => { if (on) selectedSet.Add(fishNumber); else selectedSet.Remove(fishNumber); }); if (toggle.isOn) selectedSet.Add(fishNumber); } entries[fishNumber] = new Entry { fishNumber = fishNumber, fishType = fishType, item = item, toggle = toggle }; } // ==================== 全选/取消全选 ==================== public void SelectAll() { foreach (var e in entries.Values) if (e.toggle != null) e.toggle.isOn = true; } public void DeselectAll() { foreach (var e in entries.Values) if (e.toggle != null) e.toggle.isOn = false; } // ==================== 选中列表(锁定对接入口) ==================== /// 是否至少勾选了一条(白名单是否生效)。锁定系统每帧查询,用 O(1) 属性 public bool HasSelection => selectedSet.Count > 0; /// 当前勾选的鱼编号列表(锁定系统从这里读取) public List GetSelectedFishNumbers() { return new List(selectedSet); } /// 某条鱼是否被勾选(未创建条目视为未选中)。O(1),锁定系统逐鱼查询用 public bool IsFishSelected(string fishNumber) { return !string.IsNullOrEmpty(fishNumber) && selectedSet.Contains(fishNumber); } }