using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.UI; using Bansonic; #if UNITY_EDITOR using UnityEditor; #endif public class idolEquipments : MonoBehaviour { private enum EquipFilterMode { All, HasSpecialSkill, HasIllusionSkill, HasAnySkill, HasNoSkill, HasDualSkill, Type0, Type1, Type2, Type3, Type4, Type5, Type6, Type7 } private enum EquipOrderMode { AcquireAsc, AcquireDesc, QualityAsc, QualityDesc } [Header("objects")] public GameObject eqpmtItemPrefab; public Transform eqpmtToPut; [Header("INFO")] public Text this_eqpmtName; public Text this_eqpmtType; public Text this_eqpmtLevel; public Text this_eqpmtID; [Header("waitingList")] public int i_batch = 16; public Transform eqpmtBagParent; [Header("filter")] public Toggle onlyCanEquip; public Dropdown orderFilter; public Dropdown typeFilter; [Header("emptyText")] public Text emptyText; public Scrollbar eqpmtBagScrollbar; [Header("source paths")] public string runtimeEquipmentFolder = "so/uEquip"; public string editorEquipmentFolder = "Assets/Resources/so/uEquip"; private readonly List spawnedBagItems = new List(); private Coroutine rebuildRoutine; private equipmentSO equippedEquipment; private GameObject equippedItemInstance; private AllyHero_SO currentHero; private float pendingScrollbarValue = 1f; private Coroutine restoreScrollbarRoutine; private bool suppressDropdownCallbacks; private static readonly (EquipFilterMode mode, string label)[] FilterOptions = { (EquipFilterMode.All, "全部"), (EquipFilterMode.HasSpecialSkill, "含特效技能"), (EquipFilterMode.HasIllusionSkill, "含巡演技能"), (EquipFilterMode.HasAnySkill, "含技能"), (EquipFilterMode.HasNoSkill, "不含技能"), (EquipFilterMode.HasDualSkill, "含双技能"), (EquipFilterMode.Type0, equipmentSO.EquipmentSkillType.题海战术.ToString()), (EquipFilterMode.Type1, equipmentSO.EquipmentSkillType.诛锄异己.ToString()), (EquipFilterMode.Type2, equipmentSO.EquipmentSkillType.逸乐共谋.ToString()), (EquipFilterMode.Type3, equipmentSO.EquipmentSkillType.完美至上.ToString()), (EquipFilterMode.Type4, equipmentSO.EquipmentSkillType.涵养心境.ToString()), (EquipFilterMode.Type5, equipmentSO.EquipmentSkillType.众志成城.ToString()), (EquipFilterMode.Type6, equipmentSO.EquipmentSkillType.过激行为.ToString()), (EquipFilterMode.Type7, equipmentSO.EquipmentSkillType.二次形变.ToString()) }; private static readonly (EquipOrderMode mode, string label)[] OrderOptions = { (EquipOrderMode.AcquireAsc, "获取时间升序"), (EquipOrderMode.AcquireDesc, "获取时间降序"), (EquipOrderMode.QualityAsc, "品质升序"), (EquipOrderMode.QualityDesc, "品质降序") }; private void Start() { InitializeDropdowns(); InitializeOnlyCanEquipToggle(); BindControls(); RestoreEquippedFromHero(); RebuildBag(); RefreshEquippedSlot(); } private void OnEnable() { InitializeDropdowns(); InitializeOnlyCanEquipToggle(); BindControls(); RestoreEquippedFromHero(); RebuildBag(); RefreshEquippedSlot(); } private void OnDisable() { UnbindControls(); if (rebuildRoutine != null) { StopCoroutine(rebuildRoutine); rebuildRoutine = null; } if (restoreScrollbarRoutine != null) { StopCoroutine(restoreScrollbarRoutine); restoreScrollbarRoutine = null; } } private void BindControls() { if (onlyCanEquip != null) { onlyCanEquip.onValueChanged.RemoveListener(HandleFilterChanged); onlyCanEquip.onValueChanged.AddListener(HandleFilterChanged); } if (orderFilter != null) { orderFilter.onValueChanged.RemoveListener(HandleOrderChanged); orderFilter.onValueChanged.AddListener(HandleOrderChanged); } if (typeFilter != null) { typeFilter.onValueChanged.RemoveListener(HandleTypeChanged); typeFilter.onValueChanged.AddListener(HandleTypeChanged); } } private void UnbindControls() { if (onlyCanEquip != null) { onlyCanEquip.onValueChanged.RemoveListener(HandleFilterChanged); } if (orderFilter != null) { orderFilter.onValueChanged.RemoveListener(HandleOrderChanged); } if (typeFilter != null) { typeFilter.onValueChanged.RemoveListener(HandleTypeChanged); } } private void HandleFilterChanged(bool _) { if (!suppressDropdownCallbacks) { RebuildBag(); } } private void HandleOrderChanged(int _) { if (!suppressDropdownCallbacks) { RebuildBag(); } } private void HandleTypeChanged(int _) { if (!suppressDropdownCallbacks) { RebuildBag(); } } private void InitializeDropdowns() { InitializeTypeFilterDropdown(); InitializeOrderDropdown(); } private void InitializeTypeFilterDropdown() { if (typeFilter == null) { return; } suppressDropdownCallbacks = true; typeFilter.onValueChanged.RemoveListener(HandleTypeChanged); typeFilter.ClearOptions(); typeFilter.AddOptions(FilterOptions.Select(option => new Dropdown.OptionData(option.label)).ToList()); typeFilter.value = 0; typeFilter.RefreshShownValue(); typeFilter.onValueChanged.AddListener(HandleTypeChanged); suppressDropdownCallbacks = false; } private void InitializeOrderDropdown() { if (orderFilter == null) { return; } suppressDropdownCallbacks = true; orderFilter.onValueChanged.RemoveListener(HandleOrderChanged); orderFilter.ClearOptions(); orderFilter.AddOptions(OrderOptions.Select(option => new Dropdown.OptionData(option.label)).ToList()); orderFilter.value = 3; orderFilter.RefreshShownValue(); orderFilter.onValueChanged.AddListener(HandleOrderChanged); suppressDropdownCallbacks = false; } private void InitializeOnlyCanEquipToggle() { if (onlyCanEquip == null) { return; } onlyCanEquip.SetIsOnWithoutNotify(true); } public void RebuildBag() { CacheScrollbarValue(); if (rebuildRoutine != null) { StopCoroutine(rebuildRoutine); rebuildRoutine = null; } ClearBagItems(); if (eqpmtItemPrefab == null || eqpmtBagParent == null) { RefreshEmptyState(0); return; } List equipments = LoadEquipments() .Where(e => e != null) .Where(e => !equipSmelt.IsEquipmentAssignedToSmeltPool(e) && !equipSmelt.ShouldHideConsumedEquipment(e)) .Where(e => equippedEquipment == null || e != equippedEquipment) .Where(MatchesCurrentFilters) .ToList(); SortEquipments(equipments); RefreshEmptyState(equipments.Count); if (Application.isPlaying && isActiveAndEnabled && gameObject.activeInHierarchy) { rebuildRoutine = StartCoroutine(RebuildBagAsync(equipments)); return; } SpawnBagItemsImmediate(equipments); RestoreScrollbarValueImmediate(); } private IEnumerator RebuildBagAsync(List equipments) { int batchSize = Mathf.Max(1, i_batch); for (int i = 0; i < equipments.Count; i++) { SpawnBagItem(equipments[i]); if ((i + 1) % batchSize == 0) { yield return null; } } rebuildRoutine = null; BeginRestoreScrollbarValue(); } private void SpawnBagItemsImmediate(List equipments) { for (int i = 0; i < equipments.Count; i++) { SpawnBagItem(equipments[i]); } } private void SpawnBagItem(equipmentSO equipment) { if (equipment == null || eqpmtItemPrefab == null || eqpmtBagParent == null) { return; } GameObject instance = Instantiate(eqpmtItemPrefab, eqpmtBagParent); instance.name = string.IsNullOrWhiteSpace(equipment.GetDisplayTierName()) ? equipment.name : equipment.GetDisplayTierName(); equipItemPrefab item = instance.GetComponent(); if (item != null) { item.Bind(equipment, null, HandleBagItemRightClicked); item.allowDrag = false; item.allowQuickTransfer = false; if (item.itemButton != null) { item.itemButton.interactable = true; } } spawnedBagItems.Add(instance); } private bool HandleBagItemRightClicked(equipmentSO equipment) { if (equipment == null) { return false; } if (IsEquippedByOtherHero(equipment, out AllyHero_SO occupiedHero)) { string heroName = occupiedHero != null ? occupiedHero.ally_heroName : "其他角色"; gNotice.warning.display($"该记忆已被{heroName}装备"); return true; } EquipEquipment(equipment); return true; } private bool HandleEquippedItemRightClicked(equipmentSO equipment) { if (equipment == null || equippedEquipment == null || equipment != equippedEquipment) { return false; } UnequipEquipment(); return true; } private void EquipEquipment(equipmentSO equipment) { equippedEquipment = equipment; if (currentHero != null) { currentHero.SetEquippedEquipment(equipment); } RefreshAllIdolPanels(); RefreshEquippedSlot(); RebuildBag(); } private void UnequipEquipment() { equippedEquipment = null; if (currentHero != null) { currentHero.ClearEquippedEquipment(); } RefreshAllIdolPanels(); RefreshEquippedSlot(); RebuildBag(); } public void SetHero(AllyHero_SO hero) { currentHero = hero; RestoreEquippedFromHero(); RefreshEquippedSlot(); RebuildBag(); } private void RestoreEquippedFromHero() { if (currentHero == null) { equippedEquipment = null; return; } currentHero.LoadEquippedEquipmentFromLocal(); equippedEquipment = currentHero.GetEquippedEquipmentResolved(); } private void RefreshEquippedSlot() { if (equippedItemInstance != null) { Destroy(equippedItemInstance); equippedItemInstance = null; } if (equippedEquipment != null && eqpmtItemPrefab != null && eqpmtToPut != null) { equippedItemInstance = Instantiate(eqpmtItemPrefab, eqpmtToPut); equippedItemInstance.name = string.IsNullOrWhiteSpace(equippedEquipment.GetDisplayTierName()) ? equippedEquipment.name : equippedEquipment.GetDisplayTierName(); equipItemPrefab item = equippedItemInstance.GetComponent(); if (item != null) { item.Bind(equippedEquipment, null, HandleEquippedItemRightClicked); item.allowDrag = false; item.allowQuickTransfer = false; if (item.itemButton != null) { item.itemButton.interactable = true; } } } RefreshInfoPanel(); } private void HandleEquipmentClicked(equipmentSO equipment) { if (equipment == null) { return; } if (this_eqpmtName != null) { this_eqpmtName.text = equipment.GetDisplayTierName(); this_eqpmtName.color = ResolveEquipmentColor(equipment.GetVisualQualityColorIndex()); } if (this_eqpmtType != null) { this_eqpmtType.text = $"{equipment.skillType} 类装备"; } if (this_eqpmtLevel != null) { this_eqpmtLevel.text = $"{equipment.level}追忆等阶"; } if (this_eqpmtID != null) { this_eqpmtID.text = $"记忆编号{BuildDisplayEquipmentId(equipment)}"; } } private void RefreshInfoPanel() { if (equippedEquipment == null) { if (this_eqpmtName != null) { this_eqpmtName.text = "可装配一个记忆"; this_eqpmtName.color = new Color32(0x32, 0x32, 0x32, 0xFF); } if (this_eqpmtType != null) this_eqpmtType.text = "请选择记忆"; if (this_eqpmtLevel != null) this_eqpmtLevel.text = string.Empty; if (this_eqpmtID != null) this_eqpmtID.text = "请选择记忆"; return; } HandleEquipmentClicked(equippedEquipment); } private bool IsEquippedByOtherHero(equipmentSO equipment, out AllyHero_SO occupiedHero) { occupiedHero = null; if (equipment == null) { return false; } AllyHero_SO[] heroes = LoadHeroAssetsForOwnershipCheck(); for (int i = 0; i < heroes.Length; i++) { AllyHero_SO hero = heroes[i]; if (hero == null || hero == currentHero) { continue; } hero.LoadEquippedEquipmentFromLocal(); equipmentSO equipped = hero.GetEquippedEquipmentResolved(); if (equipped == equipment || (!string.IsNullOrWhiteSpace(equipped?.name) && equipped.name == equipment.name)) { occupiedHero = hero; return true; } } return false; } private AllyHero_SO[] LoadHeroAssetsForOwnershipCheck() { #if UNITY_EDITOR if (!Application.isPlaying) { if (string.IsNullOrWhiteSpace("Assets/Resources/so/ally") || !AssetDatabase.IsValidFolder("Assets/Resources/so/ally")) { return Array.Empty(); } string[] guids = AssetDatabase.FindAssets("t:AllyHero_SO", new[] { "Assets/Resources/so/ally" }); var result = new List(guids.Length); for (int i = 0; i < guids.Length; i++) { string path = AssetDatabase.GUIDToAssetPath(guids[i]); AllyHero_SO hero = AssetDatabase.LoadAssetAtPath(path); if (hero != null) { result.Add(hero); } } return result.ToArray(); } #endif return Resources.LoadAll("so/ally"); } private Color ResolveEquipmentColor(int colorIndex) { if (eqpmtItemPrefab == null) { return Color.white; } equipItemPrefab prefab = eqpmtItemPrefab.GetComponent(); if (prefab == null || prefab.itemBtmColors == null || prefab.itemBtmColors.Length == 0) { return Color.white; } int safeIndex = Mathf.Clamp(colorIndex, 0, prefab.itemBtmColors.Length - 1); return prefab.itemBtmColors[safeIndex]; } private void RefreshEmptyState(int count) { if (emptyText != null) { emptyText.text = count > 0 ? string.Empty : "暂无装备"; } } private void RefreshAllIdolPanels() { UI_Idols[] panels = FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); for (int i = 0; i < panels.Length; i++) { UI_Idols panel = panels[i]; if (panel != null) { panel.RefreshCurrentHeroDetails(); } } } private void ClearBagItems() { for (int i = eqpmtBagParent != null ? eqpmtBagParent.childCount - 1 : -1; i >= 0; i--) { Transform child = eqpmtBagParent.GetChild(i); if (child != null) { Destroy(child.gameObject); } } spawnedBagItems.Clear(); } private void CacheScrollbarValue() { if (eqpmtBagScrollbar == null) { pendingScrollbarValue = 1f; return; } pendingScrollbarValue = eqpmtBagScrollbar.value; } private void BeginRestoreScrollbarValue() { if (!Application.isPlaying || !isActiveAndEnabled || !gameObject.activeInHierarchy) { RestoreScrollbarValueImmediate(); return; } if (restoreScrollbarRoutine != null) { StopCoroutine(restoreScrollbarRoutine); } restoreScrollbarRoutine = StartCoroutine(RestoreScrollbarValueNextFrame()); } private IEnumerator RestoreScrollbarValueNextFrame() { yield return null; Canvas.ForceUpdateCanvases(); yield return null; Canvas.ForceUpdateCanvases(); RestoreScrollbarValueImmediate(); restoreScrollbarRoutine = null; } private void RestoreScrollbarValueImmediate() { if (eqpmtBagScrollbar == null) { return; } ScrollRect scrollRect = eqpmtBagScrollbar.GetComponentInParent(); if (scrollRect != null && scrollRect.verticalScrollbar == eqpmtBagScrollbar) { scrollRect.verticalNormalizedPosition = pendingScrollbarValue; } eqpmtBagScrollbar.value = pendingScrollbarValue; } private bool MatchesCurrentFilters(equipmentSO equipment) { if (equipment == null) { return false; } if (onlyCanEquip != null && onlyCanEquip.isOn && !IsEquipmentUsableByCurrentHero(equipment)) { return false; } EquipFilterMode mode = GetSelectedFilterMode(); bool hasSpecialSkill = equipment.sa_skillID > 0; bool hasIllusionSkill = equipment.ia_skillID > 0; switch (mode) { case EquipFilterMode.All: return true; case EquipFilterMode.HasSpecialSkill: return hasSpecialSkill; case EquipFilterMode.HasIllusionSkill: return hasIllusionSkill; case EquipFilterMode.HasAnySkill: return hasSpecialSkill || hasIllusionSkill; case EquipFilterMode.HasNoSkill: return !hasSpecialSkill && !hasIllusionSkill; case EquipFilterMode.HasDualSkill: return hasSpecialSkill && hasIllusionSkill; case EquipFilterMode.Type0: return equipment.skillType == equipmentSO.EquipmentSkillType.题海战术; case EquipFilterMode.Type1: return equipment.skillType == equipmentSO.EquipmentSkillType.诛锄异己; case EquipFilterMode.Type2: return equipment.skillType == equipmentSO.EquipmentSkillType.逸乐共谋; case EquipFilterMode.Type3: return equipment.skillType == equipmentSO.EquipmentSkillType.完美至上; case EquipFilterMode.Type4: return equipment.skillType == equipmentSO.EquipmentSkillType.涵养心境; case EquipFilterMode.Type5: return equipment.skillType == equipmentSO.EquipmentSkillType.众志成城; case EquipFilterMode.Type6: return equipment.skillType == equipmentSO.EquipmentSkillType.过激行为; case EquipFilterMode.Type7: return equipment.skillType == equipmentSO.EquipmentSkillType.二次形变; default: return true; } } private bool IsEquipmentUsableByCurrentHero(equipmentSO equipment) { if (equipment == null || currentHero == null) { return false; } if (equipment.GetVisualTierPresentation() == null || string.IsNullOrWhiteSpace(equipment.GetDisplayTierName())) { return false; } return !IsEquippedByOtherHero(equipment, out _); } private void SortEquipments(List equipments) { if (equipments == null) { return; } EquipOrderMode mode = GetSelectedOrderMode(); switch (mode) { case EquipOrderMode.AcquireAsc: equipments.Sort((a, b) => CompareAcquire(a, b, false)); break; case EquipOrderMode.AcquireDesc: equipments.Sort((a, b) => CompareAcquire(a, b, true)); break; case EquipOrderMode.QualityAsc: equipments.Sort((a, b) => CompareQuality(a, b, false)); break; case EquipOrderMode.QualityDesc: equipments.Sort((a, b) => CompareQuality(a, b, true)); break; default: equipments.Sort((a, b) => CompareAcquire(a, b, false)); break; } } private EquipFilterMode GetSelectedFilterMode() { int index = typeFilter != null ? Mathf.Clamp(typeFilter.value, 0, FilterOptions.Length - 1) : 0; return FilterOptions[index].mode; } private EquipOrderMode GetSelectedOrderMode() { int index = orderFilter != null ? Mathf.Clamp(orderFilter.value, 0, OrderOptions.Length - 1) : 0; return OrderOptions[index].mode; } private equipmentSO[] LoadEquipments() { var results = new List(); #if UNITY_EDITOR if (!Application.isPlaying) { results.AddRange(LoadEditorEquipments()); } else #endif { results.AddRange(Resources.LoadAll(runtimeEquipmentFolder).Where(e => e != null)); } IReadOnlyList runtimeGenerated = equipmentGenerator.GetRuntimeGeneratedEquipments(); if (runtimeGenerated != null) { for (int i = 0; i < runtimeGenerated.Count; i++) { equipmentSO equipment = runtimeGenerated[i]; if (equipment != null && !results.Contains(equipment)) { results.Add(equipment); } } } return results.ToArray(); } private static int CompareAcquire(equipmentSO left, equipmentSO right, bool descending) { ParseEquipmentSortKey(left, out long leftDate, out long leftId); ParseEquipmentSortKey(right, out long rightDate, out long rightId); int dateCompare = leftDate.CompareTo(rightDate); if (dateCompare != 0) { return descending ? -dateCompare : dateCompare; } int idCompare = leftId.CompareTo(rightId); if (idCompare != 0) { return descending ? -idCompare : idCompare; } string leftName = left != null ? left.GetDisplayTierName() : string.Empty; string rightName = right != null ? right.GetDisplayTierName() : string.Empty; int nameCompare = string.Compare(leftName, rightName, StringComparison.Ordinal); return descending ? -nameCompare : nameCompare; } private static int CompareQuality(equipmentSO left, equipmentSO right, bool descending) { int qualityCompare = left.GetVisualQualityColorIndex().CompareTo(right.GetVisualQualityColorIndex()); if (qualityCompare != 0) { return descending ? -qualityCompare : qualityCompare; } return CompareAcquire(left, right, true); } private static void ParseEquipmentSortKey(equipmentSO equipment, out long datePart, out long idPart) { datePart = 0L; idPart = 0L; if (equipment == null || string.IsNullOrWhiteSpace(equipment.name)) { return; } string raw = equipment.name; if (raw.StartsWith("type", StringComparison.OrdinalIgnoreCase)) { int firstUnderscore = raw.IndexOf('_'); if (firstUnderscore >= 0 && firstUnderscore + 1 < raw.Length) { raw = raw.Substring(firstUnderscore + 1); } } string[] parts = raw.Split('_'); if (parts.Length >= 2) { long.TryParse(parts[0], out datePart); long.TryParse(parts[1], out idPart); } } #if UNITY_EDITOR private equipmentSO[] LoadEditorEquipments() { if (string.IsNullOrWhiteSpace(editorEquipmentFolder) || !AssetDatabase.IsValidFolder(editorEquipmentFolder)) { return Array.Empty(); } string[] guids = AssetDatabase.FindAssets("t:equipmentSO", new[] { editorEquipmentFolder }); var result = new List(guids.Length); for (int i = 0; i < guids.Length; i++) { string path = AssetDatabase.GUIDToAssetPath(guids[i]); equipmentSO equipment = AssetDatabase.LoadAssetAtPath(path); if (equipment != null) { result.Add(equipment); } } return result.ToArray(); } #endif private static string BuildDisplayEquipmentId(equipmentSO equipment) { if (equipment == null || string.IsNullOrWhiteSpace(equipment.name)) { return string.Empty; } string raw = equipment.name.Replace("(Clone)", string.Empty).Replace("(Preview)", string.Empty).Replace("(TourPreview)", string.Empty); return raw.StartsWith("type", StringComparison.OrdinalIgnoreCase) && raw.Length > 4 ? raw.Substring(4) : raw; } }