| using System.Collections.Generic; | 
| using UnityEngine; | 
| using LitJson; | 
| using System; | 
|   | 
| public class BattleManager : GameSystemManager<BattleManager> | 
| { | 
|     public StoryBattleField storyBattleField = null; | 
|   | 
|     //  同时只能有一场战斗在进行 guid, battlefield | 
|     protected Dictionary<string, BattleField> battleFields = new Dictionary<string, BattleField>(); | 
|   | 
|     protected LogicUpdate logicUpdate = new LogicUpdate(); | 
|   | 
|     public Action<string, BattleField> onBattleFieldCreate; | 
|   | 
|     public Action<string, BattleField> onBattleFieldDestroy; | 
|   | 
|     public override void Init() | 
|     { | 
|         base.Init(); | 
|         logicUpdate.Start(Run); | 
|         DTC0403_tagPlayerLoginLoadOK.playerLoginOkEvent += OnPlayerLoginOk; | 
|     } | 
|   | 
|     public override void Release() | 
|     { | 
|         base.Release(); | 
|         logicUpdate.Destroy(); | 
|         DTC0403_tagPlayerLoginLoadOK.playerLoginOkEvent -= OnPlayerLoginOk; | 
|     } | 
|   | 
|     protected void OnPlayerLoginOk() | 
|     { | 
|         long exAttr1 = PlayerDatas.Instance.GetPlayerDataByType(PlayerDataType.ExAttr1); | 
|         long exAttr2 = PlayerDatas.Instance.GetPlayerDataByType(PlayerDataType.ExAttr2); | 
|   | 
|         int MapID = 1; | 
|         int FuncLineID = (int)exAttr2; | 
|   | 
|         CreateStoryBattle(MapID, FuncLineID, null, null); | 
|     } | 
|   | 
|     //  上游戏的时候 等战斗阵容更新完毕 创建主线副本 敌方的数据可以暂时不显示 己方表现为睡觉 | 
|     //  如果主线副本存在 那么维持当前的副本不变 | 
|     protected void CreateStoryBattle(int MapID, int FuncLineID, JsonData extendData, List<TeamBase> blueTeamList = null) | 
|     { | 
|         if (null == storyBattleField) | 
|         { | 
|             var redTeamList = new List<TeamBase>(); | 
|   | 
|             TeamBase storyTeam = TeamManager.Instance.GetTeam(TeamType.Story); | 
|   | 
|             redTeamList.Add(storyTeam); | 
|   | 
|             CreateBattleField(string.Empty, MapID, FuncLineID, extendData, redTeamList, blueTeamList); | 
|         } | 
|         else | 
|         { | 
|             // storyBattleField | 
|         } | 
|     } | 
|   | 
|     public void OnBattleClose(BattleField _battleField) | 
|     { | 
|   | 
|     } | 
|   | 
|     #region 截断网络派发包 只收入当前包的后续 b425是主线的 非主线的包并不会走b425 | 
|     private bool allow = true; | 
|   | 
|     private Queue<GameNetPackBasic> packQueue = new Queue<GameNetPackBasic>(); | 
|   | 
|     public bool IsCanDistributePackage(GameNetPackBasic _package) | 
|     { | 
|         if (_package is HB425_tagSCTurnFightReportSign) | 
|         { | 
|             HB425_tagSCTurnFightReportSign pkg = _package as HB425_tagSCTurnFightReportSign; | 
|   | 
|             // 0-战报片段开始;1-战报片段结束; | 
|             if (pkg.Sign == 0) | 
|             { | 
|                 allow = false; | 
|             } | 
|             else | 
|             { | 
|                 allow = true; | 
|   | 
|                 //  发送战报片段结束包 | 
|                 AnalysisPackQueueAndDistribute(); | 
|   | 
|             } | 
|         } | 
|         else | 
|         { | 
|             if (!allow) | 
|             { | 
|                 packQueue.Enqueue(_package); | 
|             } | 
|         } | 
|   | 
|         return allow; | 
|     } | 
|   | 
|     protected int continousEmptyCount = 0; // 连续空包计数 | 
|   | 
|     protected void AnalysisPackQueueAndDistribute() | 
|     { | 
|         // 建议前端做一个防范机制:当连续多次请求得到空的战斗片段封包时(不包含B425标记的开始跟结束封包,即开始跟中间没有任何封包),强制自动帮玩家回城休息, | 
|         // 原因可能前后端数据不一致bug(比如战锤可能后端没有了,前端认为还有)或者 后端有bug导致没有处理战斗 | 
|         // 为防止死循环,可强制回城休息,让玩家重新点击关卡战斗或挑战boss, | 
|         // 正常情况下在战锤足够时理论上都可以一直循环刷怪,如果连续多次没有战斗片段封包,比如限制个连续10次以内,就可以理解为异常了 | 
|   | 
|         const int MaxContinousEmptyCount = 10; // 连续空包最大次数 | 
|   | 
|         List<GameNetPackBasic> packQueueSnapshot = new List<GameNetPackBasic>(packQueue); | 
|   | 
|         List<GameNetPackBasic> newPackList = new List<GameNetPackBasic>(); | 
|         HashSet<int> skipIndexes = new HashSet<int>(); | 
|   | 
|         // 这里已经是按照Dequeue的顺序了 | 
|         for (int i = 0; i < packQueueSnapshot.Count; i++) | 
|         { | 
|             if (skipIndexes.Contains(i)) continue; | 
|   | 
|             GameNetPackBasic pack = packQueueSnapshot[i]; | 
|   | 
|             // 碰到B421 截断 往下收集b421里的全部内容 | 
|             if (pack is HB421_tagMCTurnFightObjAction) | 
|             { | 
|                 HB421_tagMCTurnFightObjAction b421Pack = pack as HB421_tagMCTurnFightObjAction; | 
|   | 
|                 List<GameNetPackBasic> b421PackList = new List<GameNetPackBasic>(); | 
|                 i++;    // 跳过当前的B421包 | 
|   | 
|                 for (; i < packQueueSnapshot.Count; i++) | 
|                 { | 
|                     GameNetPackBasic nextPack = packQueueSnapshot[i]; | 
|                     if (nextPack is HB421_tagMCTurnFightObjAction) | 
|                     { | 
|                         // 遇到了其他B421 启动角色的Action开始, | 
|                         // B421后再碰到B421一定是有一个人的行动结束了 回退一个位置 | 
|                         i--; | 
|                         break; | 
|                     } | 
|                     else | 
|                     { | 
|                         b421PackList.Add(nextPack); | 
|                         skipIndexes.Add(i); // 标记已被合包 | 
|                     } | 
|                 } | 
|   | 
|                 // 可能没用了 主要就是利用一下skill的combine 暂留 看之后还有没有别的需求 | 
|                 CustomB421ActionPack actionPack = CustomB421ActionPack.CreateB421ActionPack(GetGUID(b421Pack.packUID), b421PackList); | 
|   | 
|                 newPackList.Add(actionPack); | 
|             } | 
|             else | 
|             { | 
|                 newPackList.Add(pack); | 
|             } | 
|         } | 
|   | 
|         // 防范机制:连续多次没有战斗片段封包时自动回城 | 
|         if (newPackList.Count == 0) | 
|         { | 
|             continousEmptyCount++; | 
|             Debug.LogWarning($"连续空战斗片段封包次数:{continousEmptyCount}"); | 
|             if (continousEmptyCount >= MaxContinousEmptyCount) | 
|             { | 
|                 Debug.LogError("连续多次没有战斗片段封包,自动回城休息!"); | 
|                 MainFightRequest(0); // 0-停止战斗回城 | 
|                 continousEmptyCount = 0; | 
|                 packQueue.Clear(); | 
|                 return; | 
|             } | 
|         } | 
|         else | 
|         { | 
|             continousEmptyCount = 0; // 有包就重置 | 
|         } | 
|   | 
|         // b421跟b426的包已经处理完了 | 
|         packQueue = new Queue<GameNetPackBasic>(newPackList); | 
|   | 
|         DistributeNextPackage(); | 
|     } | 
|   | 
|     //  专属于主线战斗的派发 | 
|     public bool DistributeNextPackage() | 
|     { | 
|         if (packQueue.Count > 0) | 
|         { | 
|             GameNetPackBasic pack = packQueue.Dequeue(); | 
|   | 
|             if (pack is CustomHB426CombinePack) | 
|             { | 
|                 CustomHB426CombinePack combinePack = pack as CustomHB426CombinePack; | 
|                 combinePack.Distribute(); | 
|             } | 
|             else if (pack is CustomB421ActionPack) | 
|             { | 
|                 CustomB421ActionPack actionPack = pack as CustomB421ActionPack; | 
|                 actionPack.Distribute(); | 
|             } | 
|             else | 
|             { | 
|                 PackageRegedit.Distribute(pack); | 
|             } | 
|   | 
|             return true; | 
|         } | 
|         else | 
|         { | 
|             return false; | 
|         } | 
|     } | 
|   | 
|     public void OnConnected() | 
|     { | 
|         if (!allow) | 
|         { | 
|             allow = true; | 
|             packQueue.Clear(); | 
|   | 
|             //  重新发送要上一组战斗包的请求 | 
|             //  TODO YYL | 
|         } | 
|     } | 
|   | 
|     #endregion | 
|   | 
|     #region 战报部分 | 
|   | 
|     protected Dictionary<string, Queue<GameNetPackBasic>> battleReportDict = new Dictionary<string, Queue<GameNetPackBasic>>(); | 
|   | 
|     protected Dictionary<string, List<ulong>> battlePackRelationList = new Dictionary<string, List<ulong>>(); | 
|   | 
|     public void PushPackage(string guid, GameNetPackBasic vNetPack) | 
|     { | 
|         Queue<GameNetPackBasic> queue = null; | 
|   | 
|         if (!battleReportDict.TryGetValue(guid, out queue)) | 
|         { | 
|             queue = new Queue<GameNetPackBasic>(); | 
|         } | 
|   | 
|         queue.Enqueue(vNetPack); | 
|   | 
|         List<ulong> uidList = null; | 
|   | 
|         if (!battlePackRelationList.TryGetValue(guid, out uidList)) | 
|         { | 
|             uidList = new List<ulong>(); | 
|         } | 
|   | 
|         uidList.Add(vNetPack.packUID); | 
|     } | 
|   | 
|     public BattleField GetBattleField(ulong packUID) | 
|     { | 
|         return GetBattleField(GetGUID(packUID)); | 
|     } | 
|   | 
|     public BattleField GetBattleField(string guid) | 
|     { | 
|         BattleField battleField = null; | 
|         battleFields.TryGetValue(guid, out battleField); | 
|         return battleField; | 
|     } | 
|   | 
|     public string GetGUID(ulong packUID) | 
|     { | 
|         foreach (var kv in battlePackRelationList) | 
|         { | 
|             if (kv.Value.Contains(packUID)) | 
|             { | 
|                 return kv.Key; | 
|             } | 
|         } | 
|         return string.Empty; | 
|     } | 
|   | 
|     public void DistributeNextReportPackage(string guid) | 
|     { | 
|         Queue<GameNetPackBasic> queue = null; | 
|   | 
|         if (!battleReportDict.TryGetValue(guid, out queue)) | 
|         { | 
|             Debug.LogError("DistributeNextReportPackage could not find queue for guid : " + guid); | 
|             return; | 
|         } | 
|   | 
|         PackageRegedit.Distribute(queue.Dequeue()); | 
|   | 
|         if (queue.Count <= 0) | 
|         { | 
|             battleReportDict.Remove(guid); | 
|             battlePackRelationList.Remove(guid); | 
|         } | 
|     } | 
|     #endregion | 
|   | 
|     public BattleField CreateBattleField(string guid, int MapID, int FuncLineID, JsonData extendData, List<TeamBase> redTeamList, List<TeamBase> blueTeamList) | 
|     { | 
|         BattleField battleField = null; | 
|   | 
|         if (battleFields.TryGetValue(guid, out battleField)) | 
|         { | 
|             Debug.LogError("战场已存在 先进行销毁"); | 
|             battleField.Destroy(); | 
|         } | 
|   | 
|         battleField = BattleFieldFactory.CreateBattleField(guid, MapID, FuncLineID, extendData, redTeamList, blueTeamList); | 
|   | 
|         onBattleFieldCreate?.Invoke(guid, battleField); | 
|   | 
|         if (string.IsNullOrEmpty(guid)) | 
|         { | 
|             storyBattleField = (StoryBattleField)battleField; | 
|         } | 
|   | 
|         battleFields.Add(guid, battleField); | 
|   | 
|         battleField.Init(MapID, FuncLineID, extendData, redTeamList, blueTeamList); | 
|   | 
|         return battleField; | 
|     } | 
|   | 
|     public void DestroyBattleField(BattleField battleField) | 
|     { | 
|         if (battleField == null) | 
|         { | 
|             Debug.LogError("DestroyBattleField called with null battleField"); | 
|             return; | 
|         } | 
|   | 
|         onBattleFieldDestroy?.Invoke(battleField.guid, battleField); | 
|   | 
|         string guid = battleField.guid; | 
|   | 
|         if (battleFields.ContainsKey(guid)) | 
|         { | 
|             battleFields.Remove(guid); | 
|         } | 
|   | 
|         if (storyBattleField == battleField) | 
|         { | 
|             storyBattleField = null; | 
|         } | 
|   | 
|         GameObject.DestroyImmediate(battleField.battleRootNode.gameObject); | 
|     } | 
|   | 
|   | 
|     // 目前支持  BYTE ReqType; // 0-停止战斗回城;1-设置消耗倍值;2-挑战关卡小怪;3-挑战关卡boss;4-继续战斗; | 
|     // 0-停止战斗回城   -  玩家主动点击回城时发送 | 
|     // 1-设置消耗倍值   -  玩家设置消耗倍值,对应到玩家FightPoint的值 | 
|     // 2-挑战关卡小怪   -  玩家点击开始战斗时发送,仅从休息状态到开始战斗时发送即可 | 
|     // 3-挑战关卡boss   -  玩家请求挑战该关卡boss时发送 | 
|     // 4-继续战斗          -   玩家主线战斗中(包含主线小怪、主线boss),前端表现完后端同步的战斗片段后,可再回复该值,后端会根据战斗逻辑及流程自动回复下一段的战斗片段封包,一直循环 | 
|     public void MainFightRequest(byte reqType, uint reqValue = 0) | 
|     { | 
|         CB413_tagCSMainFightReq req = new CB413_tagCSMainFightReq(); | 
|         req.ReqType = reqType; | 
|         req.ReqValue = reqValue; | 
|   | 
|         GameNetSystem.Instance.SendInfo(req); | 
|     } | 
|   | 
|   | 
|     public void Run() | 
|     { | 
|         foreach (var battleField in battleFields) | 
|         { | 
|             battleField.Value?.Run(); | 
|         } | 
|     } | 
|   | 
| } |