543 lines
16 KiB
C#
543 lines
16 KiB
C#
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 equipBag : 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("prefab")]
|
|
public GameObject eqpmtItemPrefab;
|
|
public Transform eqpmtItemParent;
|
|
|
|
[Header("dropdown")]
|
|
public Dropdown orderDropdown;
|
|
public Dropdown filterDropdown;
|
|
|
|
[Header("source paths")]
|
|
public string runtimeEquipmentFolder = "so/uEquip";
|
|
public string editorEquipmentFolder = "Assets/Resources/so/uEquip";
|
|
|
|
[Header("async spawn")]
|
|
public int spawnBatchSize = 12;
|
|
|
|
private readonly List<GameObject> spawnedItems = new List<GameObject>();
|
|
private Coroutine rebuildCoroutine;
|
|
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 Awake()
|
|
{
|
|
InitializeDropdowns();
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
Rebuild();
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
InitializeDropdowns();
|
|
Rebuild();
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
if (orderDropdown != null)
|
|
{
|
|
orderDropdown.onValueChanged.RemoveListener(HandleOrderChanged);
|
|
}
|
|
|
|
if (filterDropdown != null)
|
|
{
|
|
filterDropdown.onValueChanged.RemoveListener(HandleFilterChanged);
|
|
}
|
|
|
|
if (rebuildCoroutine != null)
|
|
{
|
|
StopCoroutine(rebuildCoroutine);
|
|
rebuildCoroutine = null;
|
|
}
|
|
}
|
|
|
|
public void Rebuild()
|
|
{
|
|
if (rebuildCoroutine != null)
|
|
{
|
|
StopCoroutine(rebuildCoroutine);
|
|
rebuildCoroutine = null;
|
|
}
|
|
|
|
ClearSpawnedItems();
|
|
|
|
if (eqpmtItemPrefab == null || eqpmtItemParent == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
List<equipmentSO> equipments = LoadEquipments()
|
|
.Where(equipment => equipment != null
|
|
&& !equipSmelt.IsEquipmentAssignedToSmeltPool(equipment)
|
|
&& !equipSmelt.ShouldHideConsumedEquipment(equipment))
|
|
.Where(MatchesCurrentFilter)
|
|
.ToList();
|
|
|
|
SortEquipments(equipments);
|
|
|
|
if (Application.isPlaying && isActiveAndEnabled && gameObject.activeInHierarchy)
|
|
{
|
|
rebuildCoroutine = StartCoroutine(RebuildAsync(equipments));
|
|
return;
|
|
}
|
|
|
|
SpawnAllImmediate(equipments);
|
|
}
|
|
|
|
private void InitializeDropdowns()
|
|
{
|
|
InitializeFilterDropdown();
|
|
InitializeOrderDropdown();
|
|
}
|
|
|
|
private void InitializeFilterDropdown()
|
|
{
|
|
if (filterDropdown == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
suppressDropdownCallbacks = true;
|
|
filterDropdown.onValueChanged.RemoveListener(HandleFilterChanged);
|
|
filterDropdown.ClearOptions();
|
|
filterDropdown.AddOptions(FilterOptions.Select(option => new Dropdown.OptionData(option.label)).ToList());
|
|
filterDropdown.value = 0;
|
|
filterDropdown.RefreshShownValue();
|
|
filterDropdown.onValueChanged.AddListener(HandleFilterChanged);
|
|
suppressDropdownCallbacks = false;
|
|
}
|
|
|
|
private void InitializeOrderDropdown()
|
|
{
|
|
if (orderDropdown == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
suppressDropdownCallbacks = true;
|
|
orderDropdown.onValueChanged.RemoveListener(HandleOrderChanged);
|
|
orderDropdown.ClearOptions();
|
|
orderDropdown.AddOptions(OrderOptions.Select(option => new Dropdown.OptionData(option.label)).ToList());
|
|
orderDropdown.value = 0;
|
|
orderDropdown.RefreshShownValue();
|
|
orderDropdown.onValueChanged.AddListener(HandleOrderChanged);
|
|
suppressDropdownCallbacks = false;
|
|
}
|
|
|
|
private void HandleFilterChanged(int _)
|
|
{
|
|
if (!suppressDropdownCallbacks)
|
|
{
|
|
Rebuild();
|
|
}
|
|
}
|
|
|
|
private void HandleOrderChanged(int _)
|
|
{
|
|
if (!suppressDropdownCallbacks)
|
|
{
|
|
Rebuild();
|
|
}
|
|
}
|
|
|
|
private IEnumerator RebuildAsync(List<equipmentSO> equipments)
|
|
{
|
|
if (equipments == null || equipments.Count == 0)
|
|
{
|
|
rebuildCoroutine = null;
|
|
yield break;
|
|
}
|
|
|
|
int batchSize = Mathf.Max(1, spawnBatchSize);
|
|
for (int i = 0; i < equipments.Count; i++)
|
|
{
|
|
SpawnOne(equipments[i]);
|
|
|
|
if ((i + 1) % batchSize == 0)
|
|
{
|
|
yield return null;
|
|
}
|
|
}
|
|
|
|
rebuildCoroutine = null;
|
|
}
|
|
|
|
private void SpawnAllImmediate(List<equipmentSO> equipments)
|
|
{
|
|
if (equipments == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < equipments.Count; i++)
|
|
{
|
|
SpawnOne(equipments[i]);
|
|
}
|
|
}
|
|
|
|
private void SpawnOne(equipmentSO equipment)
|
|
{
|
|
if (equipment == null || eqpmtItemPrefab == null || eqpmtItemParent == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GameObject instance = Instantiate(eqpmtItemPrefab, eqpmtItemParent);
|
|
string displayName = equipment.GetDisplayTierName();
|
|
instance.name = string.IsNullOrWhiteSpace(displayName) ? equipment.name : displayName;
|
|
|
|
equipItemPrefab item = instance.GetComponent<equipItemPrefab>();
|
|
if (item != null)
|
|
{
|
|
item.Bind(equipment, HandleEquipmentClicked);
|
|
}
|
|
|
|
spawnedItems.Add(instance);
|
|
}
|
|
|
|
private equipmentSO[] LoadEquipments()
|
|
{
|
|
var results = new List<equipmentSO>();
|
|
var indexedByName = new Dictionary<string, int>(StringComparer.Ordinal);
|
|
#if UNITY_EDITOR
|
|
if (!Application.isPlaying)
|
|
{
|
|
results.AddRange(LoadEditorEquipments());
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
results.AddRange(Resources.LoadAll<equipmentSO>(runtimeEquipmentFolder)
|
|
.Where(equipment => equipment != null));
|
|
}
|
|
|
|
for (int i = 0; i < results.Count; i++)
|
|
{
|
|
equipmentSO equipment = results[i];
|
|
if (equipment == null || string.IsNullOrWhiteSpace(equipment.name))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
indexedByName[equipment.name] = i;
|
|
}
|
|
|
|
IReadOnlyList<equipmentSO> runtimeGenerated = equipmentGenerator.GetRuntimeGeneratedEquipments();
|
|
if (runtimeGenerated != null)
|
|
{
|
|
for (int i = 0; i < runtimeGenerated.Count; i++)
|
|
{
|
|
equipmentSO equipment = runtimeGenerated[i];
|
|
if (equipment == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(equipment.name) && indexedByName.TryGetValue(equipment.name, out int existingIndex))
|
|
{
|
|
results[existingIndex] = equipment;
|
|
continue;
|
|
}
|
|
|
|
results.Add(equipment);
|
|
if (!string.IsNullOrWhiteSpace(equipment.name))
|
|
{
|
|
indexedByName[equipment.name] = results.Count - 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return results.Where(equipment => equipment != null).ToArray();
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
private equipmentSO[] LoadEditorEquipments()
|
|
{
|
|
if (string.IsNullOrWhiteSpace(editorEquipmentFolder) || !AssetDatabase.IsValidFolder(editorEquipmentFolder))
|
|
{
|
|
return Array.Empty<equipmentSO>();
|
|
}
|
|
|
|
string[] guids = AssetDatabase.FindAssets("t:equipmentSO", new[] { editorEquipmentFolder });
|
|
var result = new List<equipmentSO>(guids.Length);
|
|
for (int i = 0; i < guids.Length; i++)
|
|
{
|
|
string path = AssetDatabase.GUIDToAssetPath(guids[i]);
|
|
equipmentSO equipment = AssetDatabase.LoadAssetAtPath<equipmentSO>(path);
|
|
if (equipment != null)
|
|
{
|
|
result.Add(equipment);
|
|
}
|
|
}
|
|
|
|
return result.ToArray();
|
|
}
|
|
#endif
|
|
|
|
private bool MatchesCurrentFilter(equipmentSO equipment)
|
|
{
|
|
if (equipment == null)
|
|
{
|
|
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 void SortEquipments(List<equipmentSO> equipments)
|
|
{
|
|
if (equipments == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
EquipOrderMode mode = GetSelectedOrderMode();
|
|
equipments.Sort((left, right) => CompareEquipments(left, right, mode));
|
|
}
|
|
|
|
private EquipFilterMode GetSelectedFilterMode()
|
|
{
|
|
int index = filterDropdown != null ? Mathf.Clamp(filterDropdown.value, 0, FilterOptions.Length - 1) : 0;
|
|
return FilterOptions[index].mode;
|
|
}
|
|
|
|
private EquipOrderMode GetSelectedOrderMode()
|
|
{
|
|
int index = orderDropdown != null ? Mathf.Clamp(orderDropdown.value, 0, OrderOptions.Length - 1) : 0;
|
|
return OrderOptions[index].mode;
|
|
}
|
|
|
|
private void HandleEquipmentClicked(equipmentSO equipment)
|
|
{
|
|
if (equipment == null)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
private void ClearSpawnedItems()
|
|
{
|
|
if (eqpmtItemParent != null)
|
|
{
|
|
for (int i = eqpmtItemParent.childCount - 1; i >= 0; i--)
|
|
{
|
|
Transform child = eqpmtItemParent.GetChild(i);
|
|
if (child == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
if (!Application.isPlaying)
|
|
{
|
|
DestroyImmediate(child.gameObject);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
Destroy(child.gameObject);
|
|
}
|
|
}
|
|
}
|
|
|
|
spawnedItems.Clear();
|
|
}
|
|
|
|
private static int CompareEquipments(equipmentSO left, equipmentSO right, EquipOrderMode mode)
|
|
{
|
|
if (left == right)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (left == null)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
if (right == null)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
switch (mode)
|
|
{
|
|
case EquipOrderMode.AcquireAsc:
|
|
return CompareAcquire(left, right, false);
|
|
case EquipOrderMode.AcquireDesc:
|
|
return CompareAcquire(left, right, true);
|
|
case EquipOrderMode.QualityAsc:
|
|
return CompareQuality(left, right, false);
|
|
case EquipOrderMode.QualityDesc:
|
|
return CompareQuality(left, right, true);
|
|
default:
|
|
return CompareAcquire(left, right, false);
|
|
}
|
|
}
|
|
|
|
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.GetDisplayTierName();
|
|
string rightName = right.GetDisplayTierName();
|
|
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);
|
|
}
|
|
}
|
|
}
|