| using System; | 
| using System.Collections.Generic; | 
| using UnityEngine; | 
| using UnityEngine.UI; | 
| using DG.Tweening; | 
| using DG.Tweening.Core; | 
| using System.Linq; | 
|   | 
| public static class BattleUtility | 
| { | 
|     // 其他通用的战斗工具方法可以放在这里 | 
|   | 
|   | 
|     public static void MarkStartAndEnd(RectTransform startNode, RectTransform endNode) | 
|     { | 
|         // 运行时才执行 | 
|         if (!Application.isPlaying) | 
|         { | 
|             Debug.LogWarning("请在运行时使用该功能!"); | 
|             return; | 
|         } | 
|   | 
|         var battleField = BattleManager.Instance.storyBattleField; | 
|         if (battleField == null) | 
|         { | 
|             Debug.LogError("BattleManager.storyBattleField 未初始化!"); | 
|             return; | 
|         } | 
|   | 
|   | 
|         BattleWin battleWin = UIManager.Instance.GetUI<BattleWin>(); | 
|   | 
|         RectTransform canvasRect = battleWin.transform as RectTransform; | 
|   | 
|         CreateMarker(canvasRect, startNode, "StartMarker"); | 
|         CreateMarker(canvasRect, endNode, "EndMarker"); | 
|     } | 
|   | 
|     private static void CreateMarker(RectTransform canvasRect, RectTransform targetNode, string markerName) | 
|     { | 
|         // 获取目标节点的世界坐标(中心点) | 
|         Vector3 worldPos = targetNode.TransformPoint(targetNode.rect.center); | 
|   | 
|         // 转换到Canvas本地坐标 | 
|         Vector2 localPoint; | 
|         RectTransformUtility.ScreenPointToLocalPointInRectangle( | 
|             canvasRect, | 
|             RectTransformUtility.WorldToScreenPoint(null, worldPos), | 
|             null, | 
|             out localPoint); | 
|   | 
|         // 创建RawImage | 
|         GameObject marker = new GameObject(markerName, typeof(RawImage)); | 
|         GameObject.Destroy(marker, 5f); | 
|         marker.transform.SetParent(canvasRect, false); | 
|         var rawImage = marker.GetComponent<RawImage>(); | 
|         rawImage.color = Color.white; | 
|         rawImage.rectTransform.sizeDelta = new Vector2(100, 100); | 
|         rawImage.rectTransform.anchoredPosition = localPoint; | 
|     } | 
|   | 
|   | 
|     public static TweenerCore<Vector2, Vector2, DG.Tweening.Plugins.Options.VectorOptions> MoveToTarget( | 
|         RectTransform transform, RectTransform target, Vector2 offset, Action onComplete = null, float speed = 500f) | 
|     { | 
|         // 获取目标节点的世界坐标(锚点位置) | 
|         Vector3 worldPos = target.position; | 
|   | 
|         // 如果需要加 offset,需考虑 scale | 
|         Vector3 offsetWorld = target.TransformVector(offset); | 
|         worldPos += offsetWorld; | 
|   | 
|         RectTransform canvasRect = transform.parent as RectTransform; | 
|   | 
|         // 转换到Canvas本地坐标 | 
|         Vector2 localPoint; | 
|         RectTransformUtility.ScreenPointToLocalPointInRectangle( | 
|             canvasRect, | 
|             RectTransformUtility.WorldToScreenPoint(null, worldPos), | 
|             null, | 
|             out localPoint); | 
|   | 
|         float distance = Vector2.Distance(transform.anchoredPosition, localPoint); | 
|         float duration = distance / speed; | 
|   | 
|         var tween = transform.DOAnchorPos(localPoint, duration).SetEase(Ease.Linear); | 
|         tween.onComplete += () => | 
|         { | 
|             onComplete?.Invoke(); | 
|         }; | 
|   | 
|         return tween; | 
|     } | 
|   | 
|     public static string DisplayDamageNum(long num, int attackType) | 
|     { | 
|         var config = DamageNumConfig.Get(attackType); | 
|         var basePowerStr = UIHelper.ReplaceLargeArtNum(num); | 
|         var result = string.Empty; | 
|         for (int i = 0; i < basePowerStr.Length; i++) | 
|         { | 
|             var numChar = (char)GetDamageNumKey(config, basePowerStr[i]); | 
|             if (numChar > 0) | 
|             { | 
|                 result += numChar; | 
|             } | 
|         } | 
|         return result; | 
|     } | 
|   | 
|     public static string DisplayDamageNum(BattleDmg damage) | 
|     { | 
|         var config = DamageNumConfig.Get(damage.attackType); | 
|   | 
|         string result = string.Empty; | 
|   | 
|         //  如果是闪避 则只显示闪避两个字 | 
|         if (damage.IsType(DamageType.Dodge)) | 
|         { | 
|             result += (char)config.prefix; | 
|         } | 
|         else | 
|         { | 
|             result = ConvertToArtFont(config, damage.damage); | 
|         } | 
|   | 
|         return result; | 
|     } | 
|   | 
|     static string ConvertToArtFont(DamageNumConfig config, float _num) | 
|     { | 
|         var stringBuild = new System.Text.StringBuilder(); | 
|   | 
|         if (0 != config.plus) | 
|             stringBuild.Append((char)config.plus); | 
|         if (0 != config.prefix) | 
|             stringBuild.Append((char)config.prefix); | 
|   | 
|         var chars = UIHelper.ReplaceLargeArtNum(_num); | 
|         for (var i = 0; i < chars.Length; i++) | 
|         { | 
|             int numChar = GetDamageNumKey(config, (int)chars[i]); | 
|   | 
|             if (numChar > 0) | 
|             { | 
|                 stringBuild.Append((char)numChar); | 
|             } | 
|         } | 
|   | 
|         return stringBuild.ToString(); | 
|     } | 
|   | 
|     public static int GetMainTargetPositionNum(BattleObject caster, List<HB427_tagSCUseSkill.tagSCUseSkillHurt> targetList, SkillConfig skillConfig) | 
|     { | 
|         int returnIndex = 0; | 
|         //  根据敌方血量阵营 存活人数来选择 | 
|         BattleCamp battleCamp = skillConfig.TagFriendly != 0 ? caster.Camp : caster.GetEnemyCamp(); | 
|         List<BattleObject> targetObjList = caster.battleField.battleObjMgr.GetBattleObjList(battleCamp); | 
|   | 
|   | 
|         // 瞄准的目标范围,如果目标个数为0则为范围内全部 | 
|         // 0    全部范围 | 
|         // 1    对位,默认只选1个 | 
|         // 2    前排 | 
|         // 3    后排 | 
|         // 4    纵排,按对位规则选择纵排 | 
|         // 5    自己,默认只选自己 | 
|   | 
|         switch (skillConfig.TagAim) | 
|         { | 
|             case 0: | 
|                 // 0   全部范围: | 
|                 // 若TagCount目标个数为0或6,根据TagFriendly敌我配置,代表作用于敌方全体或我方全体,此时主目标为敌我站位中的2号位置 | 
|                 // 若TagCount目标个数为1~5个,根据TagFriendly敌我+TagAffect细分目标配置,代表随机作用于敌方或我方x个武将,第一个为主目标 | 
|                 if (skillConfig.TagCount == 0 || skillConfig.TagCount == 6) | 
|                 { | 
|                     returnIndex = 1; | 
|                 } | 
|                 else | 
|                 { | 
|                     uint objId = targetList[0].ObjID; | 
|                     BattleObject target = caster.battleField.battleObjMgr.GetBattleObject((int)objId); | 
|                     return target.teamHero.positionNum; | 
|                 } | 
|                 break; | 
|             case 1: | 
|                 // 1    对位: | 
|                 // 默认只选1个,对位规则为A1优先打B1,A2优先打B2,A3优先打B3,对位目标死亡时,优先前排,比如B2已经死亡,那么A2将优先打B1,前排1、2、3号位置全部死亡之后才开始选择后排4、5、6号位置,对位只可选1个目标,即主目标 | 
|                 if (targetList.Count > 0) | 
|                 { | 
|                     BattleObject battleObject = caster.battleField.battleObjMgr.GetBattleObject((int)targetList[0].ObjID); | 
|                     if (battleObject != null) | 
|                     { | 
|                         returnIndex = battleObject.teamHero.positionNum; | 
|                     } | 
|                     else | 
|                     { | 
|                         Debug.LogError("GetMainTargetPositionNum 找不到目标 ObjId : " + targetList[0].ObjID); | 
|                         returnIndex = 0; | 
|                     } | 
|                 } | 
|                 else | 
|                 { | 
|                     Debug.LogError("targetList 目标列表为空"); | 
|                     returnIndex = 0; | 
|                 } | 
|                 break; | 
|             case 2: | 
|                 //  1、2、3号位为前排,默认2号位置为主目标,当1、2、3号位置角色全部死亡,前排将替换成后排,5号位置变更为主目标, | 
|                 //  若配置TagAffect细分目标,且人数小于3,则所有被选择目标均为主目标(施法位置会用客户端配置) | 
|                 // (即前排默认2号位或5号位规则无效,实际作用多少人就是多少个主目标) (YL : TagAffect>0 && TagAffect != 3就是全体都是主目标 后排一样 ) | 
|                 if (skillConfig.TagAffect != 0 || skillConfig.TagCount < 3) | 
|                 { | 
|                     uint objId = targetList[0].ObjID; | 
|                     BattleObject target = caster.battleField.battleObjMgr.GetBattleObject((int)objId); | 
|                     returnIndex = target.teamHero.positionNum; | 
|                 } | 
|                 else | 
|                 { | 
|                     //  看看对面前排是否都活着 | 
|                     List<BattleObject> front = new List<BattleObject>(from bo in targetObjList where !bo.IsDead() && bo.teamHero.positionNum < 3 select bo); | 
|                     if (front.Count > 0) | 
|                     { | 
|                         returnIndex = 1; | 
|                     } | 
|                     else | 
|                     { | 
|                         returnIndex = 4; | 
|                     } | 
|                 } | 
|                 break; | 
|             case 3: | 
|                 if (skillConfig.TagAffect != 0 || skillConfig.TagCount < 3) | 
|                 { | 
|                     uint objId = targetList[0].ObjID; | 
|                     BattleObject target = caster.battleField.battleObjMgr.GetBattleObject((int)objId); | 
|                     returnIndex = target.teamHero.positionNum; | 
|                 } | 
|                 else | 
|                 { | 
|                     //  看看对面后排是否都活着 | 
|                     List<BattleObject> back = new List<BattleObject>(from bo in targetObjList where !bo.IsDead() && bo.teamHero.positionNum >= 3 select bo); | 
|                     if (back.Count > 0) | 
|                     { | 
|                         returnIndex = 4; | 
|                     } | 
|                     else | 
|                     { | 
|                         returnIndex = 1; | 
|                     } | 
|                 } | 
|                 break; | 
|             // 4    纵排,按对位规则选择纵排 | 
|             case 4: | 
|                 returnIndex = int.MaxValue; | 
|                 for (int i = 0; i < targetList.Count; i++) | 
|                 { | 
|                     var hurt = targetList[i]; | 
|                     BattleObject target = caster.battleField.battleObjMgr.GetBattleObject((int)hurt.ObjID); | 
|                     if (target == null) | 
|                     { | 
|                         Debug.LogError("GetMainTargetPositionNum 找不到目标 ObjId : " + hurt.ObjID); | 
|                         continue; | 
|                     } | 
|                     else | 
|                     { | 
|                         returnIndex = Mathf.Min(returnIndex, target.teamHero.positionNum); | 
|                     } | 
|                 } | 
|                 break; | 
|             // 5    自己,默认只选自己 | 
|             case 5: | 
|                 returnIndex = caster.teamHero.positionNum; | 
|                 break; | 
|             default: | 
|                 Debug.LogError("暂时不支持其他的方式选择主目标 有需求请联系策划 技能id:" + skillConfig.SkillID + " TagAim " + skillConfig.TagAim); | 
|                 returnIndex = 0; | 
|                 break; | 
|         } | 
|   | 
|         return returnIndex; | 
|     } | 
|   | 
|     public static int GetDamageNumKey(DamageNumConfig config, int _num) | 
|     { | 
|         if (_num == 46)      return config.nums[10]; // '.' | 
|         else if (_num == 107) return config.nums[11]; // 'k' | 
|         else if (_num == 109) return config.nums[12]; // 'm' | 
|         else if (_num == 98)  return config.nums[13]; // 'b' | 
|         else if (_num == 116) return config.nums[14]; // 't' | 
|         int targetNum = _num - 48; | 
|         if (targetNum >= config.nums.Length || targetNum < 0) | 
|         { | 
|             Debug.LogError("damage config " + config.TypeID + " _num is " +  _num + " out of range"); | 
|             return _num; | 
|         } | 
|         return config.nums[_num - 48]; | 
|     } | 
|   | 
|     /// <summary> | 
|     /// 保证所有分配项加起来等于totalDamage,避免因整除导致的误差 | 
|     /// </summary> | 
|     public static List<long> DivideDamageToList(int[] damageDivide, long totalDamage) | 
|     { | 
|         List<long> fixedDamageList = new List<long>(); | 
|   | 
|         for (int i = 0; i < damageDivide.Length; i++) | 
|         { | 
|             float fixedDamage = (float)totalDamage * (float)damageDivide[i] / 10000f; | 
|             fixedDamageList.Add((int)fixedDamage); | 
|         } | 
|          | 
|         return fixedDamageList; | 
|     } | 
|   | 
|     public static List<HB422_tagMCTurnFightObjDead> FindDeadPack(List<GameNetPackBasic> packList) | 
|     { | 
|         List<HB422_tagMCTurnFightObjDead> deadPacks = new List<HB422_tagMCTurnFightObjDead>(); | 
|         for (int i = 0; i < packList.Count; i++) | 
|         { | 
|             var pack = packList[i]; | 
|             //    寻找死亡包 找到死亡包之后要找掉落包 不能超过技能包 | 
|             if (pack is HB422_tagMCTurnFightObjDead deadPack) | 
|             { | 
|                 deadPacks.Add(deadPack); | 
|             } | 
|             else if (pack is CustomHB426CombinePack) | 
|             { | 
|                 break; | 
|             } | 
|         } | 
|         // Debug.LogError("find dead pack " + deadPacks.Count); | 
|         return deadPacks; | 
|     } | 
|   | 
|   | 
|     public static List<HB423_tagMCTurnFightObjReborn> FindRebornPack(List<GameNetPackBasic> packList) | 
|     { | 
|         List<HB423_tagMCTurnFightObjReborn> rebornPackList = new List<HB423_tagMCTurnFightObjReborn>(); | 
|         for (int i = 0; i < packList.Count; i++) | 
|         { | 
|             var pack = packList[i]; | 
|             //    寻找死亡包 找到死亡包之后要找掉落包 不能超过技能包 | 
|             if (pack is HB423_tagMCTurnFightObjReborn rebornPack) | 
|             { | 
|                 rebornPackList.Add(rebornPack); | 
|             } | 
|             else if (pack is CustomHB426CombinePack) | 
|             { | 
|                 break; | 
|             } | 
|         } | 
|         return rebornPackList; | 
|     } | 
| } |