yyl
39 分钟以前 725b7b2374f43582a2d78b2cae3f8303359651b8
Main/System/Battle/BattleUtility.cs
@@ -4,7 +4,7 @@
using UnityEngine.UI;
using DG.Tweening;
using DG.Tweening.Core;
using System.Linq;
public static class BattleUtility
{
@@ -23,7 +23,7 @@
        var battleField = BattleManager.Instance.storyBattleField;
        if (battleField == null)
        {
            BattleDebug.LogError("BattleManager.storyBattleField 未初始化!");
            Debug.LogError("BattleManager.storyBattleField 未初始化!");
            return;
        }
@@ -61,11 +61,14 @@
    public static TweenerCore<Vector2, Vector2, DG.Tweening.Plugins.Options.VectorOptions> MoveToTarget(
        RectTransform transform, RectTransform target, Vector2 offset, float duration, Action onComplete = null)
        RectTransform transform, RectTransform target, Vector2 offset, Action onComplete = null, float speed = 500f)
    {
        // 获取目标节点的世界坐标(锚点位置)
        Vector3 worldPos = target.position;
        // 获取目标节点的世界坐标(中心点)
        Vector3 worldPos = target.TransformPoint(target.rect.center + offset);
        // 如果需要加 offset,需考虑 scale
        Vector3 offsetWorld = target.TransformVector(offset);
        worldPos += offsetWorld;
        RectTransform canvasRect = transform.parent as RectTransform;
@@ -77,7 +80,8 @@
            null,
            out localPoint);
        // 创建RawImage
        float distance = Vector2.Distance(transform.anchoredPosition, localPoint);
        float duration = distance / speed;
        var tween = transform.DOAnchorPos(localPoint, duration).SetEase(Ease.Linear);
        tween.onComplete += () =>
@@ -85,45 +89,17 @@
            onComplete?.Invoke();
        };
        // MarkStartAndEnd(transform as RectTransform, target);
        // // 1. 获取目标的世界坐标(加 offset)
        // Vector3 targetWorldPos = target.TransformPoint(target.anchoredPosition + offset);
        // // 2. 获取源节点的 parent
        // RectTransform sourceParent = transform.parent as RectTransform;
        // if (sourceParent == null)
        // {
        //     BattleDebug.LogError("源节点没有父节点,无法转换坐标!");
        //     return null;
        // }
        // // 3. 把目标世界坐标转换到源 parent 的本地坐标
        // Vector2 targetAnchoredPos;
        // RectTransformUtility.ScreenPointToLocalPointInRectangle(
        //     sourceParent,
        //     RectTransformUtility.WorldToScreenPoint(CameraManager.uiCamera, targetWorldPos),
        //     CameraManager.uiCamera,
        //     out targetAnchoredPos);
        // // 4. DOTween 移动
        // var tween = transform.DOAnchorPos(targetAnchoredPos, 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((DamageType)attackType, basePowerStr[i]);
            var numChar = (char)GetDamageNumKey(config, basePowerStr[i]);
            if (numChar > 0)
            {
                result += numChar;
@@ -132,35 +108,308 @@
        return result;
    }
    public static int GetDamageNumKey(DamageType damageType, int _num)
    public static string DisplayDamageNum(BattleDmg damage)
    {
        var config = DamageNumConfig.Get(damageType.ToString());
        //.的ASCII码是46
        if (_num == 46)
        var config = DamageNumConfig.Get(damage.attackType);
        string result = string.Empty;
        //  如果是闪避 则只显示闪避两个字
        if (damage.IsType(DamageType.Dodge))
        {
            return config.nums[10];
            result += (char)config.prefix;
        }
        //k的ASCII码是107
        else if (_num == 107)
        else
        {
            return config.nums[11];
            result = ConvertToArtFont(config, damage.damage);
        }
        //m的ASCII码是109
        else if (_num == 109)
        return result;
    }
    public 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++)
        {
            return config.nums[12];
            int numChar = GetDamageNumKey(config, (int)chars[i]);
            if (numChar > 0)
            {
                stringBuild.Append((char)numChar);
            }
        }
        //b的ASCII码是98
        else if (_num == 98)
        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)
        {
            return config.nums[13];
            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将优先打B
                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;
        }
        //t的ASCII码是116
        else if (_num == 116)
        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)
        {
            return config.nums[14];
            Debug.LogError("damage config " + config.TypeID + " _num is " + _num + " out of range");
            return _num;
        }
        return config.nums[_num - 48];
    }
    /// <summary>
    /// 将整个技能的总伤害按命中次数和分段配置分配
    /// </summary>
    /// <param name="damageDivideList">整个技能的所有命中分段配置</param>
    /// <param name="hitIndex">当前是第几击(从0开始)</param>
    /// <param name="totalDamage">整个技能的总伤害</param>
    /// <returns>这一击内每一段的伤害值列表</returns>
    public static List<long> DivideDamageToList(int[][] damageDivideList, int hitIndex, long totalDamage)
    {
        if (totalDamage <= 0)
        {
            return new List<long>{};
        }
        if (damageDivideList == null || damageDivideList.Length == 0)
        {
            Debug.LogError("damageDivideList 为空或长度为0");
            return new List<long> { totalDamage };
        }
        if (hitIndex < 0 || hitIndex >= damageDivideList.Length)
        {
            Debug.LogError($"hitIndex={hitIndex} 超出范围, damageDivideList.Length={damageDivideList.Length}");
            return new List<long> { totalDamage };
        }
        int[] currentHitDivide = damageDivideList[hitIndex];
        if (currentHitDivide == null || currentHitDivide.Length == 0)
        {
            Debug.LogError($"damageDivide[{hitIndex}] 为空或长度为0");
            return new List<long> { totalDamage };
        }
        // ============ 第一步: 计算每一击应该造成的伤害 ============
        // 先计算所有击的总权重
        int totalWeight = 0;
        for (int i = 0; i < damageDivideList.Length; i++)
        {
            if (damageDivideList[i] != null && damageDivideList[i].Length > 0)
            {
                // 每一击的权重是其所有分段之和
                for (int j = 0; j < damageDivideList[i].Length; j++)
                {
                    totalWeight += damageDivideList[i][j];
                }
            }
        }
        if (totalWeight == 0)
        {
            Debug.LogError("totalWeight 为 0");
            return new List<long> { totalDamage };
        }
        // 计算当前这一击的权重
        int currentHitWeight = 0;
        for (int i = 0; i < currentHitDivide.Length; i++)
        {
            currentHitWeight += currentHitDivide[i];
        }
        // 计算当前这一击应该造成的总伤害
        long currentHitTotalDamage;
        bool isLastHit = hitIndex >= damageDivideList.Length - 1;
        if (isLastHit)
        {
            // 最后一击: 计算前面所有击已经造成的伤害,剩余的全部给最后一击
            long previousHitsDamage = 0;
            for (int i = 0; i < hitIndex; i++)
            {
                if (damageDivideList[i] != null)
                {
                    int hitWeight = 0;
                    for (int j = 0; j < damageDivideList[i].Length; j++)
                    {
                        hitWeight += damageDivideList[i][j];
                    }
                    previousHitsDamage += (long)((float)totalDamage * (float)hitWeight / (float)totalWeight);
                }
            }
            currentHitTotalDamage = totalDamage - previousHitsDamage;
        }
        else
        {
            // 非最后一击: 按权重计算
            currentHitTotalDamage = (long)((float)totalDamage * (float)currentHitWeight / (float)totalWeight);
        }
        // ============ 第二步: 将当前这一击的伤害分配到各分段 ============
        List<long> fixedDamageList = new List<long>();
        long accumulatedDamage = 0;
        for (int i = 0; i < currentHitDivide.Length; i++)
        {
            long damage;
            // 当前击的最后一段进行误差补偿
            if (i == currentHitDivide.Length - 1)
            {
                damage = currentHitTotalDamage - accumulatedDamage;
            }
            else
            {
                // 按当前击的权重分配
                damage = (long)((float)currentHitTotalDamage * (float)currentHitDivide[i] / (float)currentHitWeight);
                accumulatedDamage += damage;
            }
            fixedDamageList.Add(damage);
        }
        return fixedDamageList;
    }
    /// <summary>
@@ -168,26 +417,52 @@
    /// </summary>
    public static List<long> DivideDamageToList(int[] damageDivide, long totalDamage)
    {
        List<long> fixedDamageList = new List<long>();
        long assigned = 0;
        int count = damageDivide.Length;
        if (damageDivide == null || damageDivide.Length == 0)
        {
            Debug.LogError("damageDivide 为空或长度为0");
            return new List<long> { totalDamage };
        }
        for (int i = 0; i < count; i++)
        List<long> fixedDamageList = new List<long>();
        long accumulatedDamage = 0; // 累计已分配的伤害
        for (int i = 0; i < damageDivide.Length; i++)
        {
            long damage;
            if (i == count - 1)
            // 最后一次分配:用总伤害减去已分配的伤害,确保总和精确
            if (i == damageDivide.Length - 1)
            {
                // 最后一个分配项修正为剩余
                damage = totalDamage - assigned;
                damage = totalDamage - accumulatedDamage;
            }
            else
            {
                damage = (totalDamage * damageDivide[i] + 5000) / 10000; // 四舍五入
                assigned += damage;
                // 计算当前分段伤害(向下取整)
                damage = (long)((float)totalDamage * (float)damageDivide[i] / 10000f);
                accumulatedDamage += damage;
            }
            fixedDamageList.Add(damage);
        }
        return fixedDamageList;
    }
    public static HB419_tagSCObjHPRefresh FindObjHPRefreshPack(List<GameNetPackBasic> packList)
    {
        for (int i = 0; i < packList.Count; i++)
        {
            var pack = packList[i];
            if (pack is HB419_tagSCObjHPRefresh hpRefreshPack)
            {
                return hpRefreshPack;
            }
            else if (pack is CustomHB426CombinePack)
            {
                break;
            }
        }
        return null;
    }
    public static List<HB422_tagMCTurnFightObjDead> FindDeadPack(List<GameNetPackBasic> packList)
@@ -197,47 +472,36 @@
        {
            var pack = packList[i];
            //   寻找死亡包 找到死亡包之后要找掉落包 不能超过技能包
            if (pack is HB422_tagMCTurnFightObjDead)
            if (pack is HB422_tagMCTurnFightObjDead deadPack)
            {
                var deadPack = pack as HB422_tagMCTurnFightObjDead;
                deadPacks.Add(deadPack);
            }
            else if (pack is CustomHB426CombinePack)
            {
                //   找死亡包不要越过技能包
                var combinePack = pack as CustomHB426CombinePack;
                if (combinePack.startTag.Tag.StartsWith("Skill_"))
                {
                    break;
                }
                break;
            }
        }
        // Debug.LogError("find dead pack " + deadPacks.Count);
        return deadPacks;
    }
    public static List<HB423_tagMCTurnFightObjReborn> FindRebornPack(List<GameNetPackBasic> packList)
    {
        List<HB423_tagMCTurnFightObjReborn> rebornPack = new List<HB423_tagMCTurnFightObjReborn>();
        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)
            if (pack is HB423_tagMCTurnFightObjReborn rebornPack)
            {
                var deadPack = pack as HB423_tagMCTurnFightObjReborn;
                rebornPack.Add(deadPack);
                rebornPackList.Add(rebornPack);
            }
            else if (pack is CustomHB426CombinePack)
            {
                //   找死亡包不要越过技能包
                var combinePack = pack as CustomHB426CombinePack;
                if (combinePack.startTag.Tag.StartsWith("Skill_"))
                {
                    break;
                }
                break;
            }
        }
        return rebornPack;
        return rebornPackList;
    }
}