449 查看他人-客户端 新增命格、古宝信息,战场点击查看双方武将或npc信息
19个文件已修改
14个文件已添加
1758 ■■■■■ 已修改文件
Main/Common/EventName.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | 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/Battle/BattleObject/BattleObject.cs 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/BeautyMM/BeautyMMListWin.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Gubao/GubaoListWin.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Horse/HorseSkinWin.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/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/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/Battle/BattleObject/BattleObject.cs
@@ -99,6 +99,44 @@
        heroInfoBar = heroGo.GetComponentInChildren<BattleHeroInfoBar>(true);
        heroInfoBar.SetBattleObject(this);
        //用于点击武将时触发自定义回调
        //主线和天子的挑战以外的战场,点击武将可以显示武将信息
        if (battleField.ToString() != BattleConst.StoryBattleField &&
            battleField.ToString() != BattleConst.TianziBillboradBattleField)
        {
            var hitArea = heroGo.GetComponent<Image>();
            if (hitArea == null)
            {
                hitArea = heroGo.AddComponent<Image>();
                hitArea.color = new Color(0, 0, 0, 0); //让射线检测到
            }
            var btn = heroGo.GetComponent<ButtonEx>();
            if (btn == null)
            {
                btn = heroGo.AddComponent<ButtonEx>();
                btn.interval = 0.5f;// 防止频繁连续点击
            }
            btn.AddListener(() =>
            {
                // 判断点击的是我方阵营还是敌方阵营,目前左边都是我方阵营
                bool isMySide = Camp == BattleCamp.Red;
                EventBroadcast.Instance.Broadcast(EventName.BATTLE_CLICK_HERO, new BattleClickHeroData()
                {
                    battleName = battleField.ToString(),
                    isMySide = isMySide,
                    mapID = battleField.MapID,
                    funcLineID = battleField.FuncLineID,
                    npcID = teamHero.NPCID,
                    posNum = teamHero.positionNum,
                    heroID = teamHero.heroId,
                    teams = battleField?.battleObjMgr.GetBattleObjList(isMySide ? BattleCamp.Red : BattleCamp.Blue),
                });
            });
        }
        //  根据阵营翻转血条
        var heroInfoBarScale = heroInfoBar.transform.localScale;
        heroInfoBarScale.x *= Camp == BattleCamp.Red ? 1 : -1;
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/Gubao/GubaoListWin.cs
@@ -32,7 +32,7 @@
        });
        seeAttrBtn.AddListener(() =>
        {
            AttributeManager.Instance.OpenTotalAttributeWin(GubaoManager.Instance.gubaoAllAttrDict);
            AttributeManager.Instance.OpenSimpleAttributeWin(GubaoManager.Instance.gubaoAllAttrDict, "AttributeTitle02");
        });
    }
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/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/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,  //查看竞技场战斗敌方英雄信息
}