using System.Collections.Generic;
|
using System.Linq;
|
using System.Reflection;
|
using UnityEditor;
|
using UnityEngine;
|
|
/// <summary>
|
/// 战斗网络包查看器
|
/// 用于查看和搜索 DTCB430_tagSCTurnFightReport 解析的战斗包
|
/// 快捷键:Alt+8
|
/// </summary>
|
public class BattleReportChecker : EditorWindow
|
{
|
#region 静态数据存储
|
|
// 存储所有战斗包数据,按 guid 组织
|
private static Dictionary<string, List<PackageInfo>> allBattlePackages = new Dictionary<string, List<PackageInfo>>();
|
|
// 跟踪已执行的包UID(按 guid 组织)
|
private static Dictionary<string, HashSet<ulong>> executedPackUIDs = new Dictionary<string, HashSet<ulong>>();
|
|
// 跟踪当前正在执行的包UID(按 guid 组织)
|
private static Dictionary<string, ulong> currentExecutingPackUID = new Dictionary<string, ulong>();
|
|
// 包信息类
|
public class PackageInfo
|
{
|
public GameNetPackBasic package;
|
public string displayName;
|
public ulong packUID;
|
public string typeName;
|
public bool isExpanded;
|
public Dictionary<string, string> fields = new Dictionary<string, string>();
|
public Dictionary<string, FieldDetail> fieldDetails = new Dictionary<string, FieldDetail>();
|
|
public PackageInfo(GameNetPackBasic pack)
|
{
|
package = pack;
|
packUID = pack.packUID;
|
typeName = pack.GetType().Name;
|
displayName = $"[{packUID}] {typeName}";
|
isExpanded = false;
|
ExtractFields();
|
}
|
|
private void ExtractFields()
|
{
|
if (package == null) return;
|
|
var type = package.GetType();
|
var fieldInfos = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
|
|
foreach (var field in fieldInfos)
|
{
|
try
|
{
|
var value = field.GetValue(package);
|
string valueStr = value != null ? value.ToString() : "null";
|
|
var detail = new FieldDetail { value = value };
|
|
// 特殊处理数组(注意:先判断Array,因为Array不是IList的子类)
|
if (value != null && value.GetType().IsArray)
|
{
|
System.Array array = value as System.Array;
|
valueStr = $"Array[{array.Length}]";
|
detail.isArray = true;
|
detail.arrayValue = array;
|
}
|
// 特殊处理列表(IList包括List<T>和其他列表类型)
|
else if (value is System.Collections.IList list && !(value is string))
|
{
|
valueStr = $"List[{list.Count}]";
|
detail.isList = true;
|
detail.listValue = list;
|
}
|
|
fields[field.Name] = valueStr;
|
fieldDetails[field.Name] = detail;
|
}
|
catch (System.Exception ex)
|
{
|
fields[field.Name] = "读取失败: " + ex.Message;
|
fieldDetails[field.Name] = new FieldDetail { value = null };
|
}
|
}
|
}
|
}
|
|
// 字段详细信息类
|
public class FieldDetail
|
{
|
public object value;
|
public bool isArray;
|
public bool isList;
|
public bool isExpanded;
|
public System.Array arrayValue;
|
public System.Collections.IList listValue;
|
|
// 嵌套的子字段详情(用于复杂类型)
|
public Dictionary<string, FieldDetail> subFieldDetails = new Dictionary<string, FieldDetail>();
|
|
// 数组/列表元素的展开状态
|
public Dictionary<int, ElementDetail> elementDetails = new Dictionary<int, ElementDetail>();
|
}
|
|
// 数组/列表元素详细信息类
|
public class ElementDetail
|
{
|
public bool isExpanded;
|
public Dictionary<string, FieldDetail> fieldDetails = new Dictionary<string, FieldDetail>();
|
}
|
|
/// <summary>
|
/// 供外部调用,添加解析后的包
|
/// </summary>
|
public static void AddBattlePackages(string guid, List<GameNetPackBasic> packages)
|
{
|
if (packages == null || packages.Count == 0) return;
|
|
var packInfoList = packages.Select(p => new PackageInfo(p)).ToList();
|
|
if (allBattlePackages.ContainsKey(guid))
|
{
|
allBattlePackages[guid].AddRange(packInfoList);
|
}
|
else
|
{
|
allBattlePackages[guid] = packInfoList;
|
}
|
|
Debug.Log($"[BattleReportChecker] 已记录 {packages.Count} 个包,GUID: {guid}");
|
}
|
|
/// <summary>
|
/// 清空所有记录
|
/// </summary>
|
public static void ClearAllPackages()
|
{
|
allBattlePackages.Clear();
|
executedPackUIDs.Clear();
|
currentExecutingPackUID.Clear();
|
Debug.Log("[BattleReportChecker] 已清空所有包记录");
|
}
|
|
/// <summary>
|
/// 标记包开始执行
|
/// </summary>
|
public static void MarkPackageExecuting(string guid, ulong packUID)
|
{
|
if (!currentExecutingPackUID.ContainsKey(guid))
|
{
|
currentExecutingPackUID[guid] = packUID;
|
}
|
else
|
{
|
currentExecutingPackUID[guid] = packUID;
|
}
|
}
|
|
/// <summary>
|
/// 标记包执行完成
|
/// </summary>
|
public static void MarkPackageExecuted(string guid, ulong packUID)
|
{
|
if (!executedPackUIDs.ContainsKey(guid))
|
{
|
executedPackUIDs[guid] = new HashSet<ulong>();
|
}
|
executedPackUIDs[guid].Add(packUID);
|
|
// 清除当前执行标记
|
if (currentExecutingPackUID.ContainsKey(guid) && currentExecutingPackUID[guid] == packUID)
|
{
|
currentExecutingPackUID.Remove(guid);
|
}
|
}
|
|
#endregion
|
|
#region 窗口管理
|
|
[MenuItem("程序/战斗网络包查看器 &8")] // Alt+8
|
public static void ShowWindow()
|
{
|
var window = GetWindow<BattleReportChecker>("战斗包查看器");
|
window.minSize = new Vector2(600, 400);
|
window.Show();
|
}
|
|
#endregion
|
|
#region UI 状态
|
|
private Vector2 scrollPosition;
|
private Vector2 detailScrollPosition;
|
private string searchText = "";
|
private string detailSearchText = ""; // 详情搜索文本
|
private string lastDetailSearchText = ""; // 上次的详情搜索文本,用于检测变化
|
private string selectedGuid = "";
|
private List<string> guidList = new List<string>();
|
private int selectedGuidIndex = 0;
|
private List<PackageInfo> filteredPackages = new List<PackageInfo>();
|
private PackageInfo selectedPackage = null;
|
private PackageInfo lastSelectedPackage = null; // 上次选中的包,用于检测包切换
|
|
// 高亮相关
|
private PackageInfo highlightedPackage = null;
|
private double highlightStartTime = 0;
|
private const double highlightDuration = 5.0; // 高亮持续5秒
|
private int selectedPackageIndex = -1; // 记录选中包在列表中的索引,用于滚动
|
|
// 搜索缓存
|
private HashSet<string> matchedFieldNames = new HashSet<string>(); // 缓存匹配的字段名
|
private Dictionary<string, bool> fieldHasMatchedChild = new Dictionary<string, bool>(); // 缓存字段是否有匹配的子元素
|
|
#endregion
|
|
#region Unity 生命周期
|
|
private void OnEnable()
|
{
|
RefreshGuidList();
|
// 启用编辑器更新,让颜色实时刷新
|
EditorApplication.update += OnEditorUpdate;
|
}
|
|
private void OnDisable()
|
{
|
// 移除更新回调
|
EditorApplication.update -= OnEditorUpdate;
|
}
|
|
private void OnEditorUpdate()
|
{
|
// 定期刷新UI以更新包的执行状态颜色
|
Repaint();
|
}
|
|
private void OnGUI()
|
{
|
EditorGUILayout.BeginVertical();
|
|
DrawToolbar();
|
DrawGuidSelector();
|
DrawSearchBar();
|
|
EditorGUILayout.BeginHorizontal();
|
DrawPackageList();
|
DrawPackageDetails();
|
EditorGUILayout.EndHorizontal();
|
|
EditorGUILayout.EndVertical();
|
}
|
|
#endregion
|
|
#region UI 绘制
|
|
private void DrawToolbar()
|
{
|
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
|
if (GUILayout.Button("刷新", EditorStyles.toolbarButton, GUILayout.Width(60)))
|
{
|
// 刷新前清空搜索缓存,避免卡死
|
matchedFieldNames.Clear();
|
fieldHasMatchedChild.Clear();
|
lastSelectedPackage = null;
|
lastDetailSearchText = "";
|
|
RefreshGuidList();
|
FilterPackages();
|
}
|
|
if (GUILayout.Button("清空所有", EditorStyles.toolbarButton, GUILayout.Width(80)))
|
{
|
if (EditorUtility.DisplayDialog("确认", "确定要清空所有包记录吗?", "确定", "取消"))
|
{
|
ClearAllPackages();
|
RefreshGuidList();
|
FilterPackages();
|
}
|
}
|
|
GUILayout.FlexibleSpace();
|
|
GUILayout.Label($"总包数: {filteredPackages.Count}", EditorStyles.toolbarButton);
|
|
EditorGUILayout.EndHorizontal();
|
}
|
|
private void DrawGuidSelector()
|
{
|
EditorGUILayout.BeginHorizontal();
|
|
GUILayout.Label("战场GUID:", GUILayout.Width(80));
|
|
if (guidList.Count == 0)
|
{
|
GUILayout.Label("暂无数据", EditorStyles.helpBox);
|
}
|
else
|
{
|
int newIndex = EditorGUILayout.Popup(selectedGuidIndex, guidList.ToArray());
|
if (newIndex != selectedGuidIndex)
|
{
|
selectedGuidIndex = newIndex;
|
selectedGuid = guidList[selectedGuidIndex];
|
FilterPackages();
|
}
|
}
|
|
EditorGUILayout.EndHorizontal();
|
EditorGUILayout.Space(5);
|
}
|
|
private void DrawSearchBar()
|
{
|
EditorGUILayout.BeginHorizontal();
|
|
GUILayout.Label("搜索:", GUILayout.Width(80));
|
|
string newSearchText = EditorGUILayout.TextField(searchText);
|
if (newSearchText != searchText)
|
{
|
searchText = newSearchText;
|
FilterPackages();
|
|
// 搜索后自动选中第一个结果并高亮
|
if (filteredPackages.Count > 0)
|
{
|
selectedPackage = filteredPackages[0];
|
selectedPackageIndex = 0;
|
StartHighlight(selectedPackage);
|
ScrollToSelectedPackage();
|
}
|
}
|
|
if (GUILayout.Button("清空", GUILayout.Width(60)))
|
{
|
searchText = "";
|
FilterPackages();
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
GUILayout.Label("支持搜索:包类型名、UID、字段值", EditorStyles.miniLabel);
|
EditorGUILayout.Space(5);
|
}
|
|
private void DrawPackageList()
|
{
|
EditorGUILayout.BeginVertical(GUILayout.Width(position.width * 0.5f));
|
|
GUILayout.Label("包列表", EditorStyles.boldLabel);
|
|
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition, GUI.skin.box);
|
|
if (filteredPackages.Count == 0)
|
{
|
GUILayout.Label("没有找到匹配的包", EditorStyles.helpBox);
|
}
|
else
|
{
|
foreach (var packInfo in filteredPackages)
|
{
|
DrawPackageItem(packInfo);
|
}
|
}
|
|
EditorGUILayout.EndScrollView();
|
EditorGUILayout.EndVertical();
|
}
|
|
private void DrawPackageItem(PackageInfo packInfo)
|
{
|
// 判断包的执行状态
|
bool isExecuting = currentExecutingPackUID.ContainsKey(selectedGuid) &&
|
currentExecutingPackUID[selectedGuid] == packInfo.packUID;
|
bool isExecuted = executedPackUIDs.ContainsKey(selectedGuid) &&
|
executedPackUIDs[selectedGuid].Contains(packInfo.packUID);
|
|
// 判断是否是高亮状态
|
bool isHighlighted = IsPackageHighlighted(packInfo);
|
|
// 设置背景颜色
|
Color originalBgColor = GUI.backgroundColor;
|
if (isHighlighted)
|
{
|
// 高亮状态 - 使用绿色闪烁效果
|
float pulse = Mathf.PingPong((float)EditorApplication.timeSinceStartup * 2f, 1f);
|
GUI.backgroundColor = Color.Lerp(new Color(0.3f, 0.8f, 0.3f), new Color(0.5f, 1f, 0.5f), pulse);
|
}
|
else if (isExecuting)
|
{
|
GUI.backgroundColor = new Color(0.3f, 0.3f, 0.3f); // 深黑色(正在执行)
|
}
|
else if (isExecuted)
|
{
|
GUI.backgroundColor = new Color(0.6f, 0.6f, 0.6f); // 灰色(已执行)
|
}
|
else
|
{
|
GUI.backgroundColor = new Color(0.8f, 0.8f, 0.8f); // 浅灰色(未执行)
|
}
|
|
EditorGUILayout.BeginVertical(GUI.skin.box);
|
|
GUI.backgroundColor = originalBgColor; // 恢复原始颜色
|
|
EditorGUILayout.BeginHorizontal();
|
|
// 展开/折叠按钮
|
string foldoutLabel = packInfo.isExpanded ? "▼" : "▶";
|
if (GUILayout.Button(foldoutLabel, EditorStyles.label, GUILayout.Width(20)))
|
{
|
packInfo.isExpanded = !packInfo.isExpanded;
|
}
|
|
// 包名称(可点击选中)
|
bool isSelected = selectedPackage == packInfo;
|
|
// 根据状态设置文本颜色
|
GUIStyle style;
|
if (isSelected)
|
{
|
style = new GUIStyle(EditorStyles.boldLabel) { normal = { textColor = Color.cyan } };
|
}
|
else if (isExecuting)
|
{
|
style = new GUIStyle(EditorStyles.label) { normal = { textColor = Color.yellow } }; // 黄色高亮
|
}
|
else if (isExecuted)
|
{
|
style = new GUIStyle(EditorStyles.label) { normal = { textColor = Color.gray } };
|
}
|
else
|
{
|
style = EditorStyles.label;
|
}
|
|
if (GUILayout.Button(packInfo.displayName, style))
|
{
|
selectedPackage = packInfo;
|
selectedPackageIndex = filteredPackages.IndexOf(packInfo);
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
// 展开显示简要信息
|
if (packInfo.isExpanded)
|
{
|
EditorGUI.indentLevel++;
|
|
// 显示前5个字段
|
int count = 0;
|
foreach (var field in packInfo.fields)
|
{
|
if (count >= 5) break;
|
|
EditorGUILayout.BeginHorizontal();
|
GUILayout.Label(field.Key + ":", GUILayout.Width(120));
|
GUILayout.Label(field.Value, EditorStyles.wordWrappedLabel);
|
EditorGUILayout.EndHorizontal();
|
|
count++;
|
}
|
|
if (packInfo.fields.Count > 5)
|
{
|
GUILayout.Label($"... 更多字段请在右侧查看", EditorStyles.miniLabel);
|
}
|
|
EditorGUI.indentLevel--;
|
}
|
|
EditorGUILayout.EndVertical();
|
}
|
|
private void DrawPackageDetails()
|
{
|
EditorGUILayout.BeginVertical(GUILayout.Width(position.width * 0.5f - 10));
|
|
GUILayout.Label("包详情", EditorStyles.boldLabel);
|
|
if (selectedPackage == null)
|
{
|
GUILayout.Label("请从左侧列表选择一个包查看详情", EditorStyles.helpBox);
|
}
|
else
|
{
|
// 详情搜索栏
|
EditorGUILayout.BeginHorizontal();
|
GUILayout.Label("搜索字段:", GUILayout.Width(70));
|
detailSearchText = EditorGUILayout.TextField(detailSearchText);
|
if (GUILayout.Button("清空", GUILayout.Width(50)))
|
{
|
detailSearchText = "";
|
}
|
EditorGUILayout.EndHorizontal();
|
|
// 检测选中包或搜索文本变化,更新缓存
|
bool packageChanged = selectedPackage != lastSelectedPackage;
|
bool searchTextChanged = detailSearchText != lastDetailSearchText;
|
|
if (packageChanged || searchTextChanged)
|
{
|
lastDetailSearchText = detailSearchText;
|
lastSelectedPackage = selectedPackage;
|
UpdateDetailSearchCache();
|
}
|
|
if (string.IsNullOrEmpty(detailSearchText))
|
{
|
GUILayout.Label("在此包的字段中搜索(字段名或值)", EditorStyles.miniLabel);
|
}
|
else
|
{
|
GUILayout.Label($"找到 {matchedFieldNames.Count} 个匹配的字段(已自动展开并高亮)", EditorStyles.miniLabel);
|
}
|
EditorGUILayout.Space(5);
|
|
detailScrollPosition = EditorGUILayout.BeginScrollView(detailScrollPosition, GUI.skin.box);
|
|
// 标题
|
GUILayout.Label(selectedPackage.displayName, EditorStyles.boldLabel);
|
EditorGUILayout.Space(5);
|
|
// 基本信息 - 支持搜索高亮
|
string searchLower = detailSearchText.ToLower();
|
bool typeMatched = !string.IsNullOrEmpty(detailSearchText) && selectedPackage.typeName.ToLower().Contains(searchLower);
|
bool uidMatched = !string.IsNullOrEmpty(detailSearchText) && selectedPackage.packUID.ToString().Contains(searchLower);
|
bool cmdMatched = !string.IsNullOrEmpty(detailSearchText) && selectedPackage.package.cmd.ToString().Contains(searchLower);
|
|
DrawDetailFieldWithHighlight("包类型", selectedPackage.typeName, typeMatched);
|
DrawDetailFieldWithHighlight("UID", selectedPackage.packUID.ToString(), uidMatched);
|
DrawDetailFieldWithHighlight("Cmd", selectedPackage.package.cmd.ToString(), cmdMatched);
|
EditorGUILayout.Space(10);
|
|
// 所有字段
|
GUILayout.Label("字段列表:", EditorStyles.boldLabel);
|
|
// 根据搜索文本过滤字段
|
foreach (var field in selectedPackage.fields)
|
{
|
if (ShouldShowDetailField(field.Key, field.Value, selectedPackage))
|
{
|
DrawDetailFieldWithExpand(field.Key, field.Value, selectedPackage);
|
}
|
}
|
|
EditorGUILayout.EndScrollView();
|
}
|
|
EditorGUILayout.EndVertical();
|
}
|
|
private void DrawDetailField(string fieldName, string value)
|
{
|
EditorGUILayout.BeginHorizontal();
|
|
GUILayout.Label(fieldName + ":", GUILayout.Width(150));
|
|
EditorGUILayout.SelectableLabel(value, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
|
|
EditorGUILayout.EndHorizontal();
|
}
|
|
private void DrawDetailFieldWithHighlight(string fieldName, string value, bool isMatched)
|
{
|
if (isMatched)
|
{
|
Color originalBgColor = GUI.backgroundColor;
|
GUI.backgroundColor = new Color(1f, 1f, 0.5f); // 淡黄色背景
|
EditorGUILayout.BeginHorizontal(GUI.skin.box);
|
GUI.backgroundColor = originalBgColor;
|
|
GUIStyle labelStyle = new GUIStyle(GUI.skin.label) { fontStyle = FontStyle.Bold };
|
GUILayout.Label(fieldName + ":", labelStyle, GUILayout.Width(150));
|
EditorGUILayout.SelectableLabel(value, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
|
|
EditorGUILayout.EndHorizontal();
|
}
|
else
|
{
|
DrawDetailField(fieldName, value);
|
}
|
}
|
|
private void DrawDetailFieldWithExpand(string fieldName, string value, PackageInfo packInfo)
|
{
|
if (!packInfo.fieldDetails.ContainsKey(fieldName))
|
{
|
DrawDetailField(fieldName, value);
|
return;
|
}
|
|
var detail = packInfo.fieldDetails[fieldName];
|
|
// 使用缓存检查是否匹配
|
bool isMatched = matchedFieldNames.Contains(fieldName);
|
|
// 使用缓存检查子元素是否匹配
|
bool hasMatchedChild = false;
|
if (!string.IsNullOrEmpty(detailSearchText))
|
{
|
fieldHasMatchedChild.TryGetValue(fieldName, out hasMatchedChild);
|
}
|
|
// 如果自身匹配或子元素匹配,自动展开
|
if ((isMatched || hasMatchedChild) && !string.IsNullOrEmpty(detailSearchText))
|
{
|
detail.isExpanded = true;
|
}
|
|
// 判断是否是复杂对象(非基本类型、非字符串、非枚举)
|
bool isComplexObject = detail.value != null &&
|
!detail.value.GetType().IsPrimitive &&
|
detail.value.GetType() != typeof(string) &&
|
!detail.value.GetType().IsEnum &&
|
!detail.isArray &&
|
!detail.isList;
|
|
// 如果是数组、列表或复杂对象,显示展开按钮
|
if (detail.isArray || detail.isList || isComplexObject)
|
{
|
// 如果匹配或有匹配的子元素,添加背景颜色
|
Color originalBgColor = GUI.backgroundColor;
|
if (isMatched || hasMatchedChild)
|
{
|
GUI.backgroundColor = new Color(1f, 1f, 0.5f); // 淡黄色背景
|
}
|
|
EditorGUILayout.BeginHorizontal((isMatched || hasMatchedChild) ? GUI.skin.box : GUIStyle.none);
|
GUI.backgroundColor = originalBgColor;
|
|
// 展开按钮
|
string foldoutLabel = detail.isExpanded ? "▼" : "▶";
|
if (GUILayout.Button(foldoutLabel, EditorStyles.label, GUILayout.Width(20)))
|
{
|
detail.isExpanded = !detail.isExpanded;
|
}
|
|
// 字段名样式
|
GUIStyle labelStyle = (isMatched || hasMatchedChild) ? new GUIStyle(GUI.skin.label) { fontStyle = FontStyle.Bold } : GUI.skin.label;
|
GUILayout.Label(fieldName + ":", labelStyle, GUILayout.Width(130));
|
EditorGUILayout.SelectableLabel(value, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
|
|
EditorGUILayout.EndHorizontal();
|
|
// 如果展开,显示内容
|
if (detail.isExpanded)
|
{
|
EditorGUI.indentLevel++;
|
|
if (detail.isArray && detail.arrayValue != null)
|
{
|
DrawArrayElements(detail.arrayValue, detail);
|
}
|
else if (detail.isList && detail.listValue != null)
|
{
|
DrawListElements(detail.listValue, detail);
|
}
|
else if (isComplexObject)
|
{
|
// 展开复杂对象的字段
|
DrawObjectFields(detail.value, detail.subFieldDetails);
|
}
|
|
EditorGUI.indentLevel--;
|
}
|
}
|
else
|
{
|
// 简单字段也添加高亮
|
if (isMatched)
|
{
|
Color originalBgColor = GUI.backgroundColor;
|
GUI.backgroundColor = new Color(1f, 1f, 0.5f);
|
EditorGUILayout.BeginHorizontal(GUI.skin.box);
|
GUI.backgroundColor = originalBgColor;
|
|
GUIStyle labelStyle = new GUIStyle(GUI.skin.label) { fontStyle = FontStyle.Bold };
|
GUILayout.Label(fieldName + ":", labelStyle, GUILayout.Width(150));
|
EditorGUILayout.SelectableLabel(value, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
|
|
EditorGUILayout.EndHorizontal();
|
}
|
else
|
{
|
DrawDetailField(fieldName, value);
|
}
|
}
|
}
|
|
private void DrawArrayElements(System.Array array, FieldDetail parentDetail)
|
{
|
if (array == null)
|
{
|
EditorGUILayout.LabelField("Array is null");
|
return;
|
}
|
|
// 显示数组元素数量
|
EditorGUILayout.LabelField($"总共 {array.Length} 个元素:", EditorStyles.miniLabel);
|
|
for (int i = 0; i < array.Length; i++)
|
{
|
var element = array.GetValue(i);
|
DrawArrayElement(i, element, parentDetail);
|
}
|
}
|
|
private void DrawListElements(System.Collections.IList list, FieldDetail parentDetail)
|
{
|
if (list == null)
|
{
|
EditorGUILayout.LabelField("List is null");
|
return;
|
}
|
|
// 显示列表元素数量
|
EditorGUILayout.LabelField($"总共 {list.Count} 个元素:", EditorStyles.miniLabel);
|
|
for (int i = 0; i < list.Count; i++)
|
{
|
var element = list[i];
|
DrawArrayElement(i, element, parentDetail);
|
}
|
}
|
|
private void DrawArrayElement(int index, object element, FieldDetail parentDetail)
|
{
|
if (element == null)
|
{
|
EditorGUILayout.LabelField($"[{index}]", "null");
|
return;
|
}
|
|
var type = element.GetType();
|
|
// 检查元素是否匹配搜索
|
bool isElementMatched = false;
|
if (!string.IsNullOrEmpty(detailSearchText))
|
{
|
HashSet<object> visited = new HashSet<object>();
|
isElementMatched = RecursiveSearchInObject(element, detailSearchText.ToLower(), visited);
|
}
|
|
// 检查是否是 GameNetPackBasic 类型,并判断是否处于执行状态
|
bool isPackageElement = element is GameNetPackBasic;
|
bool isHighlightedPackage = false;
|
bool isExecutingPackage = false;
|
bool isExecutedPackage = false;
|
|
if (isPackageElement)
|
{
|
GameNetPackBasic pack = element as GameNetPackBasic;
|
ulong packUID = pack.packUID;
|
|
// 检查高亮状态
|
if (highlightedPackage != null && highlightedPackage.packUID == packUID)
|
{
|
double elapsed = EditorApplication.timeSinceStartup - highlightStartTime;
|
isHighlightedPackage = elapsed < highlightDuration;
|
}
|
|
// 检查执行状态
|
isExecutingPackage = currentExecutingPackUID.ContainsKey(selectedGuid) &&
|
currentExecutingPackUID[selectedGuid] == packUID;
|
isExecutedPackage = executedPackUIDs.ContainsKey(selectedGuid) &&
|
executedPackUIDs[selectedGuid].Contains(packUID);
|
}
|
|
// 如果是基本类型,直接显示
|
if (type.IsPrimitive || type == typeof(string) || type.IsEnum)
|
{
|
// 如果匹配,添加高亮
|
if (isElementMatched)
|
{
|
Color originalBgColor = GUI.backgroundColor;
|
GUI.backgroundColor = new Color(1f, 1f, 0.5f);
|
EditorGUILayout.BeginHorizontal(GUI.skin.box);
|
GUI.backgroundColor = originalBgColor;
|
|
GUIStyle labelStyle = new GUIStyle(EditorStyles.label) { fontStyle = FontStyle.Bold };
|
EditorGUILayout.PrefixLabel($"[{index}]", labelStyle, labelStyle);
|
EditorGUILayout.SelectableLabel(element.ToString(), EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
|
|
EditorGUILayout.EndHorizontal();
|
}
|
else
|
{
|
EditorGUILayout.BeginHorizontal();
|
EditorGUILayout.PrefixLabel($"[{index}]");
|
EditorGUILayout.SelectableLabel(element.ToString(), EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
|
EditorGUILayout.EndHorizontal();
|
}
|
}
|
else
|
{
|
// 复杂类型,需要展开
|
// 确保有ElementDetail
|
if (parentDetail != null && !parentDetail.elementDetails.ContainsKey(index))
|
{
|
parentDetail.elementDetails[index] = new ElementDetail();
|
}
|
|
var elemDetail = parentDetail?.elementDetails[index];
|
|
// 如果元素匹配搜索,自动展开
|
if (isElementMatched && elemDetail != null)
|
{
|
elemDetail.isExpanded = true;
|
}
|
|
// 为包元素设置背景颜色
|
Color originalBgColor = GUI.backgroundColor;
|
if (isPackageElement)
|
{
|
if (isHighlightedPackage)
|
{
|
// 高亮状态 - 使用绿色闪烁效果
|
float pulse = Mathf.PingPong((float)EditorApplication.timeSinceStartup * 2f, 1f);
|
GUI.backgroundColor = Color.Lerp(new Color(0.3f, 0.8f, 0.3f), new Color(0.5f, 1f, 0.5f), pulse);
|
}
|
else if (isExecutingPackage)
|
{
|
GUI.backgroundColor = new Color(0.3f, 0.3f, 0.3f); // 深黑色(正在执行)
|
}
|
else if (isExecutedPackage)
|
{
|
GUI.backgroundColor = new Color(0.6f, 0.6f, 0.6f); // 灰色(已执行)
|
}
|
else if (isElementMatched)
|
{
|
GUI.backgroundColor = new Color(1f, 1f, 0.5f); // 黄色(搜索匹配)
|
}
|
}
|
else if (isElementMatched)
|
{
|
GUI.backgroundColor = new Color(1f, 1f, 0.5f); // 黄色(搜索匹配)
|
}
|
|
EditorGUILayout.BeginVertical(isPackageElement || isElementMatched ? GUI.skin.box : GUIStyle.none);
|
GUI.backgroundColor = originalBgColor;
|
|
// 展开按钮 + 类型名
|
GUIStyle labelStyle = EditorStyles.foldout;
|
if (isPackageElement)
|
{
|
if (isHighlightedPackage)
|
{
|
labelStyle = new GUIStyle(EditorStyles.foldout) { normal = { textColor = Color.green }, fontStyle = FontStyle.Bold };
|
}
|
else if (isExecutingPackage)
|
{
|
labelStyle = new GUIStyle(EditorStyles.foldout) { normal = { textColor = Color.yellow }, fontStyle = FontStyle.Bold };
|
}
|
else if (isExecutedPackage)
|
{
|
labelStyle = new GUIStyle(EditorStyles.foldout) { normal = { textColor = Color.gray } };
|
}
|
else if (isElementMatched)
|
{
|
labelStyle = new GUIStyle(EditorStyles.foldout) { fontStyle = FontStyle.Bold };
|
}
|
}
|
else if (isElementMatched)
|
{
|
labelStyle = new GUIStyle(EditorStyles.foldout) { fontStyle = FontStyle.Bold };
|
}
|
|
if (elemDetail != null)
|
{
|
GameNetPackBasic pack = element as GameNetPackBasic;
|
string displayText = isPackageElement ? $"[{index}] [{pack.packUID}] {type.Name}" : $"[{index}] {type.Name}";
|
|
// 特殊处理:HB427 技能包显示释放者和技能名
|
if (element is HB427_tagSCUseSkill skillPack)
|
{
|
string casterName = GetObjectName(skillPack.ObjID);
|
string skillName = GetSkillName(skillPack.SkillID);
|
displayText += $" - {casterName} 使用 {skillName}";
|
}
|
// 特殊处理:CustomHB426CombinePack 显示是否需要等待
|
else if (element is CustomHB426CombinePack combinePack)
|
{
|
bool needWaiting = combinePack.NeedWaiting();
|
displayText += needWaiting ? " [需要等待]" : " [无需等待]";
|
}
|
|
elemDetail.isExpanded = EditorGUILayout.Foldout(elemDetail.isExpanded, displayText, true, labelStyle);
|
}
|
else
|
{
|
EditorGUILayout.LabelField($"[{index}] {type.Name}");
|
}
|
|
// 如果展开,显示字段
|
if (elemDetail != null && elemDetail.isExpanded)
|
{
|
EditorGUI.indentLevel++;
|
DrawObjectFields(element, elemDetail.fieldDetails);
|
EditorGUI.indentLevel--;
|
}
|
|
if (isPackageElement || isElementMatched)
|
{
|
EditorGUILayout.EndVertical();
|
}
|
}
|
}
|
|
// 绘制对象的字段(支持递归展开)
|
private void DrawObjectFields(object obj, Dictionary<string, FieldDetail> fieldDetailsDict)
|
{
|
if (obj == null) return;
|
|
var type = obj.GetType();
|
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
|
|
foreach (var field in fields)
|
{
|
try
|
{
|
var value = field.GetValue(obj);
|
|
// 确保有FieldDetail
|
if (!fieldDetailsDict.ContainsKey(field.Name))
|
{
|
var detail = new FieldDetail { value = value };
|
|
if (value != null && value.GetType().IsArray)
|
{
|
detail.isArray = true;
|
detail.arrayValue = value as System.Array;
|
}
|
else if (value is System.Collections.IList list && !(value is string))
|
{
|
detail.isList = true;
|
detail.listValue = list;
|
}
|
|
fieldDetailsDict[field.Name] = detail;
|
}
|
|
var fieldDetail = fieldDetailsDict[field.Name];
|
|
// 绘制字段
|
DrawFieldWithRecursiveExpand(field.Name, value, fieldDetail);
|
}
|
catch
|
{
|
EditorGUILayout.LabelField(field.Name, "读取失败");
|
}
|
}
|
}
|
|
// 递归地绘制字段(支持多层展开)
|
private void DrawFieldWithRecursiveExpand(string fieldName, object value, FieldDetail fieldDetail)
|
{
|
if (value == null)
|
{
|
EditorGUILayout.LabelField(fieldName + ":", "null");
|
return;
|
}
|
|
var type = value.GetType();
|
|
// 检查字段值是否匹配搜索
|
bool isValueMatched = false;
|
if (!string.IsNullOrEmpty(detailSearchText))
|
{
|
string searchLower = detailSearchText.ToLower();
|
// 字段名匹配
|
if (fieldName.ToLower().Contains(searchLower))
|
isValueMatched = true;
|
// 字段值匹配
|
else if (value.ToString().Contains(searchLower))
|
isValueMatched = true;
|
}
|
|
// 基本类型直接显示
|
if (type.IsPrimitive || type == typeof(string) || type.IsEnum)
|
{
|
// 获取显示值,如果是ObjID则追加对象名称
|
string displayValue = value.ToString();
|
if (fieldName == "ObjID" && uint.TryParse(displayValue, out uint objID))
|
{
|
string objName = GetObjectName(objID);
|
displayValue = $"{displayValue} ({objName})";
|
}
|
|
if (isValueMatched)
|
{
|
// 高亮显示匹配的字段
|
Color originalBgColor = GUI.backgroundColor;
|
GUI.backgroundColor = new Color(1f, 1f, 0.5f); // 淡黄色背景
|
EditorGUILayout.BeginHorizontal(GUI.skin.box);
|
GUI.backgroundColor = originalBgColor;
|
|
GUIStyle labelStyle = new GUIStyle(GUI.skin.label) { fontStyle = FontStyle.Bold };
|
GUILayout.Space(EditorGUI.indentLevel * 15); // 手动缩进
|
GUILayout.Label(fieldName + ":", labelStyle, GUILayout.Width(120));
|
EditorGUILayout.SelectableLabel(displayValue, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
|
|
EditorGUILayout.EndHorizontal();
|
}
|
else
|
{
|
EditorGUILayout.BeginHorizontal();
|
GUILayout.Space(EditorGUI.indentLevel * 15); // 手动缩进
|
GUILayout.Label(fieldName + ":", GUILayout.Width(120));
|
EditorGUILayout.SelectableLabel(displayValue, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
|
EditorGUILayout.EndHorizontal();
|
}
|
}
|
// 数组类型
|
else if (type.IsArray && fieldDetail.arrayValue != null)
|
{
|
fieldDetail.isExpanded = EditorGUILayout.Foldout(fieldDetail.isExpanded, $"{fieldName}: Array[{fieldDetail.arrayValue.Length}]", true);
|
|
if (fieldDetail.isExpanded)
|
{
|
EditorGUI.indentLevel++;
|
for (int i = 0; i < fieldDetail.arrayValue.Length; i++)
|
{
|
var element = fieldDetail.arrayValue.GetValue(i);
|
DrawArrayElement(i, element, fieldDetail);
|
}
|
EditorGUI.indentLevel--;
|
}
|
}
|
// 列表类型
|
else if (fieldDetail.isList && fieldDetail.listValue != null)
|
{
|
fieldDetail.isExpanded = EditorGUILayout.Foldout(fieldDetail.isExpanded, $"{fieldName}: List[{fieldDetail.listValue.Count}]", true);
|
|
if (fieldDetail.isExpanded)
|
{
|
EditorGUI.indentLevel++;
|
for (int i = 0; i < fieldDetail.listValue.Count; i++)
|
{
|
var element = fieldDetail.listValue[i];
|
DrawArrayElement(i, element, fieldDetail);
|
}
|
EditorGUI.indentLevel--;
|
}
|
}
|
// 其他复杂类型
|
else
|
{
|
EditorGUILayout.BeginHorizontal();
|
EditorGUILayout.PrefixLabel(fieldName);
|
EditorGUILayout.SelectableLabel(value.ToString(), EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
|
EditorGUILayout.EndHorizontal();
|
}
|
}
|
|
#endregion
|
|
#region 详情搜索
|
|
/// <summary>
|
/// 更新详情搜索缓存(仅在搜索文本改变时调用)
|
/// </summary>
|
private void UpdateDetailSearchCache()
|
{
|
matchedFieldNames.Clear();
|
fieldHasMatchedChild.Clear();
|
|
if (selectedPackage == null || string.IsNullOrEmpty(detailSearchText))
|
return;
|
|
string searchLower = detailSearchText.ToLower();
|
|
// 检查基本信息是否匹配(包类型、UID、Cmd)
|
bool basicInfoMatched = selectedPackage.typeName.ToLower().Contains(searchLower) ||
|
selectedPackage.packUID.ToString().Contains(searchLower) ||
|
selectedPackage.package.cmd.ToString().Contains(searchLower);
|
|
foreach (var field in selectedPackage.fields)
|
{
|
string fieldName = field.Key;
|
string fieldValue = field.Value;
|
|
// 检查字段名或字段值是否匹配
|
bool fieldMatched = fieldName.ToLower().Contains(searchLower) ||
|
fieldValue.ToLower().Contains(searchLower);
|
|
if (fieldMatched)
|
{
|
matchedFieldNames.Add(fieldName);
|
}
|
|
// 检查数组/列表的子元素是否匹配
|
if (selectedPackage.fieldDetails.ContainsKey(fieldName))
|
{
|
var detail = selectedPackage.fieldDetails[fieldName];
|
if (detail.isArray || detail.isList)
|
{
|
bool hasMatch = CheckChildElementsMatch(detail, searchLower);
|
if (hasMatch)
|
{
|
fieldHasMatchedChild[fieldName] = true;
|
if (!fieldMatched)
|
{
|
matchedFieldNames.Add(fieldName); // 也算作匹配字段
|
}
|
}
|
}
|
}
|
}
|
|
// 如果基本信息匹配但没有字段匹配,至少显示一个字段让用户知道有匹配
|
if (basicInfoMatched && matchedFieldNames.Count == 0 && selectedPackage.fields.Count > 0)
|
{
|
// 添加第一个字段,这样至少会显示一些内容
|
var firstField = selectedPackage.fields.First();
|
matchedFieldNames.Add(firstField.Key);
|
}
|
}
|
|
/// <summary>
|
/// 检查字段是否匹配搜索(用于高亮显示)
|
/// </summary>
|
private bool IsFieldMatched(string fieldName, string fieldValue, FieldDetail detail)
|
{
|
if (string.IsNullOrEmpty(detailSearchText))
|
return false;
|
|
string searchLower = detailSearchText.ToLower();
|
|
// 字段名匹配
|
if (fieldName.ToLower().Contains(searchLower))
|
return true;
|
|
// 字段值匹配
|
if (fieldValue.ToLower().Contains(searchLower))
|
return true;
|
|
return false;
|
}
|
|
/// <summary>
|
/// 检查数组/列表中是否有匹配的子元素(用于缓存计算)
|
/// </summary>
|
private bool CheckChildElementsMatch(FieldDetail detail, string searchLower)
|
{
|
HashSet<object> visited = new HashSet<object>();
|
|
// 检查数组
|
if (detail.isArray && detail.arrayValue != null)
|
{
|
foreach (var element in detail.arrayValue)
|
{
|
if (element != null)
|
{
|
// 特殊处理:如果是 GameNetPackBasic,优先检查 packUID
|
if (element is GameNetPackBasic pack && pack.packUID.ToString().Contains(searchLower))
|
return true;
|
|
if (RecursiveSearchInObject(element, searchLower, visited))
|
return true;
|
}
|
}
|
}
|
|
// 检查列表
|
if (detail.isList && detail.listValue != null)
|
{
|
foreach (var element in detail.listValue)
|
{
|
if (element != null)
|
{
|
// 特殊处理:如果是 GameNetPackBasic,优先检查 packUID
|
if (element is GameNetPackBasic pack && pack.packUID.ToString().Contains(searchLower))
|
return true;
|
|
if (RecursiveSearchInObject(element, searchLower, visited))
|
return true;
|
}
|
}
|
}
|
|
return false;
|
}
|
|
/// <summary>
|
/// 判断字段是否应该显示(根据详情搜索文本)
|
/// </summary>
|
private bool ShouldShowDetailField(string fieldName, string fieldValue, PackageInfo packInfo)
|
{
|
if (string.IsNullOrEmpty(detailSearchText))
|
return true;
|
|
string searchLower = detailSearchText.ToLower();
|
|
// 搜索字段名
|
if (fieldName.ToLower().Contains(searchLower))
|
return true;
|
|
// 搜索字段值
|
if (fieldValue.ToLower().Contains(searchLower))
|
return true;
|
|
// 如果有详细信息,递归搜索
|
if (packInfo.fieldDetails.ContainsKey(fieldName))
|
{
|
var detail = packInfo.fieldDetails[fieldName];
|
HashSet<object> visited = new HashSet<object>();
|
if (RecursiveSearchInFieldDetail(detail, searchLower, visited))
|
return true;
|
}
|
|
return false;
|
}
|
|
/// <summary>
|
/// 递归搜索字段详情
|
/// </summary>
|
private bool RecursiveSearchInFieldDetail(FieldDetail detail, string searchLower, HashSet<object> visited)
|
{
|
if (detail.value != null)
|
{
|
// 防止循环引用
|
if (!detail.value.GetType().IsPrimitive &&
|
detail.value.GetType() != typeof(string) &&
|
!detail.value.GetType().IsEnum)
|
{
|
if (visited.Contains(detail.value))
|
return false;
|
visited.Add(detail.value);
|
}
|
|
// 搜索值本身
|
if (detail.value.ToString().ToLower().Contains(searchLower))
|
return true;
|
|
// 如果是数组
|
if (detail.isArray && detail.arrayValue != null)
|
{
|
foreach (var element in detail.arrayValue)
|
{
|
if (element != null && RecursiveSearchInObject(element, searchLower, visited))
|
return true;
|
}
|
}
|
|
// 如果是列表
|
if (detail.isList && detail.listValue != null)
|
{
|
foreach (var element in detail.listValue)
|
{
|
if (element != null && RecursiveSearchInObject(element, searchLower, visited))
|
return true;
|
}
|
}
|
|
// 搜索子字段
|
foreach (var subField in detail.subFieldDetails.Values)
|
{
|
if (RecursiveSearchInFieldDetail(subField, searchLower, visited))
|
return true;
|
}
|
}
|
|
return false;
|
}
|
|
/// <summary>
|
/// 递归搜索对象内容
|
/// </summary>
|
private bool RecursiveSearchInObject(object obj, string searchLower, HashSet<object> visited)
|
{
|
if (obj == null) return false;
|
|
var type = obj.GetType();
|
|
// 特殊处理:如果是 GameNetPackBasic,优先检查 packUID
|
if (obj is GameNetPackBasic pack)
|
{
|
if (pack.packUID.ToString().Contains(searchLower))
|
return true;
|
// 还要检查类型名
|
if (pack.GetType().Name.ToLower().Contains(searchLower))
|
return true;
|
}
|
|
// 基本类型直接比较(包括所有数字类型)
|
if (type.IsPrimitive || type == typeof(string) || type.IsEnum)
|
{
|
string valueStr = obj.ToString();
|
// 对于数字,直接比较字符串(不转小写,因为数字没有大小写)
|
if (type.IsPrimitive && !type.IsEnum)
|
{
|
return valueStr.Contains(searchLower);
|
}
|
// 对于字符串和枚举,转小写比较
|
return valueStr.ToLower().Contains(searchLower);
|
}
|
|
// 防止循环引用
|
if (visited.Contains(obj))
|
return false;
|
visited.Add(obj);
|
|
// 限制递归深度,防止过深的嵌套
|
if (visited.Count > 1000)
|
return false;
|
|
// 特殊处理数组
|
if (type.IsArray)
|
{
|
var array = obj as System.Array;
|
if (array != null)
|
{
|
foreach (var element in array)
|
{
|
if (element != null && RecursiveSearchInObject(element, searchLower, visited))
|
return true;
|
}
|
}
|
return false;
|
}
|
|
// 特殊处理列表
|
if (obj is System.Collections.IList list && !(obj is string))
|
{
|
foreach (var element in list)
|
{
|
if (element != null && RecursiveSearchInObject(element, searchLower, visited))
|
return true;
|
}
|
return false;
|
}
|
|
// 复杂对象,搜索所有字段
|
var fields = type.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
|
foreach (var field in fields)
|
{
|
try
|
{
|
// 搜索字段名
|
if (field.Name.ToLower().Contains(searchLower))
|
return true;
|
|
var value = field.GetValue(obj);
|
if (value != null && RecursiveSearchInObject(value, searchLower, visited))
|
return true;
|
}
|
catch { }
|
}
|
|
return false;
|
}
|
|
#endregion
|
|
#region 辅助方法
|
|
/// <summary>
|
/// 根据对象ID获取对象名称
|
/// </summary>
|
private string GetObjectName(uint objID)
|
{
|
try
|
{
|
var battleField = BattleManager.Instance?.GetBattleField(selectedGuid);
|
if (battleField != null && battleField.battleObjMgr != null)
|
{
|
var battleObj = battleField.battleObjMgr.GetBattleObject((int)objID);
|
if (battleObj is HeroBattleObject heroBattleObject && heroBattleObject.teamHero != null)
|
{
|
return heroBattleObject.teamHero.name;
|
}
|
}
|
}
|
catch { }
|
|
return $"对象{objID}";
|
}
|
|
/// <summary>
|
/// 根据技能ID获取技能名称
|
/// </summary>
|
private string GetSkillName(uint skillID)
|
{
|
try
|
{
|
var skillConfig = SkillConfig.Get((int)skillID);
|
if (skillConfig != null && !string.IsNullOrEmpty(skillConfig.SkillName))
|
{
|
return skillConfig.SkillName;
|
}
|
}
|
catch { }
|
|
return $"技能{skillID}";
|
}
|
|
#endregion
|
|
#region 高亮和滚动
|
|
/// <summary>
|
/// 开始高亮一个包
|
/// </summary>
|
private void StartHighlight(PackageInfo package)
|
{
|
highlightedPackage = package;
|
highlightStartTime = EditorApplication.timeSinceStartup;
|
}
|
|
/// <summary>
|
/// 判断包是否处于高亮状态
|
/// </summary>
|
private bool IsPackageHighlighted(PackageInfo package)
|
{
|
if (highlightedPackage == null || highlightedPackage != package)
|
return false;
|
|
double elapsed = EditorApplication.timeSinceStartup - highlightStartTime;
|
return elapsed < highlightDuration;
|
}
|
|
/// <summary>
|
/// 滚动到选中的包位置
|
/// </summary>
|
private void ScrollToSelectedPackage()
|
{
|
if (selectedPackageIndex < 0 || filteredPackages.Count == 0)
|
return;
|
|
// 计算目标滚动位置
|
// 每个包条目的大概高度(可根据实际情况调整)
|
float itemHeight = 60f;
|
float targetY = selectedPackageIndex * itemHeight;
|
|
// 滚动到目标位置
|
scrollPosition.y = targetY;
|
|
Repaint();
|
}
|
|
#endregion
|
|
#region 数据处理
|
|
private void RefreshGuidList()
|
{
|
guidList = allBattlePackages.Keys.ToList();
|
|
if (guidList.Count > 0)
|
{
|
if (string.IsNullOrEmpty(selectedGuid) || !guidList.Contains(selectedGuid))
|
{
|
selectedGuidIndex = 0;
|
selectedGuid = guidList[0];
|
}
|
else
|
{
|
selectedGuidIndex = guidList.IndexOf(selectedGuid);
|
}
|
|
FilterPackages();
|
}
|
else
|
{
|
selectedGuid = "";
|
selectedGuidIndex = 0;
|
filteredPackages.Clear();
|
}
|
}
|
|
private void FilterPackages()
|
{
|
if (string.IsNullOrEmpty(selectedGuid) || !allBattlePackages.ContainsKey(selectedGuid))
|
{
|
filteredPackages.Clear();
|
return;
|
}
|
|
var allPackages = allBattlePackages[selectedGuid];
|
|
if (string.IsNullOrEmpty(searchText))
|
{
|
filteredPackages = new List<PackageInfo>(allPackages);
|
}
|
else
|
{
|
string searchLower = searchText.ToLower();
|
|
filteredPackages = allPackages.Where(p =>
|
{
|
// 搜索包类型名
|
if (p.typeName.ToLower().Contains(searchLower))
|
return true;
|
|
// 搜索 UID
|
if (p.packUID.ToString().Contains(searchLower))
|
return true;
|
|
// 递归搜索所有字段(包括嵌套内容)
|
if (RecursiveSearchInPackage(p.package, searchLower))
|
return true;
|
|
return false;
|
}).ToList();
|
}
|
|
// 按照 packUID 从小到大排序
|
filteredPackages.Sort((a, b) => a.packUID.CompareTo(b.packUID));
|
|
Repaint();
|
}
|
|
/// <summary>
|
/// 递归搜索对象的所有字段(包括嵌套数组、列表等)
|
/// </summary>
|
private bool RecursiveSearchInPackage(object obj, string searchLower)
|
{
|
if (obj == null) return false;
|
|
var type = obj.GetType();
|
|
// 基本类型直接比较
|
if (type.IsPrimitive || type == typeof(string) || type.IsEnum)
|
{
|
return obj.ToString().ToLower().Contains(searchLower);
|
}
|
|
// 数组类型
|
if (obj is System.Array array)
|
{
|
foreach (var element in array)
|
{
|
if (RecursiveSearchInPackage(element, searchLower))
|
return true;
|
}
|
return false;
|
}
|
|
// 列表类型
|
if (obj is System.Collections.IList list)
|
{
|
foreach (var element in list)
|
{
|
if (RecursiveSearchInPackage(element, searchLower))
|
return true;
|
}
|
return false;
|
}
|
|
// 复杂对象,递归搜索所有字段
|
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
|
foreach (var field in fields)
|
{
|
try
|
{
|
// 搜索字段名
|
if (field.Name.ToLower().Contains(searchLower))
|
return true;
|
|
var value = field.GetValue(obj);
|
if (value == null) continue;
|
|
// 递归搜索字段值
|
if (RecursiveSearchInPackage(value, searchLower))
|
return true;
|
}
|
catch
|
{
|
// 忽略读取失败的字段
|
}
|
}
|
|
return false;
|
}
|
|
#endregion
|
}
|