using System; using System.Collections.Generic; using UnityEngine; using LitJson; public class ViewNPCManager : GameSystemManager { // 缓存字典结构: MapID -> FuncLineID -> NPCID -> Attr public Dictionary>> dict = new Dictionary>>(); 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(EventName.BATTLE_CLICK_HERO, OnBattleClickHero); OtherPlayerDetailManager.Instance.OnRevPackage += OnRevPackage; } public override void Release() { DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin -= OnBeforePlayerDataInitializeEventOnRelogin; EventBroadcast.Instance.RemoveListener(EventName.BATTLE_CLICK_HERO, OnBattleClickHero); OtherPlayerDetailManager.Instance.OnRevPackage -= OnRevPackage; } private void OnBeforePlayerDataInitializeEventOnRelogin() { dict.Clear(); ResetPendingState(); } private void ResetPendingState() { isPendingOpen = false; viewNPCAttr = null; 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) { OtherPlayerDetailManager.Instance.OpenOtherHeroDetailWin(new ViewHeroDetailData() { viewHeroType = 0, clickHeroData = data, }); return; } // pvp的走查看玩家的 if (BattleManager.Instance.IsPvpBattle(data.battleName)) { switch (data.battleName) { case BattleConst.ArenaBattleField: int playerId = (int)ArenaManager.Instance.atkPlayerId; OtherPlayerDetailManager.Instance.ViewPlayerDetail(playerId, 0, (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(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.OpenOtherHeroDetailWin(new ViewHeroDetailData() { viewHeroType = 0, clickHeroData = pendingData, heroData = heroData[posNum], heroDatas = heroList, }); } public void TryOpenNPCDetailWin(uint mapID, uint funcLineID, uint npcID) { if (TryGetNPCAttr(mapID, funcLineID, npcID, out ViewNPCAttr attr)) { OpenNPCDetailWin(attr); } } public ViewNPCAttr viewNPCAttr; public void OpenNPCDetailWin(ViewNPCAttr viewNPCAttr) { this.viewNPCAttr = viewNPCAttr; if (!UIManager.Instance.IsOpened()) { UIManager.Instance.OpenWindow(); } } // --- 解析属性字典 --- private Dictionary ParseAttrDict(string jsonStr) { Dictionary result = new Dictionary(); if (string.IsNullOrEmpty(jsonStr)) return result; try { JsonData jd = JsonMapper.ToObject(jsonStr); foreach (string attrKey in jd.Keys) { if (int.TryParse(attrKey, out int attrId)) { JsonData v = jd[attrKey]; long.TryParse(v.ToString(), out long val); result[attrId] = val; } } } catch (Exception e) { Debug.LogWarning($"解析NPC属性JSON失败: {e.Message}"); } return result; } public event Action OnUpdateViewNPCAttrRet; public void UpdateViewNPCAttrRet(HB432_tagSCViewNPCAttrRet vNetData) { // 1. 更新字典数据 if (!dict.ContainsKey(vNetData.MapID)) { dict[vNetData.MapID] = new Dictionary>(); } var mapDict = dict[vNetData.MapID]; if (!mapDict.ContainsKey(vNetData.FuncLineID)) { mapDict[vNetData.FuncLineID] = new Dictionary(); } var funcLineDict = mapDict[vNetData.FuncLineID]; if (vNetData.NPCAttrList != null) { // 使用临时字典按NPCID分组 Dictionary> tempGroup = new Dictionary>(); foreach (var npcAttr in vNetData.NPCAttrList) { uint npcID = npcAttr.NPCID; if (!tempGroup.ContainsKey(npcID)) { tempGroup[npcID] = new List(); } tempGroup[npcID].Add(npcAttr); } // 存入第3层字典 foreach (var kvp in tempGroup) { var firstAttr = kvp.Value[0]; funcLineDict[kvp.Key] = new ViewNPCAttr { PosNum = firstAttr.PosNum, NPCID = firstAttr.NPCID, HeroID = firstAttr.HeroID, LV = firstAttr.LV, Star = firstAttr.Star, BreakLV = firstAttr.BreakLV, AwakeLV = firstAttr.AwakeLV, AttrDict = ParseAttrDict(firstAttr.AttrMsg) }; } } // 检查是否需要自动打开界面 (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(targetAttr); } // 处理完毕,重置状态 ResetPendingState(); } return; } OnUpdateViewNPCAttrRet?.Invoke(vNetData.MapID, vNetData.FuncLineID); } public bool TryGetNPCAttr(uint mapID, uint funcLineID, uint npcID, out ViewNPCAttr npcAttr) { npcAttr = null; if (!dict.TryGetValue(mapID, out var mapDict)) { return false; } if (!mapDict.TryGetValue(funcLineID, out var funcLineDict)) { return false; } 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) { CB416_tagCSViewNPCAttr pack = new CB416_tagCSViewNPCAttr(); pack.MapID = mapID; pack.FuncLineID = funcLineID; pack.ViewNPCID = viewNPCID; GameNetSystem.Instance.SendInfo(pack); } } public class ViewNPCAttr { public byte PosNum; // 在本阵容中的站位,从1开始 public uint NPCID; // 战斗NPCID,不同的实例ID对应的NPCID可能一样 public uint HeroID; // 武将ID,玩家或NPC均可能有,如果有值则外观相关以该武将为准,否则以NPCID为准 public ushort LV; // 等级,玩家的武将等级或NPC成长等级,等级显示以该值为准 public byte Star; // 星级 public byte BreakLV; // 突破 public byte AwakeLV; // 觉醒 public Dictionary AttrDict = new Dictionary(); // 属性; } 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 teams; }