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;
}