三国卡牌客户端基础资源仓库
yyl
2026-01-27 2f18d17e7218262dc7599c00bf552556f8247539
战报查看器
1个文件已修改
740 ■■■■■ 已修改文件
Assets/Editor/ScriptEditor/BattleReportChecker.cs 740 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/ScriptEditor/BattleReportChecker.cs
@@ -195,11 +195,24 @@
    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
    
@@ -250,6 +263,12 @@
        
        if (GUILayout.Button("刷新", EditorStyles.toolbarButton, GUILayout.Width(60)))
        {
            // 刷新前清空搜索缓存,避免卡死
            matchedFieldNames.Clear();
            fieldHasMatchedChild.Clear();
            lastSelectedPackage = null;
            lastDetailSearchText = "";
            RefreshGuidList();
            FilterPackages();
        }
@@ -307,6 +326,15 @@
        {
            searchText = newSearchText;
            FilterPackages();
            // 搜索后自动选中第一个结果并高亮
            if (filteredPackages.Count > 0)
            {
                selectedPackage = filteredPackages[0];
                selectedPackageIndex = 0;
                StartHighlight(selectedPackage);
                ScrollToSelectedPackage();
            }
        }
        
        if (GUILayout.Button("清空", GUILayout.Width(60)))
@@ -353,9 +381,18 @@
        bool isExecuted = executedPackUIDs.ContainsKey(selectedGuid) && 
                         executedPackUIDs[selectedGuid].Contains(packInfo.packUID);
        
        // 判断是否是高亮状态
        bool isHighlighted = IsPackageHighlighted(packInfo);
        // 设置背景颜色
        Color originalBgColor = GUI.backgroundColor;
        if (isExecuting)
        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); // 深黑色(正在执行)
        }
@@ -406,6 +443,7 @@
        if (GUILayout.Button(packInfo.displayName, style))
        {
            selectedPackage = packInfo;
            selectedPackageIndex = filteredPackages.IndexOf(packInfo);
        }
        
        EditorGUILayout.EndHorizontal();
@@ -452,23 +490,64 @@
        }
        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);
            
            // 基本信息
            DrawDetailField("包类型", selectedPackage.typeName);
            DrawDetailField("UID", selectedPackage.packUID.ToString());
            DrawDetailField("Cmd", selectedPackage.package.cmd.ToString());
            // 基本信息 - 支持搜索高亮
            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)
            {
                DrawDetailFieldWithExpand(field.Key, field.Value, selectedPackage);
                if (ShouldShowDetailField(field.Key, field.Value, selectedPackage))
                {
                    DrawDetailFieldWithExpand(field.Key, field.Value, selectedPackage);
                }
            }
            
            EditorGUILayout.EndScrollView();
@@ -488,6 +567,27 @@
        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))
@@ -497,6 +597,22 @@
        }
        
        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 && 
@@ -509,7 +625,15 @@
        // 如果是数组、列表或复杂对象,显示展开按钮
        if (detail.isArray || detail.isList || isComplexObject)
        {
            EditorGUILayout.BeginHorizontal();
            // 如果匹配或有匹配的子元素,添加背景颜色
            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 ? "▼" : "▶";
@@ -518,7 +642,9 @@
                detail.isExpanded = !detail.isExpanded;
            }
            
            GUILayout.Label(fieldName + ":", GUILayout.Width(130));
            // 字段名样式
            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();
@@ -547,7 +673,24 @@
        }
        else
        {
            DrawDetailField(fieldName, value);
            // 简单字段也添加高亮
            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);
            }
        }
    }
    
@@ -597,13 +740,63 @@
        
        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)
        {
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.PrefixLabel($"[{index}]");
            EditorGUILayout.SelectableLabel(element.ToString(), EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
            EditorGUILayout.EndHorizontal();
            // 如果匹配,添加高亮
            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
        {
@@ -616,11 +809,89 @@
            
            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)
            {
                string foldoutLabel = elemDetail.isExpanded ? "▼" : "▶";
                elemDetail.isExpanded = EditorGUILayout.Foldout(elemDetail.isExpanded, $"[{index}] {type.Name}", true);
                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
            {
@@ -633,6 +904,11 @@
                EditorGUI.indentLevel++;
                DrawObjectFields(element, elemDetail.fieldDetails);
                EditorGUI.indentLevel--;
            }
            if (isPackageElement || isElementMatched)
            {
                EditorGUILayout.EndVertical();
            }
        }
    }
@@ -693,13 +969,53 @@
        
        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)
        {
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.PrefixLabel(fieldName);
            EditorGUILayout.SelectableLabel(value.ToString(), EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
            EditorGUILayout.EndHorizontal();
            // 获取显示值,如果是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)
@@ -745,6 +1061,392 @@
    
    #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 != null && battleObj.teamHero != null)
                {
                    return battleObj.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()