yyl
2 天以前 a52a462c4f66ee379c79d8c55916feed58240c2a
Merge branch 'master' of http://192.168.1.20:10010/r/Project_SG_scripts

# Conflicts:
# Main/System/Battle/BattleObject/BattleObject.cs
20个文件已添加
30个文件已修改
2000 ■■■■■ 已修改文件
Main/Common/EventName.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/ConfigManager.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/FamilyConfig.cs 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/FamilyConfig.cs 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/FamilyConfig.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Attribute/AttributeManager.cs 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Attribute/SimpleAttributeAttrCell.cs 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Attribute/SimpleAttributeAttrCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Attribute/SimpleAttributeAttrItem.cs 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Attribute/SimpleAttributeAttrItem.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Attribute/SimpleAttributeWin.cs 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Attribute/SimpleAttributeWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Attribute/SimpleMinggeAttributeCell.cs 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Attribute/SimpleMinggeAttributeCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Attribute/SimpleMinggeAttributeWin.cs 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Attribute/SimpleMinggeAttributeWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleConst.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleManager.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/BeautyMM/BeautyMMListWin.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/FuncPreset/FuncPresetChooseCell.cs 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/FuncPreset/FuncPresetChooseCells.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/FuncPreset/FuncPresetChooseMoreCell.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/FuncPreset/FuncPresetManager.cs 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Gubao/GubaoListWin.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Guild/GuildHallWin.cs 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Guild/GuildLevelItem.cs 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Guild/GuildLevelItem.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Guild/GuildLevelWin.cs 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Guild/GuildLevelWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Horse/HorseSkinWin.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Mingge/MinggeEquipChangeCell.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Mingge/MinggeEquipWin.cs 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Mingge/MinggeManager.cs 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Mingge/MinggeSmallTipWin.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Mingge/MinggeWin.cs 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/OtherPlayerDetail/OtherHeroDetailWin.cs 286 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/OtherPlayerDetail/OtherHeroFightingCardItem.cs 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/OtherPlayerDetail/OtherNPCDetailWin.cs 326 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/OtherPlayerDetail/OtherNPCDetailWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/OtherPlayerDetail/OtherNpcHeroCell.cs 210 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/OtherPlayerDetail/OtherNpcHeroCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/OtherPlayerDetail/OtherPlayerDetailManager.cs 219 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/OtherPlayerDetail/OtherPlayerDetailWin.cs 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/PhantasmPavilion/PhantasmPavilionFaceWin.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/PhantasmPavilion/PhantasmPavilionModelWin.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/PhantasmPavilion/PhantasmPavilionTilteWin.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/ViewNPC/ViewNPCManager.cs 172 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/WarlordPavilion/WarlordPavilionManager.cs 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/WarlordPavilion/WarlordPavilionWin.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Utility/EnumHelper.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/EventName.cs
@@ -18,4 +18,6 @@
    public const string RECORDPLAYER_END = "RECORDPLAYER_END"; //战斗小片段结束
    public const string BATTLE_TIANZI_REFRESH_HP = "BATTLE_TIANZI_REFRESH_HP";//天子考验 刷新血量
    public const string BATTLE_CLICK_HERO = "BATTLE_CLICK_HERO";//点击英雄
}
Main/Config/ConfigManager.cs
@@ -91,7 +91,6 @@
            typeof(PlayerAttrConfig),
            typeof(PlayerFaceConfig),
            typeof(PresetUnlockConfig),
            typeof(PriorBundleConfig),
            typeof(RandomNameConfig),
            typeof(SignInConfig),
            typeof(StoreConfig),
@@ -362,8 +361,6 @@
        ClearConfigDictionary<PlayerFaceConfig>();
        // 清空 PresetUnlockConfig 字典
        ClearConfigDictionary<PresetUnlockConfig>();
        // 清空 PriorBundleConfig 字典
        ClearConfigDictionary<PriorBundleConfig>();
        // 清空 RandomNameConfig 字典
        ClearConfigDictionary<RandomNameConfig>();
        // 清空 SignInConfig 字典
Main/Config/Configs/FamilyConfig.cs
@@ -1,6 +1,6 @@
//--------------------------------------------------------
//    [Author]:           YYL
//    [  Date ]:           2025年8月5日
//    [  Date ]:           2026年1月28日
//--------------------------------------------------------
using System.Collections.Generic;
@@ -21,6 +21,7 @@
    public int DeputyLeaderMax;
    public int EliteMax;
    public int NeedExp;
    public string FuncText;
    public override int LoadKey(string _key)
    {
@@ -41,6 +42,8 @@
            int.TryParse(tables[3],out EliteMax); 
            int.TryParse(tables[4],out NeedExp); 
            FuncText = tables[5];
        }
        catch (Exception exception)
        {
Main/Config/PartialConfigs/FamilyConfig.cs
New file
@@ -0,0 +1,42 @@
using System.Collections.Generic;
using System.Linq;
public partial class FamilyConfig : ConfigBase<int, FamilyConfig>
{
    //战令类型: 所需值 :ZhanlingId
    static Dictionary<int, Dictionary<int, int>> typeToIDDict = new Dictionary<int, Dictionary<int, int>>();
    protected override void OnConfigParseCompleted()
    {
    }
    public static bool TryGetFamilyConfig(int familyLV, out FamilyConfig familyConfig)
    {
        familyConfig = null;
        if (!HasKey(familyLV))
            return false;
        familyConfig = Get(familyLV);
        return true;
    }
    public static bool TryGetNextLvConfig(int familyLV, out FamilyConfig nowConfig, out FamilyConfig nextConfig)
    {
        nextConfig = null;
        if (!TryGetFamilyConfig(familyLV, out nowConfig))
            return false;
        int nextFamilyLV = familyLV + 1;
        if (!TryGetFamilyConfig(nextFamilyLV, out nextConfig))
            return false;
        return true;
    }
    public static bool TryGetMaxFamilyLVConfig(out FamilyConfig maxConfig)
    {
        maxConfig = null;
        List<int> keys = GetKeys();
        if (keys == null || keys.Count == 0)
            return false;
        int maxLV = keys.Max();
        return TryGetFamilyConfig(maxLV, out maxConfig);
    }
}
Main/Config/PartialConfigs/FamilyConfig.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dc8a70ad1c9a7ed488fe7326ef097ce2
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/Attribute/AttributeManager.cs
@@ -11,4 +11,113 @@
            UIManager.Instance.OpenWindow<TotalAttributeWin>(functionOrder);
        }
    }
    public readonly int SimpleAttributeMaxRowCnt = 2;
    public Dictionary<int, long> simpleAttrDict = new Dictionary<int, long>();
    public List<int> simpleAttrSortList = new List<int>();
    public string simpleAttrTitleName = string.Empty;
    public void OpenSimpleAttributeWin(Dictionary<int, long> simpleAttrs, string titleNameKey = "")
    {
        simpleAttrDict = FilterValidAttributes(simpleAttrs);
        simpleAttrSortList = SortAttrId(simpleAttrDict);
        simpleAttrTitleName = Language.Get(string.IsNullOrEmpty(titleNameKey) ? "AttributeTitle00" : titleNameKey);
        if (!UIManager.Instance.IsOpened<SimpleAttributeWin>())
        {
            UIManager.Instance.OpenWindow<SimpleAttributeWin>();
        }
    }
    // 命格专用
    public Dictionary<int, int> skillDic = new Dictionary<int, int>();
    public List<int> skillList = new List<int>();
    public void OpenMinggeSimpleAttributeWin(Dictionary<int, long> simpleAttrs, Dictionary<int, int> skills)
    {
        if (skills.IsNullOrEmpty())
        {
            OpenSimpleAttributeWin(simpleAttrs, "AttributeTitle03");
            return;
        }
        simpleAttrDict = FilterValidAttributes(simpleAttrs);
        simpleAttrSortList = SortAttrId(simpleAttrDict);
        simpleAttrTitleName = Language.Get("AttributeTitle03");
        skillDic = skills;
        skillList = new List<int>(skillDic.Keys);
        skillList.Sort();
        if (!UIManager.Instance.IsOpened<SimpleMinggeAttributeWin>())
        {
            UIManager.Instance.OpenWindow<SimpleMinggeAttributeWin>();
        }
    }
    public bool TryGetInfoBySkillID(int skillTypeID, int lv, out string name, out string desc)
    {
        name = string.Empty;
        desc = string.Empty;
        if (!SkillConfig.HasKey(skillTypeID))
            return false;
        SkillConfig config = SkillConfig.Get(skillTypeID + lv - 1);
        name = Language.Get("OtherPlayerDetail14", Language.Get($"MinggeSkillType_{skillTypeID}"), lv);
        desc = config.Description;
        return true;
    }
    /// <summary>
    /// 过滤有效的属性:移除value<=0或showType<=0或不在配置表中的项
    /// </summary>
    private Dictionary<int, long> FilterValidAttributes(Dictionary<int, long> dict)
    {
        if (dict == null)
            return null;
        var result = new Dictionary<int, long>();
        foreach (var kvp in dict)
        {
            if (!PlayerPropertyConfig.HasKey(kvp.Key))
                continue;
            PlayerPropertyConfig config = PlayerPropertyConfig.Get(kvp.Key);
            if (kvp.Value > 0 && config.showType > 0 && config.showType < 5)
            {
                result.Add(kvp.Key, kvp.Value);
            }
        }
        return result;
    }
    /// <summary>
    /// 根据PlayerPropertyConfig的showType和showSequence对属性ID进行排序
    /// 排序规则:先按showType升序,showType相同按showSequence升序,showSequence相同按ID升序
    /// </summary>
    private List<int> SortAttrId(Dictionary<int, long> dict)
    {
        if (dict == null)
            return null;
        var list = new List<int>(dict.Keys);
        list.Sort((a, b) =>
        {
            bool hasKeyA = PlayerPropertyConfig.HasKey(a);
            bool hasKeyB = PlayerPropertyConfig.HasKey(b);
            // 如果一个在表中一个不在,将不在表的排在后面
            if (hasKeyA != hasKeyB)
                return hasKeyA.CompareTo(hasKeyB);
            var configA = PlayerPropertyConfig.Get(a);
            var configB = PlayerPropertyConfig.Get(b);
            int showTypeA = configA?.showType ?? 0;
            int showTypeB = configB?.showType ?? 0;
            if (showTypeA != showTypeB)
                return showTypeA.CompareTo(showTypeB);
            int showSequenceA = configA?.showSequence ?? 0;
            int showSequenceB = configB?.showSequence ?? 0;
            if (showSequenceA != showSequenceB)
                return showSequenceA.CompareTo(showSequenceB);
            return a.CompareTo(b);
        });
        return list;
    }
}
Main/System/Attribute/SimpleAttributeAttrCell.cs
New file
@@ -0,0 +1,26 @@
using System.Collections.Generic;
using UnityEngine;
public class SimpleAttributeAttrCell : MonoBehaviour
{
    [SerializeField] List<AttrTextItem> items;
    public void Display(int rowIndex, List<int> simpleAttrList, Dictionary<int, long> simpleAttrDict)
    {
        if (simpleAttrList.IsNullOrEmpty() || simpleAttrDict.IsNullOrEmpty())
            return;
        for (int i = 0; i < items.Count; i++)
        {
            int index = rowIndex * AttributeManager.Instance.SimpleAttributeMaxRowCnt + i;
            if (index < simpleAttrList.Count)
            {
                items[i].SetActive(true);
                items[i].Display(simpleAttrList[index], simpleAttrDict);
            }
            else
            {
                items[i].SetActive(false);
            }
        }
    }
}
Main/System/Attribute/SimpleAttributeAttrCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f0453564687973b46a145f72b051d463
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/Attribute/SimpleAttributeAttrItem.cs
New file
@@ -0,0 +1,19 @@
using System.Collections.Generic;
using UnityEngine;
public class AttrTextItem : MonoBehaviour
{
    [SerializeField] TextEx attrName;
    [SerializeField] TextEx attrValue;
    public void Display(int attrId, Dictionary<int, long> simpleAttrDict)
    {
        attrName.SetActive(false);
        attrValue.SetActive(false);
        if (simpleAttrDict == null || !simpleAttrDict.TryGetValue(attrId, out long value) || !PlayerPropertyConfig.HasKey(attrId))
            return;
        PlayerPropertyConfig config = PlayerPropertyConfig.Get(attrId);
        attrName.SetActive(true);
        attrValue.SetActive(true);
        attrName.text = config.ShowName;
        attrValue.text = StringUtility.Concat("+", PlayerPropertyConfig.GetValueDescription(attrId, value));
    }
}
Main/System/Attribute/SimpleAttributeAttrItem.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 86a66a7462e49f1488b07a61f766fcee
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/Attribute/SimpleAttributeWin.cs
New file
@@ -0,0 +1,49 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class SimpleAttributeWin : UIBase
{
    [SerializeField] Transform noAttr;
    [SerializeField] TextEx title;
    [SerializeField] ScrollerController scroller;
    Dictionary<int, long> simpleAttrDict;
    List<int> simpleAttrSortList;
    string simpleAttrTitleName;
    protected override void OnPreOpen()
    {
        simpleAttrDict = AttributeManager.Instance.simpleAttrDict;
        simpleAttrSortList = AttributeManager.Instance.simpleAttrSortList;
        simpleAttrTitleName = AttributeManager.Instance.simpleAttrTitleName;
        scroller.OnRefreshCell += OnRefreshCell;
        title.text = simpleAttrTitleName;
        CreateScroller();
    }
    protected override void OnPreClose()
    {
        scroller.OnRefreshCell -= OnRefreshCell;
    }
    private void OnRefreshCell(ScrollerDataType type, CellView cell)
    {
        var _cell = cell.GetComponent<SimpleAttributeAttrCell>();
        _cell?.Display(cell.index, simpleAttrSortList, simpleAttrDict);
    }
    private void CreateScroller()
    {
        scroller.Refresh();
        noAttr.SetActive(simpleAttrSortList.IsNullOrEmpty());
        if (simpleAttrSortList != null)
        {
            int rowCount = (int)Mathf.Ceil((float)simpleAttrSortList.Count / AttributeManager.Instance.SimpleAttributeMaxRowCnt);
            for (int i = 0; i < rowCount; i++)
            {
                scroller.AddCell(ScrollerDataType.Header, i);
            }
        }
        scroller.Restart();
    }
}
Main/System/Attribute/SimpleAttributeWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0179d1698f9b9d54c884f5d49a04f45a
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/Attribute/SimpleMinggeAttributeCell.cs
New file
@@ -0,0 +1,30 @@
using System.Collections.Generic;
using UnityEngine;
public class SimpleMinggeAttributeCell : MonoBehaviour
{
    [SerializeField] TextEx nameText;
    [SerializeField] TextEx descText;
    AttributeManager manager { get { return AttributeManager.Instance; } }
    public void Display(int index, List<int> skillList, Dictionary<int, int> skillDic)
    {
        if (skillList.IsNullOrEmpty() || skillDic.IsNullOrEmpty() || index < 0 || index >= skillList.Count)
            return;
        int skillId = skillList[index];
        if (!skillList.Contains(skillId))
            return;
        int lv = skillDic[skillId];
        if (!manager.TryGetInfoBySkillID(skillId, lv, out string name, out string desc))
            return;
        nameText.text = name;
        descText.text = desc;
    }
    public float GetHeight(string content)
    {
        var height = 0f;
        if (string.IsNullOrEmpty(content))
            return height;
        descText.text = content;
        return descText.preferredHeight + 5;
    }
}
Main/System/Attribute/SimpleMinggeAttributeCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a1aad0616b358bd4f8b709018b2d5540
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/Attribute/SimpleMinggeAttributeWin.cs
New file
@@ -0,0 +1,101 @@
using System.Collections.Generic;
using UnityEngine;
public class SimpleMinggeAttributeWin : UIBase
{
    [SerializeField] TextEx title;
    [SerializeField] ScrollerController scroller;
    [SerializeField] ScrollerController skillScroller;
    [SerializeField] SimpleMinggeAttributeCell simpleMinggeAttributeCell;
    Dictionary<int, long> simpleAttrDict;
    List<int> simpleAttrSortList;
    string simpleAttrTitleName;
    Dictionary<int, int> skillDic;
    List<int> skillList;
    protected override void OnPreOpen()
    {
        simpleAttrDict = AttributeManager.Instance.simpleAttrDict;
        simpleAttrSortList = AttributeManager.Instance.simpleAttrSortList;
        simpleAttrTitleName = AttributeManager.Instance.simpleAttrTitleName;
        skillDic = AttributeManager.Instance.skillDic;
        skillList = AttributeManager.Instance.skillList;
        skillScroller.OnGetDynamicSize += OnGetSkillChatDynamicSize;
        skillScroller.OnRefreshCell += OnRefreshSkillCell;
        scroller.OnRefreshCell += OnRefreshCell;
        title.text = simpleAttrTitleName;
        CreateScroller();
        CreateSkillScroller();
    }
    protected override void OnPreClose()
    {
        scroller.OnRefreshCell -= OnRefreshCell;
        skillScroller.OnGetDynamicSize -= OnGetSkillChatDynamicSize;
        skillScroller.OnRefreshCell -= OnRefreshSkillCell;
    }
    private void OnRefreshCell(ScrollerDataType type, CellView cell)
    {
        var _cell = cell.GetComponent<SimpleAttributeAttrCell>();
        _cell?.Display(cell.index, simpleAttrSortList, simpleAttrDict);
    }
    private void OnRefreshSkillCell(ScrollerDataType type, CellView cell)
    {
        var _cell = cell.GetComponent<SimpleMinggeAttributeCell>();
        _cell?.Display(cell.index, skillList, skillDic);
    }
    private void CreateSkillScroller()
    {
        skillScroller.Refresh();
        if (skillList != null)
        {
            for (int i = 0; i < skillList.Count; i++)
            {
                skillScroller.AddCell(ScrollerDataType.Header, i);
            }
        }
        skillScroller.Restart();
    }
    private bool OnGetSkillChatDynamicSize(ScrollerDataType type, int index, out float height)
    {
        height = 0;
        if (skillList.IsNullOrEmpty() || index < 0 || index >= skillList.Count)
            return false;
        if (!TryGetContentByIndex(index, out string content))
            return false;
        height = simpleMinggeAttributeCell.GetHeight(content);
        return true;
    }
    private bool TryGetContentByIndex(int index, out string content)
    {
        content = string.Empty;
        if (skillDic.IsNullOrEmpty() || index < 0 || index >= skillList.Count)
            return false;
        int skillID = skillList[index];
        int lv = skillDic == null || !skillDic.ContainsKey(skillID) ? 0 : skillDic[skillID];
        if (!AttributeManager.Instance.TryGetInfoBySkillID(skillID, lv, out string name, out content))
            return false;
        return true;
    }
    private void CreateScroller()
    {
        scroller.Refresh();
        if (simpleAttrSortList != null)
        {
            int rowCount = (int)Mathf.Ceil((float)simpleAttrSortList.Count / AttributeManager.Instance.SimpleAttributeMaxRowCnt);
            for (int i = 0; i < rowCount; i++)
            {
                scroller.AddCell(ScrollerDataType.Header, i);
            }
        }
        scroller.Restart();
    }
}
Main/System/Attribute/SimpleMinggeAttributeWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 10fbe3cb918521a4c97aac5716db4d4c
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/Battle/BattleConst.cs
@@ -53,6 +53,11 @@
        {30030, WarlordPavilionBattleField},
    };
    //pvp战斗的战场,不在这个列表的视为pve类型的战场
    public static List<string> pvpBattleNameDic = new List<string>()
    {
        ArenaBattleField,
    };
    
    #endregion
Main/System/Battle/BattleManager.cs
@@ -695,6 +695,12 @@
        return null;
    }
    // 判断是不是PvP类型的战场 true pvp false pve
    public bool IsPvpBattle(string battleName)
    {
        return BattleConst.pvpBattleNameDic.Contains(battleName);
    }
    public void DestroyAllBattleField()
    {
        var battleFieldsList = battleFields.Values.ToList();
Main/System/BeautyMM/BeautyMMListWin.cs
@@ -14,7 +14,7 @@
    {
        seeAttrBtn.AddListener(() =>
        {
            AttributeManager.Instance.OpenTotalAttributeWin(BeautyMMManager.Instance.allMMTalentAttr);
            AttributeManager.Instance.OpenSimpleAttributeWin(BeautyMMManager.Instance.allMMTalentAttr, "AttributeTitle01");
        });
        allMMBtn.AddListener(() =>
        {
Main/System/FuncPreset/FuncPresetChooseCell.cs
@@ -17,6 +17,7 @@
    [SerializeField] Button foldBtn;    //折叠
    [SerializeField] FuncPresetChooseCells pareant;
    [SerializeField] Image redImg;
    int curBattleType;
    public void Display(int battleType, int funcType, int id, bool isUnFold)
@@ -35,14 +36,16 @@
            caseNameText.text = data.PresetName;
            numUnFoldText.text = id.ToString();
            changeNameBtn.AddListener(()=>ChangeName(funcType, id));
            foldBtn.AddListener(()=>Fold(funcType, id));
            changeNameBtn.AddListener(() => ChangeName(funcType, id));
            foldBtn.AddListener(() => Fold(funcType, id));
        }
        else
        {
            unFoldObj.SetActive(false);
            foldObj.SetActive(true);
            redImg.SetActive(FuncPresetManager.Instance.ShowRed(funcType, id));
            unFoldBtn.AddListener(() =>
            {
                if (id == selectID && pareant.unFoldID == 0)
@@ -62,16 +65,17 @@
                    }
                }
            });
            lockImg.SetActive(!data.unLock);
            numText.text = !data.unLock ? "" :id.ToString();
            numText.text = !data.unLock ? "" : id.ToString();
            selectImg.SetActive(selectID == id);
        }
    }
    void ChangeName(int funcType, int id)
    {
        UIManager.Instance.OpenWindow<FuncPresetChangeNameWin>(funcType*100 + id);
Main/System/FuncPreset/FuncPresetChooseCells.cs
@@ -17,6 +17,8 @@
    [SerializeField] HorizontalLayoutGroup layoutGroup;
    [SerializeField] FuncPresetChooseCell[] cells;
    [SerializeField] Button unFoldBtn;   //展开更多
    [SerializeField] Image redImg;
    //被选中的方案会显示在外层,如果不是第四个则第四个会显示在最上面
    [SerializeField] Transform moreCellObj;
@@ -91,6 +93,9 @@
            }
        }
        redImg.SetActive(FuncPresetManager.Instance.GetNeedShowRedID(funcType) > 4);
        UIUtility.ForceRefreshLayout(this.transform).Forget();
    }
Main/System/FuncPreset/FuncPresetChooseMoreCell.cs
@@ -12,6 +12,7 @@
    [SerializeField] Transform selectObj;   //流派名才有选中,方案名选中后就显示在外层第四个
    [SerializeField] FuncPresetChooseCells pareant; //记录展开的方案ID用于表现
    [SerializeField] Transform moreCellObj;
    [SerializeField] Image redImg;
    
    public void Display(int battleType, int funcType, int id)
    {
@@ -52,6 +53,7 @@
            });
            changeNameBtn.SetActive(false);
            chooseBtn.RemoveAllListeners();
            redImg.SetActive(FuncPresetManager.Instance.ShowRed(funcType, id));
        }
        selectObj?.SetActive(selectID == id);
        
Main/System/FuncPreset/FuncPresetManager.cs
@@ -343,10 +343,49 @@
        }
        else
        {
            UIManager.Instance.OpenWindow<FuncPresetWin>(battleType);
        }
            UIManager.Instance.OpenWindow<FuncPresetWin>(battleType);
        }
    }
    //功能类的满足条件后,提示红点手动解锁
    // 某个方案是否要红点提示
    public bool ShowRed(int funcType, int id)
    {
        if (funcType == (int)FuncPresetType.Mingge)
        {
            var presetData = GetFuncPreset(funcType, id);
            if (presetData.unLock)
            {
                return false;
            }
            var config = PresetUnlockConfig.GetPresetUnlockConfig(funcType, id);
            if (config != null && config.UnlockType == 2 && MinggeManager.Instance.m_GanwuLV < config.UnlockValue)
            {
                return false;
            }
            return true;
        }
        return false;
    }
    public int GetNeedShowRedID(int funcType)
    {
        if (funcType == (int)FuncPresetType.Mingge)
        {
            var maxCnt = PresetUnlockConfig.GetFuncPresetMaxCount(funcType); ;
            for (int i = 1; i <= maxCnt; i++)
            {
                if (ShowRed(funcType, i))
                {
                    return i;
                }
            }
        }
        return 0;
    }
}
Main/System/Gubao/GubaoListWin.cs
@@ -32,7 +32,7 @@
        });
        seeAttrBtn.AddListener(() =>
        {
            AttributeManager.Instance.OpenTotalAttributeWin(GubaoManager.Instance.gubaoAllAttrDict);
            AttributeManager.Instance.OpenSimpleAttributeWin(GubaoManager.Instance.gubaoAllAttrDict, "AttributeTitle02");
        });
    }
Main/System/Guild/GuildHallWin.cs
@@ -42,7 +42,7 @@
        });
        guildLVBtn.AddListener(() =>
        {
            // UIManager.Instance.OpenWindow<GuildLevelUpWin>();
            UIManager.Instance.OpenWindow<GuildLevelWin>();
        });
        noticeBtn.AddListener(() =>
@@ -98,8 +98,8 @@
        guildNameText.text = guildInfo.FamilyName + Language.Get("L1039", ServerListCenter.Instance.GetServerName(guildInfo.ServerID));
        guildNumText.text = GuildManager.Instance.EncryptGuildID(guildInfo.FamilyID);
        noticeText.text = string.IsNullOrEmpty(guildInfo.Broadcast) ? Language.Get("Guild_62") : guildInfo.Broadcast;
        lvExpProcess.fillAmount = (float)guildInfo.FamilyLVExp / config.NeedExp;
        expText.text = guildInfo.FamilyLVExp + "/" + config.NeedExp;
        lvExpProcess.fillAmount = config.NeedExp == 0 ? 1 : (float)guildInfo.FamilyLVExp / config.NeedExp;
        expText.text = config.NeedExp == 0 ? Language.Get("L1055") : StringUtility.Concat(guildInfo.FamilyLVExp.ToString(), "/", config.NeedExp.ToString());
    }
Main/System/Guild/GuildLevelItem.cs
New file
@@ -0,0 +1,16 @@
using UnityEngine;
public class GuildLevelItem : MonoBehaviour
{
    [SerializeField] TextEx lvText;
    [SerializeField] TextEx memberMaxText;
    [SerializeField] TextEx funcInfoText;
    public void Display(FamilyConfig config)
    {
        if (config == null)
            return;
        lvText.text = Language.Get("BattlePassValue1", config.FamilyLV);
        memberMaxText.text = StringUtility.Concat(Language.Get("Guild_81"), Language.Get("Guild_40", config.MemberMax));
        funcInfoText.text = StringUtility.Concat(Language.Get("Guild_81"), Language.Get("Guild_39", config.FuncText)); ;
    }
}
Main/System/Guild/GuildLevelItem.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a0dc23011932ea5488d36288228cdf8e
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/Guild/GuildLevelWin.cs
New file
@@ -0,0 +1,75 @@
using UnityEngine;
/// <summary>
/// 公会等级界面
/// </summary>
public class GuildLevelWin : UIBase
{
    [SerializeField] Transform noMax;
    [SerializeField] Transform max;
    [SerializeField] GuildLevelItem nowItem;
    [SerializeField] GuildLevelItem nextItem;
    [SerializeField] GuildLevelItem maxItem;
    [SerializeField] ImageEx sliderImage;
    [SerializeField] TextEx sliderText;
    protected override void OnPreOpen()
    {
        PlayerDatas.Instance.fairyData.OnRefreshFairyInfo += OnRefreshFairyInfo;
        Display();
    }
    protected override void OnPreClose()
    {
        PlayerDatas.Instance.fairyData.OnRefreshFairyInfo -= OnRefreshFairyInfo;
    }
    private void OnRefreshFairyInfo()
    {
        Display();
    }
    private void Display()
    {
        PlayerFairyData fairyData = PlayerDatas.Instance.fairyData;
        if (fairyData == null || !fairyData.HasFairy || fairyData.fairy == null)
            return;
        int nowFamilyLV = fairyData.fairy.FamilyLV;
        bool hasNextLv = FamilyConfig.TryGetNextLvConfig(nowFamilyLV, out FamilyConfig nowConfig, out FamilyConfig nextConfig);
        noMax.SetActive(hasNextLv);
        max.SetActive(!hasNextLv);
        nowItem.SetActive(hasNextLv);
        nextItem.SetActive(hasNextLv);
        maxItem.SetActive(!hasNextLv);
        if (hasNextLv)
        {
            nowItem.Display(nowConfig);
            nextItem.Display(nextConfig);
            RefreshSlider();
            return;
        }
        if (!FamilyConfig.TryGetMaxFamilyLVConfig(out FamilyConfig maxConfig))
            return;
        maxItem.Display(maxConfig);
    }
    public void RefreshSlider()
    {
        PlayerFairyData fairyData = PlayerDatas.Instance.fairyData;
        if (fairyData == null || !fairyData.HasFairy || fairyData.fairy == null)
            return;
        int nowFamilyLV = fairyData.fairy.FamilyLV;
        bool hasNextLv = FamilyConfig.TryGetNextLvConfig(nowFamilyLV, out FamilyConfig nowConfig, out FamilyConfig nextConfig);
        if (hasNextLv)
        {
            int familyLVExp = fairyData.fairy.FamilyLVExp;
            sliderImage.fillAmount = nowConfig.NeedExp == 0 ? 1 : (float)familyLVExp / nowConfig.NeedExp;
            sliderText.text = nowConfig.NeedExp == 0 ? Language.Get("L1055") : StringUtility.Concat(familyLVExp.ToString(), "/", nowConfig.NeedExp.ToString());
        }
    }
}
Main/System/Guild/GuildLevelWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 834b5b5af88e38d4783f30efa3d89599
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/Horse/HorseSkinWin.cs
@@ -214,7 +214,7 @@
    void OnSeeAttrBtnClick()
    {
        AttributeManager.Instance.OpenTotalAttributeWin(HorseManager.Instance.skinAttrDic);
        AttributeManager.Instance.OpenSimpleAttributeWin(HorseManager.Instance.skinAttrDic, "AttributeTitle04");
    }
Main/System/Mingge/MinggeEquipChangeCell.cs
@@ -131,7 +131,7 @@
            if (i >= fightAttrs.Count)
            {
                //显示锁住
                fightAttrNames[i].text = Language.Get("Mingge27", MinggeManager.Instance.unlockAttrLVList[i - 1] - 1);
                fightAttrNames[i].text = Language.Get("Mingge27", MinggeManager.Instance.unlockAttrLVList[i - 1]);
                fightAttrValues[i].SetActive(false);
            }
            else
Main/System/Mingge/MinggeEquipWin.cs
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
@@ -7,13 +8,15 @@
    [SerializeField] MinggeEquipChangeCell oldEquipCell;
    [SerializeField] MinggeEquipChangeCell newEquipCell;
    [SerializeField] Transform presetObj;
    [SerializeField] ScrollRect scrollRect;
    [SerializeField] GroupButtonEx[] presetBtns;
    [SerializeField] Text[] presetNameTexts;
    List<Text> presetNameTexts = new List<Text>();
    int selectPresetID = 0;
    int lastDropIndex = -1;
    protected override void InitComponent()
    {
        presetNameTexts.Clear();
        for (int i = 0; i < presetBtns.Length; i++)
        {
            int index = i + 1;
@@ -22,6 +25,7 @@
                selectPresetID = index;
                Display();
            });
            presetNameTexts.Add(presetBtns[i].GetComponentInChildren<Text>());
        }
    }
@@ -70,7 +74,9 @@
                    presetBtns[i].SetActive(false);
                }
            }
            presetBtns[selectPresetID -1].SelectBtn();
            presetBtns[selectPresetID - 1].SelectBtn();
            //滚动到 selectPresetID 位置
            scrollRect.horizontalNormalizedPosition = selectPresetID < 4 ? 0 : selectPresetID * 1f / cnt;
        }
        else
        {
Main/System/Mingge/MinggeManager.cs
@@ -43,6 +43,7 @@
        PackManager.Instance.RefreshItemEvent += RefreshItemEvent;
        DTCA814_tagMCMakeItemAnswer.MakeItemAnswerEvent += OnEquipResult;
        DTC0403_tagPlayerLoginLoadOK.playerLoginOkEvent += OnPlayerLoginOK;
        FuncPresetManager.Instance.OnFuncPresetUseDataEvent += OnFuncPresetUseDataEvent;
        ParseConfig();
    }
@@ -52,6 +53,7 @@
        PackManager.Instance.RefreshItemEvent -= RefreshItemEvent;
        DTCA814_tagMCMakeItemAnswer.MakeItemAnswerEvent -= OnEquipResult;
        DTC0403_tagPlayerLoginLoadOK.playerLoginOkEvent -= OnPlayerLoginOK;
        FuncPresetManager.Instance.OnFuncPresetUseDataEvent -= OnFuncPresetUseDataEvent;
    }
@@ -139,11 +141,22 @@
    public void UpdateMinggeInfo(HB132_tagSCMinggeInfo netPack)
    {
        var beforeLV= m_GanwuLV;
        m_GanwuLV = netPack.GanwuLV;
        m_GanwuExp = (int)netPack.GanwuExp;
        m_Lingying = (int)netPack.Lingying;
        if (beforeLV != m_GanwuLV)
        {
            UpdateRedPoint();
            beforeLV = m_GanwuLV;
        }
        OnMinggeInfoUpdate?.Invoke();
    }
    void OnFuncPresetUseDataEvent(int type)
    {
        UpdateRedPoint();
    }
@@ -319,12 +332,14 @@
    Redpoint tmpRP = new Redpoint(MainRedDot.MainAffairsRedpoint, MainRedDot.RedPoint_Mingge);
    Redpoint redpointMG = new Redpoint(MainRedDot.RedPoint_Mingge, MainRedDot.RedPoint_Mingge * 10 + 1);
    Redpoint redpointPray = new Redpoint(MainRedDot.RedPoint_Mingge, MainRedDot.RedPoint_Mingge * 10 + 2);
    Redpoint redpointPreset = new Redpoint(MainRedDot.RedPoint_Mingge, MainRedDot.RedPoint_Mingge * 10 + 3);
    
    void UpdateRedPoint()
    {
        redpointMG.state = RedPointState.None;
        redpointPray.state = RedPointState.None;
        redpointPreset.state = RedPointState.None;
        if (!FuncOpen.Instance.IsFuncOpen((int)FuncOpenEnum.Mingge))
        {
            return;
@@ -338,6 +353,10 @@
        {
            redpointPray.state = RedPointState.Simple;
        }
        if (FuncPresetManager.Instance.GetNeedShowRedID((int)FuncPresetType.Mingge) != 0)
        {
            redpointPreset.state = RedPointState.Simple;
        }
    }
    #endregion
Main/System/Mingge/MinggeSmallTipWin.cs
@@ -73,7 +73,7 @@
                //显示锁住
                fightAttrNames[i].text = "?";
                fightAttrNames[i].colorType = TextColType.NavyGray;
                fightAttrValues[i].text = Language.Get("Mingge27", MinggeManager.Instance.unlockAttrLVList[i - 1] - 1);
                fightAttrValues[i].text = Language.Get("Mingge27", MinggeManager.Instance.unlockAttrLVList[i - 1]);
                fightAttrValues[i].colorType = TextColType.NavyGray;
            }
            else
Main/System/Mingge/MinggeWin.cs
@@ -41,7 +41,13 @@
        presetChooseCells.ChangeAlignment(TextAnchor.MiddleCenter);
        seeAttrBtn.AddListener(() =>
        {
            AttributeManager.Instance.OpenTotalAttributeWin(MinggeManager.Instance.minggeAttrDict);
            var skillTypeDict = MinggeManager.Instance.GetMinggeSkillTypeIDDict();
            var skillDict = new Dictionary<int, int>();
            foreach (var kvp in skillTypeDict)
            {
                skillDict[kvp.Key] = kvp.Value.Count;
            }
            AttributeManager.Instance.OpenMinggeSimpleAttributeWin(MinggeManager.Instance.minggeAttrDict, skillDict);
        });
        tyBtn.AddListener(SendTuiyan);
        for (int i = 0; i < suiteNameTexts.Length; i++)
Main/System/OtherPlayerDetail/OtherHeroDetailWin.cs
@@ -6,6 +6,8 @@
public class OtherHeroDetailWin : UIBase
{
    [SerializeField] ImageEx bgImage;
    [SerializeField] ImageEx iconImage;
    [SerializeField] SkillBaseCell normalSkillCell;
    [SerializeField] SkillBaseCell angerSkillCell;
    [SerializeField] HeroShowBaseCell heroShow;
@@ -16,22 +18,25 @@
    [SerializeField] GameObject fetterGo;
    [SerializeField] Text[] fetterText;   //羁绊
    [SerializeField] Text[] fetterNameText;   //羁绊
    [SerializeField] Color32 nameCol;
    [SerializeField] Color32 descCol;
    [SerializeField] Color32 grayCol;
    [SerializeField] GameObject potential;
    [SerializeField] GameObject potentialCell; //潜能和觉醒用于创建
    [SerializeField] Transform potentialCellParent; //潜能父节点
    [SerializeField] Transform awakeCellParent; //潜能父节点
    [SerializeField] GameObject awakeGo;
    OtherPlayerDetailManager.RolePlusData.HeroData heroData;
    List<OtherPlayerDetailManager.RolePlusData.HeroData> heroDatas;
    Dictionary<int, long> attrDict;
    OtherPlayerDetailManager manager { get { return OtherPlayerDetailManager.Instance; } }
    protected override void InitComponent()
    {
        awakeCellList = new List<GameObject>();
        potentialCellList = new List<GameObject>();
        btnTotalAttr.SetListener(() => { AttributeManager.Instance.OpenTotalAttributeWin(attrDict); });
        btnTotalAttr.SetListener(() =>
        {
            AttributeManager.Instance.OpenTotalAttributeWin(attrDict == null ? new Dictionary<int, long>() : attrDict);
        });
    }
    protected override void OnPreOpen()
@@ -42,29 +47,149 @@
            allAttrScroll.verticalNormalizedPosition = 1f;
        }
        heroData = manager.heroData;
        heroDatas = manager.heroDatas;
        attrDict = heroData.AttrDict;
        Display();
    }
    protected override void OnPreClose()
    {
    }
    private bool TryGetHeroInfo(BattleClickHeroData data, out HeroInfo heroInfo)
    {
        heroInfo = null;
        if (!HeroManager.Instance.HasHero(data.heroID))
            return false;
        heroInfo = HeroManager.Instance.GetHeroByID(data.heroID);
        return true;
    }
    private void Display()
    {
        if (heroDatas.IsNullOrEmpty() || heroData == null)
        attrDict = null;
        switch (manager.viewHeroType)
        {
            return;
            case 0:
                BattleClickHeroData data = manager.clickHeroData;
                bgImage.SetSprite(data.isMySide ? "OtherHeroDetailBGBlue" : "OtherHeroDetailBGRed");
                iconImage.SetSprite(data.isMySide ? "OtherHeroDetailIconBlue" : "OtherHeroDetailIconRed");
                if (data.isMySide)
                {
                    if (!TryGetHeroInfo(data, out HeroInfo heroInfo))
                    {
                        SysNotifyMgr.Instance.ShowTip("NoHeroFromBag");
                        DelayCloseWindow().Forget();
                        return;
                    }
                    attrDict = FightPowerManager.Instance.GetHeroTotalAttr(heroInfo);
                    DisplayByMyPack(heroInfo, data);
                }
                else
                {
                    if (BattleManager.Instance.IsPvpBattle(data.battleName))
                    {
                        attrDict = manager?.heroData?.AttrDict;
                        DiplayByViewOtherPlayer();
                    }
                    else
                    {
                        attrDict = manager?.viewNPCAttr?.AttrDict;
                        DisplayByViewNPCAttr();
                    }
                }
                break;
            case 1:
            default:
                bgImage.SetSprite("OtherHeroDetailBGRed");
                iconImage.SetSprite("OtherHeroDetailIconRed");
                attrDict = manager?.heroData?.AttrDict;
                DiplayByViewOtherPlayer();
                break;
        }
    }
    void DisplayByMyPack(HeroInfo heroInfo, BattleClickHeroData data)
    {
        if (heroInfo == null)
            return;
        HeroConfig heroConfig = heroInfo?.heroConfig;
        if (heroConfig == null || data.teams == null || !data.isMySide)
            return;
        int heroID = heroConfig.HeroID;
        int skinID = heroConfig?.SkinIDList[0] ?? 0;
        int lv = heroInfo.heroLevel;
        int star = heroInfo.heroStar;
        int quality = heroConfig.Quality;
        int breakLevel = heroInfo.breakLevel;
        int awakeLevel = heroInfo.awakeLevel;
        int atkSkillID = heroConfig.AtkSkillID;
        int angerSkillID = heroConfig.AngerSkillID;
        List<int> talentIDList = heroInfo.talentIDList;
        List<int> talentLvList = heroInfo.talentLvList;
        int[] FetterIDList = heroConfig.FetterIDList;
        DisplaySkill(heroID, atkSkillID, angerSkillID);
        DisplayHeroShow(heroID, skinID, breakLevel, star, awakeLevel, lv);
        DisplayAttr(attrDict);
        DisplayGiftBaseCell(heroID, awakeLevel, talentIDList, talentLvList);
        DisplayFetter(data.teams, FetterIDList);
        DisplayPotential(heroID, breakLevel);
        RefreshAwake(heroID, quality, awakeLevel);
        ForceRefreshLayout();
    }
    void DisplayByViewNPCAttr()
    {
        ViewNPCAttr viewNPCAttr = manager.viewNPCAttr;
        if (viewNPCAttr == null)
            return;
        int heroID = (int)viewNPCAttr.HeroID;
        if (!HeroConfig.HasKey(heroID))
            return;
        HeroConfig heroConfig = HeroConfig.Get(heroID);
        int skinID = heroConfig?.SkinIDList[0] ?? 0;
        int lv = viewNPCAttr.LV;
        int star = viewNPCAttr.Star;
        int quality = heroConfig.Quality;
        int breakLevel = viewNPCAttr.BreakLV;
        int awakeLevel = viewNPCAttr.AwakeLV;
        int atkSkillID = heroConfig.AtkSkillID;
        int angerSkillID = heroConfig.AngerSkillID;
        List<int> talentIDList = null;
        List<int> talentLvList = null;
        int[] FetterIDList = heroConfig.FetterIDList;
        DisplaySkill(heroID, atkSkillID, angerSkillID);
        DisplayHeroShow(heroID, skinID, breakLevel, star, awakeLevel, lv);
        DisplayAttr(attrDict);
        DisplayGiftBaseCell(heroID, awakeLevel, talentIDList, talentLvList);
        DisplayFetter(teams: null, FetterIDList);
        DisplayPotential(heroID, breakLevel);
        RefreshAwake(heroID, quality, awakeLevel);
        ForceRefreshLayout();
    }
    void DiplayByViewOtherPlayer()
    {
        OtherPlayerDetailManager.RolePlusData.HeroData heroData = manager.heroData;
        List<OtherPlayerDetailManager.RolePlusData.HeroData> heroDatas = manager.heroDatas;
        if (heroDatas.IsNullOrEmpty() || heroData == null)
            return;
        int heroID = heroData.HeroID;
        if (!HeroConfig.HasKey(heroID))
        {
            return;
        }
        HeroConfig heroConfig = HeroConfig.Get(heroID);
@@ -82,7 +207,7 @@
        DisplaySkill(heroID, atkSkillID, angerSkillID);
        DisplayHeroShow(heroID, skinID, breakLevel, star, awakeLevel, lv);
        DisplayAttr(heroData);
        DisplayAttr(attrDict);
        DisplayGiftBaseCell(heroID, awakeLevel, talentIDList, talentLvList);
        DisplayFetter(heroDatas, FetterIDList);
        DisplayPotential(heroID, breakLevel);
@@ -90,6 +215,7 @@
        ForceRefreshLayout();
    }
    List<GameObject> awakeCellList;   //觉醒
    void RefreshAwake(int heroID, int quality, int awakeLevel)
@@ -164,7 +290,11 @@
    void DisplayPotential(int heroID, int breakLevel)
    {
        if (!HeroBreakConfig.configDics.ContainsKey(heroID))
        {
            potential.SetActive(false);
            return;
        }
        var list = HeroBreakConfig.configDics[heroID].Keys.ToList();
@@ -218,31 +348,59 @@
            }
        }
    }
    private void DisplayAttr(OtherPlayerDetailManager.RolePlusData.HeroData heroData)
    private void DisplayAttr(Dictionary<int, long> attrDict)
    {
        if (heroData == null || heroData.AttrDict == null)
        {
        if (attrDict.IsNullOrEmpty())
            return;
        }
        var attrDict = heroData.AttrDict;
        var baseAttrIDList = PlayerPropertyConfig.playerPropertyDict[PlayerPropertyConfig.baseType];
        var fightAttrIDList = PlayerPropertyConfig.playerPropertyDict[PlayerPropertyConfig.fightType];
        var totalAttrIDList = baseAttrIDList.Concat(fightAttrIDList).ToList();
        var antiAttrIDList = PlayerPropertyConfig.playerPropertyDict[PlayerPropertyConfig.fightAntiType];
        var totalAttrIDList = baseAttrIDList.Concat(fightAttrIDList).Concat(antiAttrIDList).ToList();
        for (int i = 0; i < attrs.Length; i++)
        {
            if (i < totalAttrIDList.Count)
            {
                attrs[i].SetActive(true);
                int attrId = totalAttrIDList[i];
                long attrValue = attrDict.ContainsKey(attrId) ? attrDict[attrId] : 0;
                attrs[i].text = PlayerPropertyConfig.GetFullDescription(attrId, attrValue);
                if (PlayerPropertyConfig.HasKey(attrId))
                {
                    PlayerPropertyConfig config = PlayerPropertyConfig.Get(attrId);
                    long attrValue = attrDict.ContainsKey(attrId) ? attrDict[attrId] : 0;
                    attrs[i].text = Language.Get("OtherPlayerDetail15", config.Name, PlayerPropertyConfig.GetValueDescription(attrId, attrValue));
                }
                else
                {
                    attrs[i].SetActive(false);
                }
            }
            else
            {
                attrs[i].SetActive(false);
            }
        }
    }
    private bool HasFetterHero(List<BattleObject> teams, int heroID)
    {
        if (teams.IsNullOrEmpty())
        {
            return false;
        }
        for (int i = 0; i < teams.Count; i++)
        {
            if (teams[i].teamHero == null)
            {
                continue;
            }
            if (teams[i].teamHero.heroId == heroID)
            {
                return true;
            }
        }
        return false;
    }
    private bool HasFetterHero(List<OtherPlayerDetailManager.RolePlusData.HeroData> heroDatas, int heroID)
@@ -259,6 +417,53 @@
            }
        }
        return false;
    }
    private void DisplayFetter(List<BattleObject> teams, int[] FetterIDList)
    {
        if (FetterIDList.IsNullOrEmpty() || teams.IsNullOrEmpty())
        {
            fetterGo.SetActive(false);
            return;
        }
        for (int i = 0; i < fetterText.Length; i++)
        {
            if (i < FetterIDList.Length)
            {
                fetterText[i].SetActive(true);
                var fetterID = FetterIDList[i];
                HeroFetterConfig fetterConfig = HeroFetterConfig.Get(fetterID);
                List<string> heroNames = new List<string>();
                bool isAllCollect = true; //是否全收集
                foreach (var tmpHeroID in fetterConfig.HeroIDList)
                {
                    heroNames.Add(HeroConfig.Get(tmpHeroID).Name);
                    bool hasFetterHero = HasFetterHero(teams, tmpHeroID);
                    if (!hasFetterHero)
                    {
                        isAllCollect = false;
                    }
                }
                fetterGo.SetActive(isAllCollect);
                var attrStr = string.Join(Language.Get("SplitString1"), heroNames) + Language.Get("herocard38");
                for (int j = 0; j < fetterConfig.AttrIDList.Length; j++)
                {
                    string format = !isAllCollect ? "{0}+{1}" : "{0}+" + UIHelper.AppendColor(TextColType.Green, "{1}");
                    attrStr += Language.Get("L1112") + PlayerPropertyConfig.GetFullDescription(fetterConfig.AttrIDList[j], fetterConfig.AttrValueList[j], format);
                }
                fetterText[i].text = attrStr;
                fetterText[i].color = isAllCollect ? descCol : grayCol;
                fetterNameText[i].text = fetterConfig.FetterName;
                fetterNameText[i].color = isAllCollect ? nameCol : grayCol;
            }
            else
            {
                fetterText[i].SetActive(false);
            }
        }
    }
    private void DisplayFetter(List<OtherPlayerDetailManager.RolePlusData.HeroData> heroDatas, int[] FetterIDList)
@@ -296,9 +501,9 @@
                    attrStr += Language.Get("L1112") + PlayerPropertyConfig.GetFullDescription(fetterConfig.AttrIDList[j], fetterConfig.AttrValueList[j], format);
                }
                fetterText[i].text = attrStr;
                fetterText[i].color = UIHelper.GetUIColor(isAllCollect ? TextColType.NavyBrown : TextColType.NavyGray);
                fetterText[i].color = isAllCollect ? descCol : grayCol;
                fetterNameText[i].text = fetterConfig.FetterName;
                fetterNameText[i].color = UIHelper.GetUIColor(isAllCollect ? TextColType.NavyBrown : TextColType.NavyGray);
                fetterNameText[i].color = isAllCollect ? nameCol : grayCol;
            }
            else
@@ -310,12 +515,33 @@
    private void DisplayGiftBaseCell(int heroID, int awakeLevel, List<int> talentIDList, List<int> talentLvList)
    {
        int showCount = HeroUIManager.Instance.GetGiftGirdMaxCount(heroID);
        if (talentIDList == null || talentLvList == null)
        {
            for (int i = 0; i < giftBaseCells.Length; i++)
            {
                if (i >= showCount)
                {
                    giftBaseCells[i].SetActive(false);
                    continue;
                }
                giftBaseCells[i].SetActive(true);
                //非对比的显示
                if (i < HeroUIManager.Instance.normalGiftMaxCnt)
                {
                    giftBaseCells[i].Init(0, 0);
                }
                else
                {
                    giftBaseCells[i].Init(-1, 0, 0, heroID, i);
                }
            }
            return;
        }
        int showCount = HeroUIManager.Instance.GetGiftGirdMaxCount(heroID);
        for (int i = 0; i < giftBaseCells.Length; i++)
        {
            if (i >= showCount)
@@ -354,6 +580,14 @@
    private void DisplaySkill(int heroID, int atkSkillID, int angerSkillID)
    {
        if (!HeroConfig.HasKey(heroID) || !SkillConfig.HasKey(atkSkillID) || !SkillConfig.HasKey(angerSkillID))
        {
            normalSkillCell.SetActive(false);
            angerSkillCell.SetActive(false);
            return;
        }
        normalSkillCell.SetActive(true);
        angerSkillCell.SetActive(true);
        normalSkillCell.Init(atkSkillID, () =>
        {
            UIManager.Instance.OpenWindow<HeroSkillWin>(heroID);
@@ -381,6 +615,8 @@
        {
            LayoutRebuilder.ForceRebuildLayoutImmediate(layout.GetComponent<RectTransform>());
        }
        // Layout刷新完成后重新设置滚动位置
        if (allAttrScroll != null)
            allAttrScroll.verticalNormalizedPosition = 1f;
    }
}
Main/System/OtherPlayerDetail/OtherHeroFightingCardItem.cs
@@ -83,10 +83,9 @@
        {
            return;
        }
        manager.viewHeroType = 1;
        manager.heroData = heroData;
        manager.heroDatas = heros;
        if (!UIManager.Instance.IsOpened<OtherHeroDetailWin>())
        {
            UIManager.Instance.OpenWindow<OtherHeroDetailWin>();
Main/System/OtherPlayerDetail/OtherNPCDetailWin.cs
New file
@@ -0,0 +1,326 @@
using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
public class OtherNPCDetailWin : UIBase
{
    [SerializeField] RectTransform funcarea;
    [SerializeField] Transform bkImage1;
    [SerializeField] Transform bkImage2;
    [SerializeField] ImageEx bgImage;
    [SerializeField] ImageEx iconImage;
    [SerializeField] GameObject skill;
    [SerializeField] SkillBaseCell normalSkillCell;
    [SerializeField] SkillBaseCell angerSkillCell;
    [SerializeField] OtherNpcHeroCell heroShow;
    [SerializeField] ButtonEx btnTotalAttr;
    [SerializeField] TextEx[] attrs;
    [SerializeField] ScrollRect allAttrScroll;      //培养属性 滚动区
    [SerializeField] GameObject potential;
    [SerializeField] GameObject potentialCell; //潜能和觉醒用于创建
    [SerializeField] Transform potentialCellParent; //潜能父节点
    [SerializeField] Transform awakeCellParent; //潜能父节点
    [SerializeField] GameObject awakeGo;
    Dictionary<int, long> attrDict;
    OtherPlayerDetailManager manager { get { return OtherPlayerDetailManager.Instance; } }
    protected override void InitComponent()
    {
        awakeCellList = new List<GameObject>();
        potentialCellList = new List<GameObject>();
        btnTotalAttr.SetListener(() =>
        {
            AttributeManager.Instance.OpenTotalAttributeWin(attrDict == null ? new Dictionary<int, long>() : attrDict);
        });
    }
    protected override void OnPreOpen()
    {
        // 重置所有滚动区到顶部
        if (allAttrScroll != null)
            allAttrScroll.verticalNormalizedPosition = 1f;
        Display();
    }
    protected override void OnPreClose()
    {
    }
    private bool IsSimple(ViewNPCAttr viewNPCAttr)
    {
        int heroID = (int)viewNPCAttr.HeroID;
        return !HeroBreakConfig.configDics.ContainsKey(heroID) && !HeroAwakeConfig.CanAwake(heroID, 1);
    }
    private void Display()
    {
        BattleClickHeroData data = manager.clickHeroData;
        bgImage.SetSprite(data.isMySide ? "OtherHeroDetailBGBlue" : "OtherHeroDetailBGRed");
        iconImage.SetSprite(data.isMySide ? "OtherHeroDetailIconBlue" : "OtherHeroDetailIconRed");
        attrDict = manager?.viewNPCAttr?.AttrDict;
        DisplayByViewNPCAttr();
    }
    void DisplayByViewNPCAttr()
    {
        ViewNPCAttr viewNPCAttr = manager.viewNPCAttr;
        if (viewNPCAttr == null)
            return;
        int heroID = (int)viewNPCAttr.HeroID;
        if (!HeroConfig.HasKey(heroID))
            return;
        HeroConfig heroConfig = HeroConfig.Get(heroID);
        bool isSimple = IsSimple(viewNPCAttr);
        bkImage1.SetActive(!isSimple);
        bkImage2.SetActive(isSimple);
        funcarea.sizeDelta = new Vector2(funcarea.sizeDelta.x, isSimple ? 245 : 503);
        int skinID = heroConfig?.SkinIDList[0] ?? 0;
        int lv = viewNPCAttr.LV;
        int star = viewNPCAttr.Star;
        int quality = heroConfig.Quality;
        int breakLevel = viewNPCAttr.BreakLV;
        int awakeLevel = viewNPCAttr.AwakeLV;
        int atkSkillID = heroConfig.AtkSkillID;
        int angerSkillID = heroConfig.AngerSkillID;
        DisplaySkill(heroID, atkSkillID, angerSkillID);
        DisplayHeroShow(heroID, skinID, breakLevel, star, awakeLevel, lv);
        DisplayAttr(attrDict);
        DisplayPotential(heroID, breakLevel);
        RefreshAwake(heroID, quality, awakeLevel);
        ForceRefreshLayout();
    }
    List<GameObject> awakeCellList;   //觉醒
    void RefreshAwake(int heroID, int quality, int awakeLevel)
    {
        if (!HeroAwakeConfig.CanAwake(heroID, 1))
        {
            awakeGo.SetActive(false);
            return;
        }
        awakeGo.SetActive(true);
        var maxLV = HeroAwakeConfig.GetMaxAwakeLV(heroID);
        var starCnt = HeroQualityConfig.Get(quality).InitStarUpper;
        for (int i = 1; i <= maxLV; i++)
        {
            if (i > awakeCellList.Count)
            {
                awakeCellList.Add(Instantiate(potentialCell, awakeCellParent));
            }
            var go = awakeCellList[i - 1];
            var descText = go.GetComponent<Text>();
            var nameText = go.GetComponent<Text>("skillname");
            go.SetActive(true);
            var config = HeroAwakeConfig.GetHeroAwakeConfig(heroID, i);
            int type = config.UnlockTalentSlot != 0 ? 1 : config.SkillID != 0 ? 2 : 3;
            var awakeStr = string.Empty;
            if (type == 1)
            {
                starCnt += config.AddStarUpper;
                awakeStr = Language.Get("HeroAwake8", config.UnlockTalentSlot, starCnt);
            }
            else if (type == 2)
            {
                var skill = SkillConfig.Get(config.SkillID);
                if (skill != null)
                {
                    awakeStr = Language.Get("L1039", skill.SkillName) + skill.Description;
                }
                else
                {
                    Debug.LogError($"heroId:{heroID} 觉醒技能ID:{config.SkillID} 配置错误技能不存在");
                }
            }
            else
            {
                for (int k = 0; k < config.AttrIDList.Length; k++)
                {
                    awakeStr += (string.IsNullOrEmpty(config.SkillIName) ? string.Empty : Language.Get("L1039", config.SkillIName)) +
                    PlayerPropertyConfig.GetFullDescription(config.AttrIDList[k], config.AttrValueList[k], "{0}+" + UIHelper.AppendColor(TextColType.Green, "{1}"))
                    + (k == config.AttrIDList.Length - 1 ? "" : "\n");
                }
            }
            if (i - 1 < awakeLevel)
            {
                nameText.text = Language.Get("herocard12", i) + Language.Get("L1096");
                descText.text = awakeStr;
            }
            else
            {
                //置灰
                nameText.text = UIHelper.AppendColor(TextColType.NavyGray, Language.Get("herocard12", i) + Language.Get("L1096"));
                descText.text = UIHelper.AppendColor(TextColType.NavyGray, UIHelper.RemoveColor(awakeStr));
            }
        }
    }
    List<GameObject> potentialCellList;   //潜能
    void DisplayPotential(int heroID, int breakLevel)
    {
        if (!HeroBreakConfig.configDics.ContainsKey(heroID))
        {
            potential.SetActive(false);
            return;
        }
        var list = HeroBreakConfig.configDics[heroID].Keys.ToList();
        list.Sort();
        for (int i = 0; i < list.Count; i++)
        {
            var nextQualityBreakConfig = HeroBreakConfig.GetHeroBreakConfig(heroID, i + 1);
            if (nextQualityBreakConfig == null)
                break;
            List<string> attrStrArr = new List<string>();
            for (int j = 0; j < nextQualityBreakConfig.AttrIDList.Length; j++)
            {
                if (nextQualityBreakConfig.AttrIDList[j] == 0)
                    continue;
                string format = i < breakLevel ? "{0}" + UIHelper.AppendColor(TextColType.Green, "+{1}") : "{0}+{1}";
                attrStrArr.Add((string.IsNullOrEmpty(nextQualityBreakConfig.SkillIName) ? string.Empty : Language.Get("L1039", nextQualityBreakConfig.SkillIName)) +
                PlayerPropertyConfig.GetFullDescription(nextQualityBreakConfig.AttrIDList[j], nextQualityBreakConfig.AttrValueList[j], format));
            }
            if (nextQualityBreakConfig.SkillID != 0)
            {
                var skill = SkillConfig.Get(nextQualityBreakConfig.SkillID);
                if (skill != null)
                {
                    attrStrArr.Add(Language.Get("L1039", skill.SkillName) + skill.Description);
                }
                else
                {
                    Debug.LogError("未配置技能" + nextQualityBreakConfig.SkillID);
                }
            }
            if (i >= potentialCellList.Count)
            {
                potentialCellList.Add(Instantiate(potentialCell, potentialCellParent));
            }
            var go = potentialCellList[i];
            var descText = go.GetComponent<Text>();
            var nameText = go.GetComponent<Text>("skillname");
            go.SetActive(true);
            if (i < breakLevel)
            {
                nameText.text = Language.Get("herocard63", i + 1);
                descText.text = string.Join("\n", attrStrArr);
            }
            else
            {
                //置灰
                nameText.text = UIHelper.AppendColor(TextColType.NavyGray, Language.Get("herocard63", i + 1));
                descText.text = UIHelper.AppendColor(TextColType.NavyGray, UIHelper.RemoveColor(string.Join("\n", attrStrArr)));
            }
        }
    }
    private void DisplayAttr(Dictionary<int, long> attrDict)
    {
        if (attrDict.IsNullOrEmpty())
            return;
        var baseAttrIDList = PlayerPropertyConfig.playerPropertyDict[PlayerPropertyConfig.baseType];
        var fightAttrIDList = PlayerPropertyConfig.playerPropertyDict[PlayerPropertyConfig.fightType];
        var antiAttrIDList = PlayerPropertyConfig.playerPropertyDict[PlayerPropertyConfig.fightAntiType];
        var totalAttrIDList = baseAttrIDList.Concat(fightAttrIDList).Concat(antiAttrIDList).ToList();
        for (int i = 0; i < attrs.Length; i++)
        {
            if (i < totalAttrIDList.Count)
            {
                attrs[i].SetActive(true);
                int attrId = totalAttrIDList[i];
                if (PlayerPropertyConfig.HasKey(attrId))
                {
                    PlayerPropertyConfig config = PlayerPropertyConfig.Get(attrId);
                    long attrValue = attrDict.ContainsKey(attrId) ? attrDict[attrId] : 0;
                    attrs[i].text = Language.Get("OtherPlayerDetail15", config.Name, PlayerPropertyConfig.GetValueDescription(attrId, attrValue));
                }
                else
                {
                    attrs[i].SetActive(false);
                }
            }
            else
            {
                attrs[i].SetActive(false);
            }
        }
    }
    private void DisplayHeroShow(int heroID, int skinID, int breakLevel, int star, int awakeLevel, int lv)
    {
        heroShow.Init(heroID, skinID, breakLevel, star, awakeLevel, lv);
    }
    private void DisplaySkill(int heroID, int atkSkillID, int angerSkillID)
    {
        if (!HeroConfig.HasKey(heroID) || !SkillConfig.HasKey(atkSkillID) || !SkillConfig.HasKey(angerSkillID))
        {
            skill.SetActive(false);
            return;
        }
        SkillConfig atkSkillIDConfig = SkillConfig.Get(atkSkillID);
        SkillConfig angerSkillIDConfig = SkillConfig.Get(angerSkillID);
        //没有技能图标也不显示技能
        if (string.IsNullOrEmpty(atkSkillIDConfig.IconName) || string.IsNullOrEmpty(angerSkillIDConfig.IconName))
        {
            skill.SetActive(false);
            return;
        }
        skill.SetActive(true);
        normalSkillCell.Init(atkSkillID, () =>
        {
            UIManager.Instance.OpenWindow<HeroSkillWin>(heroID);
        }, true);
        angerSkillCell.Init(angerSkillID, () =>
        {
            UIManager.Instance.OpenWindow<HeroSkillWin>(heroID);
        }, true);
    }
    /// <summary>
    /// 强制刷新Layout,解决嵌套Layout和ContentSizeFitter的重叠问题
    /// </summary>
    async UniTask ForceRefreshLayout()
    {
        await UniTask.DelayFrame(2);
        // 刷新所有Layout组件
        var layouts = allAttrScroll.GetComponentsInChildren<LayoutGroup>(true);
        foreach (var layout in layouts)
        {
            LayoutRebuilder.ForceRebuildLayoutImmediate(layout.GetComponent<RectTransform>());
        }
        await UniTask.DelayFrame(2);
        // 刷新所有Layout组件
        foreach (var layout in layouts)
        {
            LayoutRebuilder.ForceRebuildLayoutImmediate(layout.GetComponent<RectTransform>());
        }
        // Layout刷新完成后重新设置滚动位置
        if (allAttrScroll != null)
            allAttrScroll.verticalNormalizedPosition = 1f;
    }
}
Main/System/OtherPlayerDetail/OtherNPCDetailWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d8178183658a6654cb57052375a17a33
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/OtherPlayerDetail/OtherNpcHeroCell.cs
New file
@@ -0,0 +1,210 @@
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
//武将模型无站台模块
public class OtherNpcHeroCell : MonoBehaviour
{
    UIHeroController m_HeroModel;
    UIHeroController heroModel
    {
        get
        {
            if (m_HeroModel == null)
            {
                m_HeroModel = this.transform.GetComponent<UIHeroController>("Container_HeroShow/rolemodel");
            }
            return m_HeroModel;
        }
    }
    Transform m_StarRect;
    Transform starRect
    {
        get
        {
            if (m_StarRect == null)
            {
                m_StarRect = this.transform.Find("Container_HeroShow/layout/layout/stars");
            }
            return m_StarRect;
        }
    }
    List<Image> m_StarsImg;
    List<Image> starsImg
    {
        get
        {
            if (m_StarsImg == null)
            {
                m_StarsImg = new List<Image>();
                for (int i = 0; i < 5; i++)
                {
                    var star = this.transform.GetComponent<Image>("Container_HeroShow/layout/layout/stars/star" + i);
                    m_StarsImg.Add(star);
                }
            }
            return m_StarsImg;
        }
    }
    Image m_LockState;
    Image lockState
    {
        get
        {
            if (m_LockState == null)
            {
                m_LockState = this.transform.GetComponent<Image>("Container_HeroShow/lockstate");
            }
            return m_LockState;
        }
    }
    Text m_Name;
    Text nameText
    {
        get
        {
            if (m_Name == null)
            {
                m_Name = this.transform.GetComponent<Text>("Container_HeroShow/layout/name");
            }
            return m_Name;
        }
    }
    Text m_LvText;  //武将等级 + 觉醒等级
    Text lvText
    {
        get
        {
            if (m_LvText == null)
            {
                m_LvText = this.transform.GetComponent<Text>("Container_HeroShow/layout/layout/lv");
            }
            return m_LvText;
        }
    }
    OutlineEx m_LvTextOutline;
    OutlineEx heroLVOutline
    {
        get
        {
            if (m_LvTextOutline == null)
            {
                m_LvTextOutline = this.transform.GetComponent<OutlineEx>("Container_HeroShow/layout/layout/lv");
            }
            return m_LvTextOutline;
        }
    }
    Button m_StarBtn;
    Button starBtn
    {
        get
        {
            if (m_StarBtn == null)
            {
                m_StarBtn = this.transform.GetComponent<Button>("Container_HeroShow/layout/layout/stars");
            }
            return m_StarBtn;
        }
    }
    void Awake()
    {
        LoadPrefab();
    }
    // 武将模型站台显示
    public void Init(int heroID, int skinID, int breakLevel = 0, int star = 0, int awakelv = 0, int lv = 0, bool isShowLock = false)
    {
        LoadPrefab();   //存在被卸载的可能,重新加载
        var heroConfig = HeroConfig.Get(heroID);
        heroModel.Create(skinID, 1.2f);
        if (star == 0)
        {
            starRect.SetActive(false);
        }
        else
        {
            starRect.SetActive(true);
            for (int i = 0; i < starsImg.Count; i++)
            {
                if ((star - 1) % starsImg.Count >= i)
                {
                    starsImg[i].SetActive(true);
                    starsImg[i].SetSprite("herostar" + (((star - 1) / starsImg.Count) + 1) * starsImg.Count);
                }
                else
                {
                    starsImg[i].SetActive(false);
                }
            }
        }
        lockState.SetActive(isShowLock);
        nameText.text = breakLevel == 0 ? heroConfig.Name : Language.Get("herocardbreaklv", heroConfig.Name, breakLevel);
        nameText.color = UIHelper.GetUIColorByFunc(heroConfig.Quality);
        if (lv == 0)
        {
            lvText.SetActive(false);
        }
        else
        {
            lvText.SetActive(true);
            if (awakelv == 0)
            {
                lvText.text = string.Format("{0}{1}", Language.Get("L1094"), lv);
            }
            else
            {
                lvText.text = string.Format("{0}{1} {2}", Language.Get("L1094"), lv, Language.Get("herocard12", awakelv));
            }
            heroLVOutline.colorType = awakelv == 0 ? QualityTextColType.None : QualityTextColType.red;
        }
        starBtn.AddListener(() =>
        {
            SmallTipWin.showText = Language.Get("HeroGift14", star);
            SmallTipWin.worldPos = CameraManager.uiCamera.ScreenToWorldPoint(Input.mousePosition);
            UIManager.Instance.OpenWindow<SmallTipWin>();
        });
    }
    GameObject cellContainer;
    protected void LoadPrefab()
    {
        if (cellContainer != null)
            return;
        var tmp = transform.Find("Container_HeroShow");
        if (tmp != null)
        {
            cellContainer = tmp.gameObject;
            return;
        }
        if (cellContainer == null)
        {
            cellContainer = UIUtility.CreateWidget("OtherNpcHeroCell", "Container_HeroShow");
            if (cellContainer != null)
            {
                cellContainer.transform.SetParentEx(this.transform, Vector3.zero, Quaternion.identity, Vector3.one);
                cellContainer.transform.SetAsFirstSibling();
            }
        }
    }
}
Main/System/OtherPlayerDetail/OtherNpcHeroCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a237531ac4d059148a7e71e9a8e1a4f8
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/OtherPlayerDetail/OtherPlayerDetailManager.cs
@@ -8,6 +8,26 @@
//查看其他玩家的简短信息, 该模块处理数据,其他由各自功能模块处理 通过OnRevPackage
public class OtherPlayerDetailManager : GameSystemManager<OtherPlayerDetailManager>
{
    public int viewPlayer { get; private set; }
    // 查询类型:根据类型做不同处理
    // 按功能自定义枚举值 EnumHelper的 ViewPlayerType
    public int viewPlayerType { get; private set; }
    public int viewPreSetType { get; private set; }
    private Dictionary<int, ViewPlayerData> viewPlayerDataDic = new Dictionary<int, ViewPlayerData>();
    // 查看类型,玩家ID
    public event Action<int, int> OnRevPackage; //尽量不要用第一个参数viewtype做判断,容易出错
    public int viewHeroType;//0:战场点击模型 1:查看他人英雄详情界面点击英雄卡牌
    public BattleClickHeroData clickHeroData;
    public ViewNPCAttr viewNPCAttr;
    public RolePlusData.HeroData heroData;
    public List<RolePlusData.HeroData> heroDatas;
    public RolePlusData.EquipData equip;
    public override void Init()
    {
@@ -22,54 +42,39 @@
    public void OnBeforePlayerDataInitializeEventOnRelogin()
    {
        viewPlayer = -1;
        viewPlayerType = -1;
        viewPlayerLineupType = -1;
        viewPreSetType = -1;
    }
    public int viewPlayer { get; private set; }
    // 查询类型:根据类型做不同处理
    // 按功能自定义枚举值 EnumHelper的 ViewPlayerType
    public int viewPlayerType { get; private set; }
    public int viewPlayerLineupType { get; private set; }
    public RolePlusData.HeroData heroData;
    public List<RolePlusData.HeroData> heroDatas;
    public RolePlusData.EquipData equip;
    private Dictionary<int, ViewPlayerData> viewPlayerDataDic = new Dictionary<int, ViewPlayerData>();
    // 查看类型,玩家ID
    public event Action<int, int> OnRevPackage; //尽量不要用第一个参数viewtype做判断,容易出错
    // 获取其他玩家本地缓存数据
    public ViewPlayerData GetViewPlayerData(int player)
    public ViewPlayerData GetViewPlayerData(int playerId)
    {
        ViewPlayerData viewPlayerData = null;
        viewPlayerDataDic.TryGetValue(player, out viewPlayerData);
        viewPlayerDataDic.TryGetValue(playerId, out viewPlayerData);
        return viewPlayerData;
    }
    // 向服务端请求其他玩家数据
    public void ViewPlayerDetail(int _playerId, int viewType = (int)ViewPlayerType.viewPlayerData, int viewPlayerLineupType = (int)BattlePreSetType.Story)
    public void ViewPlayerDetail(int playerId, int viewType = (int)ViewPlayerType.viewPlayerData, int viewBattlePreSetType = (int)BattlePreSetType.Story)
    {
        if (_playerId == PlayerDatas.Instance.baseData.PlayerID)
        {
        // 自己不能查看自己的信息
        if (playerId == PlayerDatas.Instance.baseData.PlayerID)
            return;
        }
        viewPlayerType = viewType;
        this.viewPlayerLineupType = viewPlayerLineupType;
        ViewRoleParticulars(_playerId);
        viewPreSetType = viewBattlePreSetType;
        ViewRoleParticulars(playerId);
    }
    // 向服务端请求玩家数据
    void ViewRoleParticulars(int playerID)
    {
        if (playerID == 0)
        {
        if (playerID <= 0)
            return;
        }
        viewPlayer = playerID;
        if (viewPlayerDataDic.ContainsKey(playerID))
        {
            ViewPlayerData viewPlayerData = viewPlayerDataDic[playerID];
@@ -80,34 +85,10 @@
            }
        }
        //CC002_tagCGViewCrossPlayerInfo c002 = new CC002_tagCGViewCrossPlayerInfo();
        CA212_tagCMViewPlayerInfo pak = new CA212_tagCMViewPlayerInfo();
        pak.PlayerID = (uint)playerID;
        GameNetSystem.Instance.SendInfo(pak);
    }
    //本服竞技场假查询模拟封包,返回查看机器人数据 打开RoleParticularsWin界面,专用接口其他地方不要调用
    public void ViewFairyArenaRobot(int playerID, ViewPlayerData _viewPlayerData)
    {
        ViewPlayerData viewPlayerData = null;
        viewPlayer = playerID;
        if (!viewPlayerDataDic.TryGetValue(playerID, out viewPlayerData))
        {
            viewPlayerDataDic.Add(playerID, _viewPlayerData);
        }
        else
        {
            viewPlayerDataDic[playerID] = _viewPlayerData;
        }
        viewPlayerType = (int)ViewPlayerType.viewPlayerData;
        ShowRoleParticulars(playerID);
    }
    public void OnRevRoleEquip(HA705_tagSCQueryPlayerCacheResult package)
    {
@@ -159,15 +140,46 @@
        {
            if (!UIManager.Instance.IsOpened<OtherPlayerDetailWin>())
            {
                UIManager.Instance.OpenWindow<OtherPlayerDetailWin>(viewPlayerLineupType);
                UIManager.Instance.OpenWindow<OtherPlayerDetailWin>(viewPreSetType);
            }
        }
        OnRevPackage?.Invoke(viewPlayerType, playerID);
        viewPlayerType = -1;
        viewPlayerLineupType = -1;
        viewPreSetType = -1;
    }
    #region 获取PlusData中的数据
    /// <summary>
    /// 获取特定战斗环境下的命格方案详情
    /// </summary>
    public RolePlusData.MinggePresetData GetMinggePresetByBattleType(int playerID, int battleType)
    {
        int presetID = GetFuncPresetID(playerID, battleType, (int)FuncPresetType.Mingge);
        var minggeData = GetMinggeTotalData(playerID);
        if (minggeData == null || minggeData.PresetDic == null)
            return null;
        // 取出对应的方案数据
        if (minggeData.PresetDic.TryGetValue(presetID, out var data))
        {
            return data;
        }
        // 如果找不到该方案ID(兼容性),尝试取默认方案 ID=1
        if (presetID != 1 && minggeData.PresetDic.TryGetValue(1, out var defaultData))
        {
            return defaultData;
        }
        return null;
    }
    public RolePlusData.MinggeData GetMinggeTotalData(int playerID)
    {
        ViewPlayerData viewPlayerData = GetViewPlayerData(playerID);
        return viewPlayerData?.rolePlusData?.minggeData;
    }
    public int GetFuncPresetID(int playerID, int battleType, int funcType)
    {
@@ -362,6 +374,13 @@
        return viewPlayerData?.rolePlusData?.horseData;
    }
    // 获取古宝数据对象
    public RolePlusData.GubaoData GetGubaoData(int playerID)
    {
        ViewPlayerData viewPlayerData = GetViewPlayerData(playerID);
        return viewPlayerData?.rolePlusData?.gubaoData;
    }
    #endregion
    #region 解析PlusData
    public class ViewPlayerData
@@ -395,6 +414,8 @@
        // 阵容字典 <阵容ID, 阵容数据>
        public Dictionary<int, LineupData> LineupDic = new Dictionary<int, LineupData>();
        //<战斗预设类型,<预设类型,使用的方案ID>>
        public Dictionary<int, Dictionary<int, int>> BatPresetDic = new Dictionary<int, Dictionary<int, int>>();
        // 红颜数据
@@ -402,6 +423,12 @@
        // 坐骑数据
        public HorseData horseData;
        // 古宝数据
        public GubaoData gubaoData;
        // 命格数据
        public MinggeData minggeData;
        // 红颜数据类
        public class BeautyData
@@ -423,6 +450,27 @@
        {
            public int ItemID;
            public Dictionary<int, List<int>> UserData = new Dictionary<int, List<int>>();
        }
        // 古宝数据类
        public class GubaoData
        {
            public int Cnt; // 已解锁数
            public Dictionary<int, long> AttrDict = new Dictionary<int, long>(); // 属性
        }
        public class MinggeData
        {
            public int GWLV; // 感悟等级
            public Dictionary<int, MinggePresetData> PresetDic = new Dictionary<int, MinggePresetData>(); // <预设ID, 预设详情>
        }
        public class MinggePresetData
        {
            // 命格技能 <意象技能ID, 等级>
            public Dictionary<int, int> SkillDic = new Dictionary<int, int>();
            // 命格属性 <属性ID, 值>
            public Dictionary<int, long> AttrDict = new Dictionary<int, long>();
        }
        // 阵容数据类
@@ -490,6 +538,9 @@
                BatPresetDic.Clear();
                beautyData = null; // 必须置空,防止显示上一个玩家的数据
                horseData = null;  // 必须置空
                gubaoData = null;  // 必须置空
                minggeData = null;
                // 1. 转为 JsonData 对象
                JsonData jd = JsonMapper.ToObject(jsonStr);
@@ -514,6 +565,20 @@
                        }
                    }
                }
                // 【新增】解析古宝 (Gubao)
                if (jd.Keys.Contains("Gubao"))
                {
                    JsonData gubaoJd = jd["Gubao"];
                    gubaoData = new GubaoData();
                    if (gubaoJd.Keys.Contains("Cnt"))
                        gubaoData.Cnt = (int)gubaoJd["Cnt"];
                    // 解析属性
                    gubaoData.AttrDict = ParseAttrDict(gubaoJd, "Attr");
                }
                // 【新增】解析红颜 (Beauty)
                if (jd.Keys.Contains("Beauty"))
                {
@@ -624,6 +689,52 @@
                    }
                }
                // 【新增】解析命格 (Mingge)
                if (jd.Keys.Contains("Mingge"))
                {
                    JsonData minggeJd = jd["Mingge"];
                    minggeData = new MinggeData();
                    // 1. 解析感悟等级
                    if (minggeJd.Keys.Contains("GWLV"))
                        minggeData.GWLV = (int)minggeJd["GWLV"];
                    // 2. 解析预设 (Preset)
                    if (minggeJd.Keys.Contains("Preset"))
                    {
                        JsonData presetRootJd = minggeJd["Preset"];
                        foreach (string key in presetRootJd.Keys)
                        {
                            // key 为命格预设ID
                            if (int.TryParse(key, out int presetId))
                            {
                                JsonData singlePresetJd = presetRootJd[key];
                                MinggePresetData presetData = new MinggePresetData();
                                // 解析属性 (Attr)
                                presetData.AttrDict = ParseAttrDict(singlePresetJd, "Attr");
                                // 解析技能 (Skill) - {"SkillID": level, ...}
                                if (singlePresetJd.Keys.Contains("Skill"))
                                {
                                    JsonData skillJd = singlePresetJd["Skill"];
                                    foreach (string skillKey in skillJd.Keys)
                                    {
                                        if (int.TryParse(skillKey, out int skillId))
                                        {
                                            if (int.TryParse(skillJd[skillKey].ToString(), out int level))
                                            {
                                                presetData.SkillDic[skillId] = level;
                                            }
                                        }
                                    }
                                }
                                minggeData.PresetDic[presetId] = presetData;
                            }
                        }
                    }
                }
            }
            catch (Exception e)
Main/System/OtherPlayerDetail/OtherPlayerDetailWin.cs
@@ -28,6 +28,10 @@
    [SerializeField] ButtonEx btnReport;
    [SerializeField] ButtonEx btnAddFriend;
    [SerializeField] ButtonEx btnPrivateChat;
    [SerializeField] ButtonEx btnMingge;
    [SerializeField] TextEx txtMinggeLv;
    [SerializeField] ButtonEx btnGubao;
    [SerializeField] TextEx txtGubaoLv;
    OtherPlayerDetailManager.ViewPlayerData viewPlayerData;
    OtherPlayerDetailManager manager { get { return OtherPlayerDetailManager.Instance; } }
@@ -40,12 +44,14 @@
        btnGuild.SetListener(OnClickGuild);
        btnMM.SetListener(OnClickMM);
        btnHorse.SetListener(OnClickHorse);
        btnMingge.SetListener(OnClickMingge);
        btnGubao.SetListener(OnClickGubao);
    }
    protected override void OnPreOpen()
    {
        GuildManager.Instance.OnRefreshFairyList += OnRefreshFairyList;
        funcPresetID = OtherPlayerDetailManager.Instance.GetFuncPresetID(manager.viewPlayer, functionOrder, (int)FuncPresetType.Team);
        funcPresetID = OtherPlayerDetailManager.Instance.GetFuncPresetID(manager.viewPlayer, functionOrder, (int)FuncPresetType.Global);
        Display();
    }
@@ -89,7 +95,6 @@
        int playerID = viewPlayerData.PlayerID;
        DisplayMM(playerID);
        var heroList = manager.GetHeroDataSortList(playerID, funcPresetID);
        if (heroList.IsNullOrEmpty())
        {
@@ -110,13 +115,29 @@
        var equipDict = manager.GetEquipDataDict(playerID);
        DisplayEquip(equipDict);
        DisplayGubao(playerID);
        DisplayMingge(playerID);
    }
    private void DisplayMM(int playerID)
    {
        OtherPlayerDetailManager.RolePlusData.BeautyData beautyData = manager.GetBeautyData(playerID);
        txtMMCnt.text = Language.Get("OtherPlayerDetail08", beautyData == null ? 0 : beautyData.Cnt);
        txtMMCnt.text = Language.Get("OtherPlayerDetail11", beautyData == null ? 0 : beautyData.Cnt);
    }
    private void DisplayGubao(int playerID)
    {
        OtherPlayerDetailManager.RolePlusData.GubaoData gubaoData = manager.GetGubaoData(playerID);
        txtGubaoLv.text = Language.Get("OtherPlayerDetail12", gubaoData == null ? 0 : gubaoData.Cnt);
    }
    private void DisplayMingge(int playerID)
    {
        var mingge = manager.GetMinggeTotalData(playerID);
        txtMinggeLv.text = Language.Get("OtherPlayerDetail13", mingge == null ? 0 : mingge.GWLV);
    }
    private void DisplayGuildInfo(OtherPlayerDetailManager.ViewPlayerData viewPlayerData)
    {
        if (viewPlayerData.FamilyEmblemID <= 0 || string.IsNullOrEmpty(viewPlayerData.FamilyEmblemWord))
@@ -259,7 +280,7 @@
        int playerID = viewPlayerData.PlayerID;
        OtherPlayerDetailManager.RolePlusData.BeautyData beautyData = manager.GetBeautyData(playerID);
        AttributeManager.Instance.OpenTotalAttributeWin(beautyData == null || beautyData.AttrDict == null ? new Dictionary<int, long>() : beautyData.AttrDict);
        AttributeManager.Instance.OpenSimpleAttributeWin(beautyData == null || beautyData.AttrDict == null ? new Dictionary<int, long>() : beautyData.AttrDict, "AttributeTitle01");
    }
    private void OnClickHorse()
    {
@@ -273,6 +294,31 @@
        {
            return;
        }
        AttributeManager.Instance.OpenTotalAttributeWin(horseData.AttrDict == null ? new Dictionary<int, long>() : horseData.AttrDict);
        AttributeManager.Instance.OpenSimpleAttributeWin(horseData.AttrDict == null ? new Dictionary<int, long>() : horseData.AttrDict, "AttributeTitle04");
    }
    private void OnClickMingge()
    {
        if (viewPlayerData == null)
            return;
        int playerID = viewPlayerData.PlayerID;
        // 根据当前入口的战斗预设类型获取命格预设数据
        var minggePresetData = manager.GetMinggePresetByBattleType(playerID, functionOrder);
        // 获取属性字典,如果为空则使用空字典
        var attrDict = minggePresetData?.AttrDict ?? new Dictionary<int, long>();
        var skillDict = minggePresetData?.SkillDic ?? new Dictionary<int, int>();
        // 打开属性窗口
        AttributeManager.Instance.OpenMinggeSimpleAttributeWin(attrDict, skillDict);
    }
    private void OnClickGubao()
    {
        if (viewPlayerData == null)
            return;
        int playerID = viewPlayerData.PlayerID;
        OtherPlayerDetailManager.RolePlusData.GubaoData gubaoData = manager.GetGubaoData(playerID);
        AttributeManager.Instance.OpenSimpleAttributeWin(gubaoData == null || gubaoData.AttrDict == null ? new Dictionary<int, long>() : gubaoData.AttrDict, "AttributeTitle02");
    }
}
Main/System/PhantasmPavilion/PhantasmPavilionFaceWin.cs
@@ -69,7 +69,7 @@
        btnFace.AddListener(() => { SelectTeamFunc(PhantasmPavilionType.Face, true); });
        btnFacePic.AddListener(() => { SelectTeamFunc(PhantasmPavilionType.FacePic, true); });
        btnChatBox.AddListener(() => { SelectTeamFunc(PhantasmPavilionType.ChatBox, true); });
        btnAllAdd.AddListener(() => { AttributeManager.Instance.OpenTotalAttributeWin(manager.GetTotalAttr()); });
        btnAllAdd.AddListener(() => { AttributeManager.Instance.OpenSimpleAttributeWin(manager.GetTotalAttr()); });
    }
    protected override void OnPreOpen()
Main/System/PhantasmPavilion/PhantasmPavilionModelWin.cs
@@ -45,7 +45,7 @@
        base.InitComponent();
        btnTabType1.AddListener(() => { SelectTeamFunc(PhantasmPavilionType.Model, 0, true); });
        btnTabType2.AddListener(() => { SelectTeamFunc(PhantasmPavilionType.Model, 1, true); });
        btnAllAdd.AddListener(() => { AttributeManager.Instance.OpenTotalAttributeWin(manager.GetTotalAttr()); });
        btnAllAdd.AddListener(() => { AttributeManager.Instance.OpenSimpleAttributeWin(manager.GetTotalAttr()); });
    }
    protected override void OnPreOpen()
Main/System/PhantasmPavilion/PhantasmPavilionTilteWin.cs
@@ -51,7 +51,7 @@
        btnTakeOff3.AddListener(() => { manager.SendOPPack(PhantasmPavilionType.Title, PhantasmPavilionOperation.Remove, (uint)manager.selectId); });
        btnTabType1.AddListener(() => { SelectTeamFunc(PhantasmPavilionType.Title, 0); });
        btnTabType2.AddListener(() => { SelectTeamFunc(PhantasmPavilionType.Title, 1); });
        btnAllAdd.AddListener(() => { AttributeManager.Instance.OpenTotalAttributeWin(manager.GetTotalAttr()); });
        btnAllAdd.AddListener(() => { AttributeManager.Instance.OpenSimpleAttributeWin(manager.GetTotalAttr()); });
    }
    protected override void OnPreOpen()
Main/System/ViewNPC/ViewNPCManager.cs
@@ -5,21 +5,145 @@
public class ViewNPCManager : GameSystemManager<ViewNPCManager>
{
    // 缓存字典结构: MapID -> FuncLineID -> NPCID -> Attr
    public Dictionary<uint, Dictionary<uint, Dictionary<uint, ViewNPCAttr>>> dict = new Dictionary<uint, Dictionary<uint, Dictionary<uint, ViewNPCAttr>>>();
    private BattleClickHeroData pendingData; // 用于记录当前正在请求查看的 NPC 信息,以便回包时自动打开界面 ---
    private bool isPendingOpen = false; // 是否是点击触发的请求(需要自动打开界面)
    OtherPlayerDetailManager otherPlayerManager { get { return OtherPlayerDetailManager.Instance; } }
    public override void Init()
    {
        DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin += OnBeforePlayerDataInitializeEventOnRelogin;
        EventBroadcast.Instance.AddListener<BattleClickHeroData>(EventName.BATTLE_CLICK_HERO, OnBattleClickHero);
        OtherPlayerDetailManager.Instance.OnRevPackage += OnRevPackage;
    }
    public override void Release()
    {
        DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin -= OnBeforePlayerDataInitializeEventOnRelogin;
        EventBroadcast.Instance.RemoveListener<BattleClickHeroData>(EventName.BATTLE_CLICK_HERO, OnBattleClickHero);
        OtherPlayerDetailManager.Instance.OnRevPackage -= OnRevPackage;
    }
    private void OnBeforePlayerDataInitializeEventOnRelogin()
    {
        dict.Clear();
        ResetPendingState();
    }
    private void ResetPendingState()
    {
        isPendingOpen = false;
        pendingData.isMySide = false;
        pendingData.funcLineID = 0;
        pendingData.mapID = 0;
        pendingData.npcID = 0;
        pendingData.teams = null;
    }
    private void OnBattleClickHero(BattleClickHeroData data)
    {
        pendingData = data;
        // 主线不显示NPC信息
        if (data.mapID == 1)
            return;
        // 自己的数据直接读背包
        if (data.isMySide)
        {
            OpenNPCDetailWin(pendingData, null);
            return;
        }
        // pvp的走查看玩家的
        if (BattleManager.Instance.IsPvpBattle(data.battleName))
        {
            switch (data.battleName)
            {
                case BattleConst.ArenaBattleField:
                    int playerId = (int)ArenaManager.Instance.atkPlayerId;
                    OtherPlayerDetailManager.Instance.ViewPlayerDetail(playerId, (int)ViewPlayerType.viewArenaBattleEnemyHero, (int)BattlePreSetType.Arena);
                    break;
            }
            return;
        }
        // 非pvp的走查看npc的
        // 尝试直接从缓存获取数据
        if (TryGetNPCAttr((uint)data.mapID, (uint)data.funcLineID, (uint)data.npcID, out ViewNPCAttr attr))
        {
            OpenNPCDetailWin(pendingData, attr);
        }
        else
        {
            isPendingOpen = true;
            SendViewNPCAttr(data);
        }
    }
    private void OnRevPackage(int viewPlayerType, int playerID)
    {
        switch (viewPlayerType)
        {
            case (int)ViewPlayerType.viewArenaBattleEnemyHero:
                ViewArenaBattleEnemyHero(playerID);
                break;
        }
    }
    private void ViewArenaBattleEnemyHero(int playerID)
    {
        int presetID = otherPlayerManager.GetFuncPresetID(playerID, OtherPlayerDetailManager.Instance.viewPreSetType, (int)FuncPresetType.Global);
        var heroList = otherPlayerManager.GetHeroDataSortList(playerID, presetID);
        if (heroList.IsNullOrEmpty())
        {
            heroList = otherPlayerManager.GetHeroDataSortList(playerID, FuncPresetManager.FuncDefaultPresetID);
        }
        var heroData = otherPlayerManager.GetHeroDataDict(playerID, presetID);
        if (heroList.IsNullOrEmpty())
        {
            heroList = otherPlayerManager.GetHeroDataSortList(playerID, FuncPresetManager.FuncDefaultPresetID);
        }
        if (heroList == null || heroData == null)
            return;
        int posNum = pendingData.posNum + 1;
        if (!heroData.ContainsKey(posNum))
            return;
        OtherPlayerDetailManager.Instance.viewHeroType = 0;
        OtherPlayerDetailManager.Instance.clickHeroData = pendingData;
        OtherPlayerDetailManager.Instance.heroData = heroData[posNum];
        OtherPlayerDetailManager.Instance.heroDatas = heroList;
        if (!UIManager.Instance.IsOpened<OtherHeroDetailWin>())
        {
            UIManager.Instance.OpenWindow<OtherHeroDetailWin>();
        }
    }
    private void OpenNPCDetailWin(BattleClickHeroData data, ViewNPCAttr npcAttr)
    {
        OtherPlayerDetailManager.Instance.viewHeroType = 0;
        OtherPlayerDetailManager.Instance.clickHeroData = data;
        OtherPlayerDetailManager.Instance.viewNPCAttr = npcAttr;
        if (data.isMySide)
        {
            if (!UIManager.Instance.IsOpened<OtherHeroDetailWin>())
            {
                UIManager.Instance.OpenWindow<OtherHeroDetailWin>();
            }
        }
        else
        {
            if (!UIManager.Instance.IsOpened<OtherNPCDetailWin>())
            {
                UIManager.Instance.OpenWindow<OtherNPCDetailWin>();
            }
        }
    }
    // --- 解析属性字典 ---
    private Dictionary<int, long> ParseAttrDict(string jsonStr)
    {
@@ -34,18 +158,12 @@
                if (int.TryParse(attrKey, out int attrId))
                {
                    JsonData v = jd[attrKey];
                    long val = 0;
                    // 健壮性数值转换
                    if (v.IsLong) val = (long)v;
                    else if (v.IsInt) val = (long)(int)v;
                    else if (v.IsDouble) val = (long)(double)v;
                    else if (v.IsString) long.TryParse(v.ToString(), out val);
                    long.TryParse(v.ToString(), out long val);
                    result[attrId] = val;
                }
            }
        }
        catch (System.Exception e)
        catch (Exception e)
        {
            Debug.LogWarning($"解析NPC属性JSON失败: {e.Message}");
        }
@@ -53,8 +171,10 @@
    }
    public event Action<uint, uint> OnUpdateViewNPCAttrRet;
    public void UpdateViewNPCAttrRet(HB432_tagSCViewNPCAttrRet vNetData)
    {
        // 1. 更新字典数据
        if (!dict.ContainsKey(vNetData.MapID))
        {
            dict[vNetData.MapID] = new Dictionary<uint, Dictionary<uint, ViewNPCAttr>>();
@@ -102,8 +222,27 @@
                };
            }
        }
        // 检查是否需要自动打开界面 (Pending 逻辑)
        if (isPendingOpen)
        {
            // 检查回包是否对应当前的请求 (MapID 和 FuncLineID 匹配)
            if (vNetData.MapID == pendingData.mapID && vNetData.FuncLineID == pendingData.funcLineID)
            {
                if (TryGetNPCAttr((uint)pendingData.mapID, (uint)pendingData.funcLineID, (uint)pendingData.npcID, out ViewNPCAttr targetAttr))
                {
                    OpenNPCDetailWin(pendingData, targetAttr);
                }
                // 处理完毕,重置状态
                ResetPendingState();
            }
            return;
        }
        OnUpdateViewNPCAttrRet?.Invoke(vNetData.MapID, vNetData.FuncLineID);
    }
    public bool TryGetNPCAttr(uint mapID, uint funcLineID, uint npcID, out ViewNPCAttr npcAttr)
@@ -121,6 +260,11 @@
        }
        return funcLineDict.TryGetValue(npcID, out npcAttr);
    }
    public void SendViewNPCAttr(BattleClickHeroData data)
    {
        SendViewNPCAttr((uint)data.mapID, (uint)data.funcLineID, (uint)data.npcID);
    }
    public void SendViewNPCAttr(uint mapID, uint funcLineID, uint viewNPCID)
@@ -143,4 +287,16 @@
    public byte BreakLV;        // 突破
    public byte AwakeLV;        // 觉醒
    public Dictionary<int, long> AttrDict = new Dictionary<int, long>(); // 属性;
}
public struct BattleClickHeroData
{
    public string battleName;
    public bool isMySide;
    public int heroID;
    public int funcLineID;
    public int mapID;
    public int npcID;
    public int posNum;
    public List<BattleObject> teams;
}
Main/System/WarlordPavilion/WarlordPavilionManager.cs
@@ -135,8 +135,7 @@
    public bool IsFuncOpen(bool isTip = false)
    {
        if (!TryGetHistoryMaxFinishProgress(out int layerNum, out int levelNum))
            return false;
        TryGetHistoryMaxFinishProgress(out int layerNum, out int levelNum);
        int lv = PlayerDatas.Instance.baseData.LV;
        bool isOpen = false;
Main/System/WarlordPavilion/WarlordPavilionWin.cs
@@ -91,7 +91,6 @@
    protected override void OnPreOpen()
    {
        GlobalTimeEvent.Instance.secondEvent += OnSecondEvent;
        ViewNPCManager.Instance.OnUpdateViewNPCAttrRet += OnUpdateViewNPCAttrRet;
        manager.OnUpdateDingjungeInfoEvent += OnUpdateDingjungeInfo;
        Display();
    }
@@ -99,7 +98,6 @@
    protected override void OnPreClose()
    {
        GlobalTimeEvent.Instance.secondEvent -= OnSecondEvent;
        ViewNPCManager.Instance.OnUpdateViewNPCAttrRet -= OnUpdateViewNPCAttrRet;
        manager.OnUpdateDingjungeInfoEvent -= OnUpdateDingjungeInfo;
    }
Main/Utility/EnumHelper.cs
@@ -1841,5 +1841,5 @@
{
    viewPlayerData = 0,  //查看玩家基本信息,公用打开界面
    viewGuildLeader = 1,  //查看玩家的公会族长信息
    viewArenaBattleEnemyHero = 2,  //查看竞技场战斗敌方英雄信息
}