hch
2025-09-04 62188b271cce5e3aec5ca40d58c30f08643e2f60
Main/System/Battle/BattleUtility.cs
@@ -4,29 +4,331 @@
using UnityEngine.UI;
using DG.Tweening;
using DG.Tweening.Core;
using System.Linq;
public static class BattleUtility
{
    // 其他通用的战斗工具方法可以放在这里
    public static TweenerCore<Vector2, Vector2, DG.Tweening.Plugins.Options.VectorOptions> MoveToTarget(RectTransform transform, RectTransform target, Vector2 offset, float duration, Action onComplete = null)
    public static void MarkStartAndEnd(RectTransform startNode, RectTransform endNode)
    {
        Vector3 targetWorldPos = target.TransformPoint(target.anchoredPosition + offset);
        // 运行时才执行
        if (!Application.isPlaying)
        {
            Debug.LogWarning("请在运行时使用该功能!");
            return;
        }
        RectTransform parentRect = transform.parent as RectTransform;
        var battleField = BattleManager.Instance.storyBattleField;
        if (battleField == null)
        {
            Debug.LogError("BattleManager.storyBattleField 未初始化!");
            return;
        }
        Vector2 targetAnchoredPos;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(
            parentRect,
            RectTransformUtility.WorldToScreenPoint(null, targetWorldPos),
            null,
            out targetAnchoredPos);
        // 3. DOTween 移动
        return transform.DOAnchorPos(targetAnchoredPos, duration)
            .SetEase(Ease.Linear)
            .OnComplete(() => onComplete?.Invoke());
        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, float duration, Action onComplete = null)
    {
        // 获取目标节点的世界坐标(中心点)
        Vector3 worldPos = target.TransformPoint(target.rect.center + offset);
        RectTransform canvasRect = transform.parent as RectTransform;
        // 转换到Canvas本地坐标
        Vector2 localPoint;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(
            canvasRect,
            RectTransformUtility.WorldToScreenPoint(null, worldPos),
            null,
            out localPoint);
        // 创建RawImage
        var tween = transform.DOAnchorPos(localPoint, duration).SetEase(Ease.Linear);
        tween.onComplete += () =>
        {
            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)
    {
        // 服务器位数到客户端类型ID的映射
        Dictionary<int, int> serverToClientTypeMap = new Dictionary<int, int>
        {
            { 1, 2 },    // 普通伤血
            { 2, 4 },    // 恢复回血
            { 3, 8 },    // 反弹伤血
            { 4, 16 },   // 持续伤血
            { 5, 32 },   // 格挡
            { 7, 64 },   // 暴击伤害
            { 9, 128 },  // 闪避
            // 其它类型如需补充可继续添加
        };
        int damageTypeValue = 0;
        for (int i = 0; i < 32; i++)
        {
            int flag = 1 << i;
            if ((attackType & flag) != 0)
            {
                // 只处理有映射的类型
                if (serverToClientTypeMap.TryGetValue(i + 1, out int clientTypeId))
                {
                    damageTypeValue += clientTypeId;
                }
            }
        }
        DamageType damageType = (DamageType)damageTypeValue;
        var config = DamageNumConfig.Get(damageTypeValue);
        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 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    自己,默认只选自己
        // 6    继承主技能/来源技能目标
        //      一般是额外触发的技能使用,如概率附加某buff
        //      额外触发的技能如果目标与主技能不一致,则重新设定目标即可
        //      或被动触发的技能,可继承触发来源技能的攻防双方关系
        switch (skillConfig.TagAim)
        {
            case 0:
                //  全部范围
                //全部范围+敌我+目标数量为6
                //就是取敌我站位中的2号位
                returnIndex = 1;
                break;
            case 1:
                returnIndex = caster.teamHero.positionNum;
                //  寻找对位是否有人 没有的话选择最小的
                List<BattleObject> opposite = new List<BattleObject>(from bo in targetObjList where !bo.IsDead() && bo.teamHero.positionNum == returnIndex select bo);
                if (opposite.Count > 0)
                {
                    returnIndex = opposite[0].teamHero.positionNum;
                }
                else
                {
                    opposite = new List<BattleObject>(from bo in targetObjList where !bo.IsDead() select bo);
                    opposite.Sort((a, b) => a.teamHero.positionNum.CompareTo(b.teamHero.positionNum));
                    returnIndex = opposite.Count > 0 ? opposite[0].teamHero.positionNum : returnIndex;
                }
                break;
            case 2:
                //  看看对面前排是否都活着
                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:
                //  看看对面后排是否都活着
                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:
                List<BattleObject> vertical = new List<BattleObject>(from bo in targetObjList where !bo.IsDead() && (bo.teamHero.positionNum - caster.teamHero.positionNum) % 3 == 0 select bo);
                //  TODO YYL
                break;
            // 5    自己,默认只选自己
            case 5:
                returnIndex = caster.teamHero.positionNum;
                break;
            default:
                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'
        return config.nums[_num - 48];
    }
    /// <summary>
    /// 保证所有分配项加起来等于totalDamage,避免因整除导致的误差
    /// </summary>
    public static List<long> DivideDamageToList(int[] damageDivide, long totalDamage)
    {
        List<long> fixedDamageList = new List<long>();
        long assigned = 0;
        int count = damageDivide.Length;
        for (int i = 0; i < count; i++)
        {
            long damage;
            if (i == count - 1)
            {
                // 最后一个分配项修正为剩余
                damage = totalDamage - assigned;
            }
            else
            {
                damage = (totalDamage * damageDivide[i] + 5000) / 10000; // 四舍五入
                assigned += damage;
            }
            fixedDamageList.Add(damage);
        }
        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)
            {
                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;
                }
            }
        }
        return deadPacks;
    }
    public static List<HB423_tagMCTurnFightObjReborn> FindRebornPack(List<GameNetPackBasic> packList)
    {
        List<HB423_tagMCTurnFightObjReborn> rebornPack = new List<HB423_tagMCTurnFightObjReborn>();
        for (int i = 0; i < packList.Count; i++)
        {
            var pack = packList[i];
            //   寻找死亡包 找到死亡包之后要找掉落包 不能超过技能包
            if (pack is HB423_tagMCTurnFightObjReborn)
            {
                var deadPack = pack as HB423_tagMCTurnFightObjReborn;
                rebornPack.Add(deadPack);
            }
            else if (pack is CustomHB426CombinePack)
            {
                //   找死亡包不要越过技能包
                var combinePack = pack as CustomHB426CombinePack;
                if (combinePack.startTag.Tag.StartsWith("Skill_"))
                {
                    break;
                }
            }
        }
        return rebornPack;
    }
}