yyl
2025-09-18 871594462e82d6bc1341918d39e11ab036d59563
Main/System/Battle/Skill/SkillBase.cs
@@ -3,10 +3,15 @@
using System;
using DG.Tweening;
using Spine;
using System.Linq;
public class SkillBase
{
   const float moveTime = 0.5f;
   protected SkillEffect skillEffect;
   protected HB427_tagSCUseSkill tagUseSkillAttack;
   protected SkillConfig skillConfig;
@@ -33,6 +38,8 @@
   protected List<HB405_tagMCAddExp> expPackList = new List<HB405_tagMCAddExp>();
   protected bool moveFinished = false;
   public SkillBase(BattleObject _caster, SkillConfig _skillCfg, HB427_tagSCUseSkill vNetData, List<GameNetPackBasic> _packList, BattleField _battleField = null)
   {
      caster = _caster;
@@ -41,6 +48,8 @@
      battleField = _battleField;
      packList = _packList;
   }
   public virtual void Run()
@@ -48,6 +57,11 @@
      if (startCounting)
      {
         curFrame++;
      }
      if (null != skillEffect)
      {
         skillEffect.Run();
      }
      if (otherSkillAction != null)
@@ -65,20 +79,6 @@
   }
   protected virtual void OnActiveSkillFrame()
   {
   }
   protected virtual void OnStartSkillFrame()
   {
   }
   protected virtual void OnEndSkillFrame()
   {
   }
   public void Pause()
   {
@@ -91,142 +91,329 @@
      startCounting = pauseState;
   }
   // 0·移动到距离目标n码,的距离释放(可配置,9999即原地释放,负数则是移动到人物背面,人物要转身)
   // 1·移动到距离阵容位置n码的距离(如2号位,5号位)释放(即战场中央此类)
   public virtual void Cast()
   {
      EventBroadcast.Instance.Broadcast<string, SkillConfig, TeamHero>(EventName.BATTLE_CAST_SKILL, battleField.guid, skillConfig, caster.teamHero);
      //   高亮所有本次技能相关的目标
      HighLightAllTargets();
      //   距离配成负数要转身 TurnBack
      switch (skillConfig.castMode)
      {
         case SkillCastMode.Self:
            PlayCastAnimation(() => DoSkillLogic(OnSkillFinished));
            CastImpl(OnAttackFinish);
            break;
         case SkillCastMode.Enemy:
            MoveToTarget(caster.GetEnemyCamp(), skillConfig, _onComplete: () => TurnBack(() => PlayCastAnimation(() => DoSkillLogic(() => { BackToOrigin(OnSkillFinished); }))));
            CastToEnemy();
            break;
         case SkillCastMode.Target:
            // 目标是敌方主目标
            if (tagUseSkillAttack.HurtCount <= 0)
            {
               Debug.LogError("技能攻击包没有目标 HurtCount <= 0");
               OnSkillFinished();
               return;
            }
            var mainHurt = tagUseSkillAttack.HurtList[0];
            BattleObject mainTarget = battleField.battleObjMgr.GetBattleObject((int)mainHurt.ObjID);
            if (mainTarget == null)
            {
               Debug.LogError("目标为空 mainTarget == null ObjID : " + mainHurt.ObjID);
               OnSkillFinished();
               return;
            }
            MoveToTarget(mainTarget.Camp, mainTarget, _onComplete: () => TurnBack(() => PlayCastAnimation(() => DoSkillLogic(() => { BackToOrigin(OnSkillFinished); }))));
            CastToTarget();
            break;
         case SkillCastMode.Allies:
            MoveToTarget(caster.Camp, skillConfig, _onComplete: () => TurnBack(() => PlayCastAnimation(() => DoSkillLogic(() => { BackToOrigin(OnSkillFinished); }))));
            CastToAllies();
            break;
         // case SkillCastMode.DashCast:
         //    DashToTarget(() => BackToOrigin(OnSkillFinished));
         //    break;
         case SkillCastMode.DashCast:
            DashCast(OnAttackFinish);
            break;
         default:
            Debug.LogError("暂时不支持其他的方式释放 有需求请联系策划");
            Debug.LogError("强制结束技能 暂时不支持其他的方式释放 有需求请联系策划 技能id:" + skillConfig.SkillID + " cast position " + skillConfig.CastPosition);
            ForceFinished();
            break;
      }
   }
   //   冲刺的技能 动作 跟移动 是同时进行的 移动到目标的一瞬间就要进行技能逻辑
   // protected void DashToTarget(Action _onComplete)
   // {
   //    TrackEntry entry = PlayCastAnimation();
   //    BattleObject mainTarget = battleField.battleObjMgr.GetBattleObject((int)tagUseSkillAttack.AttackID);
   //    if (mainTarget == null)
   //    {
   //       Debug.LogError("目标为空 mainTarget == null AttackID : " + tagUseSkillAttack.AttackID);
   //       _onComplete?.Invoke();
   //       return;
   //    }
   //    //   做一个微微的提前
   //    MoveToTarget(mainTarget.Camp, mainTarget.teamHero.positionNum, entry.AnimationEnd - 0.05f, () => DoSkillLogic(_onComplete));
   // }
   //   这里其实是技能后摇结束的地方
   protected virtual void DoSkillLogic(Action _onComplete = null)
   //   冲撞攻击
   protected void DashCast(Action _onComplete)
   {
      Debug.LogError("DashCast 还没实现");
      ForceFinished();
      //   YYL TODO
      // var entry = caster.motionBase.PlayAnimation(skillConfig.GetMotionName(), false);
      // float animationTime = entry.AnimationTime;
      // int mainTargetPosNum = BattleUtility.GetMainTargetPositionNum(caster, tagUseSkillAttack.HurtList.ToList(), skillConfig);
      // BattleCamp battleCamp = skillConfig.TagFriendly != 0 ? caster.Camp : caster.GetEnemyCamp();
      // RectTransform targetTrans = battleField.GetTeamNode(battleCamp, mainTargetPosNum);
      // var tweener = BattleUtility.MoveToTarget(caster.heroRectTrans, targetTrans, new Vector2(skillConfig.CastDistance, 0), animationTime * 0.9f, () =>
      // {
      //    caster.motionBase.PlayAnimation(MotionName.idle, true);
      //    _onComplete?.Invoke();
      // });
      // battleField.battleTweenMgr.OnPlayTween(tweener);
   }
   protected TrackEntry PlayCastAnimation(Action onComplete = null)
   protected void MoveToTarget(RectTransform target, Vector2 offset, Action _onComplete = null, float speed = 500f)
   {
      // 播放施法动作
      return caster.motionBase.PlaySkillAnimation(skillConfig, onComplete,
            OnStartSkillFrame,//攻击前摇结束
            OnActiveSkillFrame);//攻击中摇结束
   }
      //   原地释放
      if (skillConfig.CastDistance >= 9999)
      {
         _onComplete?.Invoke();
         return;
      }
   public void MoveToTarget(BattleCamp camp, BattleObject target, float duration = 0.2f, Action _onComplete = null)
   {
      targetNode = battleField.GetTeamNode(camp, target);
      Vector2 offset = new Vector2(skillConfig.CastDistance, 0);
      RectTransform selfRect = caster.heroRectTrans;
      RectTransform targetRect = targetNode;
      var tweener = BattleUtility.MoveToTarget(selfRect, targetRect, offset, duration, _onComplete);
      caster.motionBase.PlayAnimation(MotionName.run, true);
      var tweener = BattleUtility.MoveToTarget(caster.heroRectTrans, target, offset, () =>
      {
         caster.motionBase.PlayAnimation(MotionName.idle, true);
         _onComplete?.Invoke();
      }, speed);
      battleField.battleTweenMgr.OnPlayTween(tweener);
   }
   public void MoveToTarget(BattleCamp camp, SkillConfig skillCfg, float duration = 0.2f, Action _onComplete = null)
   {
      targetNode = battleField.GetTeamNode(camp, skillCfg);
      Vector2 offset = new Vector2(skillConfig.CastDistance, 0);
      RectTransform selfRect = caster.heroRectTrans;
      RectTransform targetRect = targetNode;
      var tweener = BattleUtility.MoveToTarget(selfRect, targetRect, offset, duration, _onComplete);
      battleField.battleTweenMgr.OnPlayTween(tweener);
   }
   public void TurnBack(Action _onComplete)
   protected void TurnBack(Action _onComplete, float forward)
   {
      if (skillConfig.CastDistance < 0)
      {
         //   转身
         caster.heroGo.transform.localScale = new Vector3(-1, 1, 1);
         Vector3 scale = caster.heroGo.transform.localScale;
         scale.x = Mathf.Abs(scale.x) * forward;
         caster.heroGo.transform.localScale = scale;
      }
      _onComplete?.Invoke();
   }
   public void BackToOrigin(Action _onComplete = null)
   protected void CastToEnemy()
   {
      RectTransform selfRect = caster.heroRectTrans;
      Vector2 targetAnchoredPos = Vector2.zero;
      var tween = selfRect.DOAnchorPos(targetAnchoredPos, 0.2f)
         .SetEase(Ease.Linear)
         .OnComplete(() =>
         {
            //   转成正确方向
            caster.heroGo.transform.localScale = Vector3.one;
            _onComplete?.Invoke();
         });
      battleField.battleTweenMgr.OnPlayTween(tween);
      RectTransform target = battleField.GetTeamNode(caster.GetEnemyCamp(), skillConfig);
      MoveToTarget(target, new Vector2(skillConfig.CastDistance, 0), () =>
      {
         //   到位置转身(不一定非要转身 但是流程要写)
         TurnBack(() =>
         {
            //   到达目标位置
            CastImpl(() =>
            {
               TurnBack(
                  () =>
                  {
                     //   回到原来的位置
                     MoveToTarget(battleField.GetTeamNode(caster.Camp, caster.teamHero.positionNum), Vector2.zero,
                        OnAttackFinish, 750F);
                  }
               , -1f);
            });
         }, -1f);
      });
   }
   protected void CastToTarget()
   {
      // 目标是敌方主目标
      if (tagUseSkillAttack.HurtCount <= 0)
      {
         Debug.LogError("技能攻击包没有目标 HurtCount <= 0");
         OnSkillFinished();
         return;
      }
      int mainTargetPosNum = BattleUtility.GetMainTargetPositionNum(caster, tagUseSkillAttack.HurtList.ToList(), skillConfig);
      BattleCamp battleCamp = skillConfig.TagFriendly != 0 ? caster.Camp : caster.GetEnemyCamp();
      RectTransform targetTrans = battleField.GetTeamNode(battleCamp, mainTargetPosNum);
      MoveToTarget(targetTrans, new Vector2(skillConfig.CastDistance, 0), () =>
      {
         //   到位置转身(不一定非要转身 但是流程要写)
         TurnBack(() =>
         {
            //   到达目标位置
            CastImpl(() =>
            {
               TurnBack(
                  () =>
                  {
                     RectTransform rectTransform = battleField.GetTeamNode(caster.Camp, caster.teamHero.positionNum);
                     //   回到原来的位置
                     MoveToTarget(rectTransform, Vector2.zero, OnAttackFinish, 750F);
                  }
               , -1f);
            });
         }, -1f);
      });
   }
   protected virtual void OnAllAttackMoveFinished()
   {
      moveFinished = true;
   }
   protected void CastToAllies()
   {
      RectTransform target = battleField.GetTeamNode(caster.Camp, skillConfig);
      MoveToTarget(target, new Vector2(skillConfig.CastDistance, 0), () =>
      {
         //   到位置转身(不一定非要转身 但是流程要写)
         TurnBack(() =>
         {
            //   到达目标位置
            CastImpl(() =>
            {
               TurnBack(
                  () =>
                  {
                     //   回到原来的位置
                     MoveToTarget(battleField.GetTeamNode(caster.Camp, caster.teamHero.positionNum),
                        Vector2.zero, OnAttackFinish, 750F);
                  }
               , -1f);
            });
         }, -1f);
      });
   }
   protected void OnAttackFinish()
   {
      TurnBack(null, 1f);
      OnAllAttackMoveFinished();
      caster.motionBase.PlayAnimation(MotionName.idle, true);
   }
   //   承载技能大部分的逻辑
   protected TrackEntry CastImpl(Action onComplete = null)
   {
      // 播放施法动作
      //   onComplete是指施法动作播放完的回调 不代表是技能结束
      //   具体技能结束的时间应该看技能对应的逻辑
      //   这里只提供6个动作相关的函数
      // OnSkillStart 动作第一帧
      // OnStartSkillFrameEnd 前摇结束
      // OnMiddleFrameStart 中摇开始
      // OnMiddleFrameEnd 中摇结束
      // OnFinalFrameStart 后摇开始
      // OnFinalFrameEnd 后摇结束
      return caster.motionBase.PlaySkillAnimation(skillConfig, this, onComplete);
   }
   //   技能开始
   public void OnSkillStart()
   {
      skillEffect = SkillEffectFactory.CreateSkillEffect(
            caster,
            skillConfig,
            tagUseSkillAttack
         );
      // if (skillEffect != null)
      {
         skillEffect.Play(OnHitTargets);
      }
   }
   //   技能前摇帧结束
   public virtual void OnStartSkillFrameEnd()
   {
   }
   /// <summary>
   /// 中摇开始 times=第几次循环 从0开始
   /// </summary>
   /// <param name="times"></param>
   public virtual void OnMiddleFrameStart(int times)
   {
      // if (skillEffect != null)
      {
         skillEffect.OnMiddleFrameStart(times);
      }
   }
   public virtual void OnMiddleFrameEnd(int times, int hitIndex)
   {
      // if (skillEffect != null)
      {
         skillEffect.OnMiddleFrameEnd(times, hitIndex);
      }
   }
   /// <summary>
   /// 后摇开始
   /// </summary>
   public virtual void OnFinalFrameStart()
   {
      // if (skillEffect != null)
      {
         skillEffect.OnFinalFrameStart();
      }
   }
   /// <summary>
   /// 后摇结束
   /// </summary>
   public virtual void OnFinalFrameEnd()
   {
      // if (skillEffect != null)
      {
         skillEffect.OnFinalFrameEnd();
      }
      HandleDead();
   }
   protected void HighLightAllTargets()
   {
      // 高亮所有目标
      HashSet<BattleObject> highlightList = new HashSet<BattleObject>(battleField.battleObjMgr.GetBattleObjList(tagUseSkillAttack));
      highlightList.Add(caster);
      if (skillConfig.FuncType != 2)
         return;
      // 高亮所有目标
      List<BattleObject> targetList = battleField.battleObjMgr.GetBattleObjList(tagUseSkillAttack);
      List<BattleObject> highlightList = new List<BattleObject>(targetList);
      highlightList.Add(caster);
      List<BattleObject> allList = battleField.battleObjMgr.allBattleObjDict.Values.ToList<BattleObject>();
      caster.heroInfoBar.SetActive(false);
      for (int i = 0; i < allList.Count; i++)
      {
         BattleObject bo = allList[i];
         if (highlightList.Contains(bo))
         {
            bo.layerMgr.SetFront();
            bo.heroInfoBar.SetActive(true);
            bo.heroRectTrans.SetParent(battleField.battleRootNode.skillFrontNode, true);
         }
         else
         {
            bo.layerMgr.SetBack();
            bo.heroRectTrans.SetParent(battleField.battleRootNode.skillBackNode, true);
         }
         if (targetList.Contains(bo))
         {
            bo.heroInfoBar.SetActive(true);
         }
         else
         {
            bo.heroInfoBar.SetActive(false);
         }
      }
      caster.layerMgr.SetSortingOrder(BattleConst.ActiveHeroActionSortingOrder);
      battleField.battleRootNode.skillMaskNode.SetActive(true);
      // caster.battleField.skillMask
      //   把这些BO全高亮 或者说把除了这些的都放在遮罩后面
      //   YYL TODO
   }
@@ -245,163 +432,151 @@
            continue;
         }
         // 伤害分布 (万分比)
         int[] damageDivide = skillConfig.DamageDivide[_hitIndex];
         long totalDamage = GeneralDefine.GetFactValue(hurt.HurtHP, hurt.HurtHPEx);
         // 保证所有分配项加起来等于totalDamage,避免因整除导致的误差
         List<long> damageList = DivideDamageToList(damageDivide, totalDamage);
         OnHitEachTarget(target, totalDamage, damageList, ref hurt);
         OnHitEachTarget(_hitIndex, target, hurt);
      }
   }
   /// <summary>
   /// 保证所有分配项加起来等于totalDamage,避免因整除导致的误差
   /// </summary>
   protected 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;
   }
   protected virtual void OnHitEachTarget(BattleObject target, long totalDamage, List<long> damageList, ref HB427_tagSCUseSkill.tagSCUseSkillHurt hurt)
   protected virtual void OnHitEachTarget(int _hitIndex, BattleObject target, HB427_tagSCUseSkill.tagSCUseSkillHurt hurt)
   {
      target.Hurt(damageList, totalDamage, hurt.AttackTypes);
      // 伤害分布 (万分比)
      // Debug.LogError("skillConfig.DamageDivide.Count " + skillConfig.DamageDivide.Length + " _hitIndex " + _hitIndex);
      int[] damageDivide = skillConfig.DamageDivide[_hitIndex];
      //   击中目标的时候,不管近战远程 都确认一下是否有爆炸特效 然后播放
      if (skillConfig.ExplosionEffectId > 0)
      {
         // 播放爆炸特效
         target.battleField.battleEffectMgr.PlayEffect(
            target.ObjID,
            skillConfig.ExplosionEffectId,
            target.heroGo.transform
         );
      }
      long totalDamage = GeneralDefine.GetFactValue(hurt.HurtHP, hurt.HurtHPEx);
      //   受伤之后辨别死亡状态 死亡包其实前后帧会多次触发 应该要即时remove其他的包来保证不重复
      if (target.IsDead())
      {
         // SkillRecordAction里的drop事件前移到dead之后
         HB422_tagMCTurnFightObjDead deadPack = FindDeadPack(target);
         CheckAfterDeadhPack(target, deadPack);
         if (deadPack != null)
         {
            //   处理掉落包
            for (int i = 0; i < dropPackList.Count; i++)
            {
               PackageRegedit.Distribute(dropPackList[i]);
            }
      // 保证所有分配项加起来等于totalDamage,避免因整除导致的误差
      List<long> damageList = BattleUtility.DivideDamageToList(damageDivide, totalDamage);
      // public uint ObjID;
      // public uint AttackTypes;        // 飘血类型汇总,支持多种类型并存,如无视防御且暴击同时被格挡,二进制或运算最终值;0-失败;1-普通;2-回血;5-格挡;6-无视防御;7-暴击;9-闪避
      // public uint HurtHP;        // 飘血值,求余亿部分
      // public uint HurtHPEx;        // 飘血值,整除亿部分
      // public uint CurHP;        // 更新剩余血量,求余亿部分
      // public uint CurHPEx;        // 更新剩余血量,整除亿部分
      // public uint SuckHP;        // 本次伤害转化的吸血量
      // public uint BounceHP;        // 本次伤害反弹的伤害量
            dropPackList.Clear();
            target.PushExpPackList(new List<HB405_tagMCAddExp>(expPackList));
      //   TODO YYL AttackTypes  要表现成什么样呢? 支持多种类型并存,如无视防御且暴击同时被格挡,二进制或运算最终值;0-失败;1-普通;2-回血;5-格挡;6-无视防御;7-暴击;9-闪避
      target.Hurt(damageList, totalDamage, hurt, skillConfig);
            expPackList.Clear();
            // 处理死亡包
            PackageRegedit.Distribute(deadPack);
            packList.Remove(deadPack);
         }
         //   复活包暂时不管 可能是技能的包
         // HB423_tagMCTurnFightObjReborn rebornPack = FindRebornPack(target);
         // if (rebornPack != null)
         // {
         //    //   处理复活包
         //    PackageRegedit.Distribute(rebornPack);
         //    packList.Remove(rebornPack);
         // }
      }
      //   TODO YYL 这里是要做统一计算后再hurt跟suckhp还是怎样
      caster.SuckHp(hurt.SuckHP, skillConfig);//   吸血
      caster.HurtByReflect(hurt.BounceHP, skillConfig);// 反弹伤害
   }
   protected HB423_tagMCTurnFightObjReborn FindRebornPack(BattleObject target)
   {
      HB423_tagMCTurnFightObjReborn rebornPack = null;
      for (int i = 0; i < packList.Count; i++)
      {
         var pack = packList[i];
         if (pack is HB423_tagMCTurnFightObjReborn)
         {
            rebornPack = pack as HB423_tagMCTurnFightObjReborn;
            if (rebornPack.ObjID == target.ObjID)
            {
               return rebornPack;
            }
         }
         else if (pack is CustomHB426CombinePack)
         {
            var combinePack = pack as CustomHB426CombinePack;
            if (combinePack.startTag.Tag.StartsWith("Skill_"))
            {
               break; // 找到技能包就不需要再处理了
            }
         }
      }
      return null;
   }
   protected HB422_tagMCTurnFightObjDead FindDeadPack(BattleObject target)
   protected void HandleDead()
   {
      HB422_tagMCTurnFightObjDead deadPack = null;
      for (int i = 0; i < packList.Count; i++)
      {
         var pack = packList[i];
         //   寻找死亡包 找到死亡包之后要找掉落包 不能超过技能包
         if (pack is HB422_tagMCTurnFightObjDead)
         {
            deadPack = pack as HB422_tagMCTurnFightObjDead;
            if (deadPack.ObjID == target.ObjID)
            {
               return deadPack;
            }
         }
         else if (pack is CustomHB426CombinePack)
         {
            //   找死亡包不要越过技能包
            var combinePack = pack as CustomHB426CombinePack;
            if (combinePack.startTag.Tag.StartsWith("Skill_"))
            {
               break;
            }
         }
      }
      return null;
   }
      var deadPackList = BattleUtility.FindDeadPack(packList);
      int deadCount = deadPackList.Count;
   protected void CheckAfterDeadhPack(BattleObject target, HB422_tagMCTurnFightObjDead deadPack)
   {
      if (null == deadPack)
      if (deadCount <= 0)
      {
         //   如果没死亡就不用管
         return;
      }
      var deadPackIndex = packList.IndexOf(deadPack);
      if (deadPackIndex < 0)
      CheckAfterDeadhPack();
      // 处理掉落包 提前distribute之后 PackManager才有掉落物 所以不跟assignexp一样distribute
      foreach (var _dropPack in dropPackList)
      {
         return;
         PackageRegedit.Distribute(_dropPack);
         packList.Remove(_dropPack);
      }
      // 获取掉落物品
      var dropPack = PackManager.Instance.GetSinglePack(PackType.DropItem);
      var itemDict = dropPack.GetAllItems();
      List<ItemModel> itemList = new List<ItemModel>(
         from item in itemDict.Values
         where item != null && item.isAuction
         select item);
      // 分配掉落和经验
      var dropAssign = AssignDrops(itemList, deadCount);
      var expAssign = AssignExp(expPackList, deadCount);
      // 构造 BattleDrops 并分配
      for (int i = 0; i < deadCount; i++)
      {
         BattleObject deadTarget = battleField.battleObjMgr.GetBattleObject((int)deadPackList[i].ObjID);
         List<ItemModel> itemModelDrops = dropAssign[i];
         List<int> itemModelDropsIndexList = new List<int>(
            from item in itemModelDrops select item.gridIndex);
         BattleDrops battleDrops = new BattleDrops()
         {
            rectTransform = deadTarget.heroRectTrans,
            dropItemPackIndex = itemModelDropsIndexList,
            expDrops = expAssign[i]
         };
         deadTarget.PushDropItems(battleDrops);
      }
      // 分发死亡包
      battleField.OnObjsDead(new List<HB422_tagMCTurnFightObjDead>(deadPackList));
      foreach (var deadPack in deadPackList)
      {
         packList.Remove(deadPack);
      }
      deadPackList.Clear();
   }
   // 分配掉落
   protected List<List<ItemModel>> AssignDrops(List<ItemModel> itemList, int deadCount)
   {
      var dropAssign = new List<List<ItemModel>>(deadCount);
      for (int i = 0; i < deadCount; i++)
         dropAssign.Add(new List<ItemModel>());
      for (int i = 0; i < itemList.Count; i++)
         dropAssign[i % deadCount].Add(itemList[i]);
      return dropAssign;
   }
   // 分配经验:每个原始包都平均分配到每个死亡对象
   protected List<List<HB405_tagMCAddExp>> AssignExp(List<HB405_tagMCAddExp> expList, int deadCount)
   {
      var expAssign = new List<List<HB405_tagMCAddExp>>(deadCount);
      for (int i = 0; i < deadCount; i++)
         expAssign.Add(new List<HB405_tagMCAddExp>());
      foreach (var expPack in expList)
      {
         long totalExp = GeneralDefine.GetFactValue(expPack.Exp, expPack.ExpPoint);
         long avgExp = totalExp / deadCount;
         long remain = totalExp % deadCount;
         for (int i = 0; i < deadCount; i++)
         {
            long assignExp = avgExp + (i < remain ? 1 : 0);
            long expPoint = assignExp / 100000000;
            long exp = assignExp % 100000000;
            var newPack = new HB405_tagMCAddExp
            {
               Exp = (uint)exp,
               ExpPoint = (uint)expPoint,
               Source = expPack.Source // 保持原包来源
            };
            expAssign[i].Add(newPack);
         }
         packList.Remove(expPack);
      }
      return expAssign;
   }
   protected void CheckAfterDeadhPack()
   {
      List<int> removeIndexList = new List<int>();
      for (int i = deadPackIndex + 1; i < packList.Count; i++)
      for (int i = 0; i < packList.Count; i++)
      {
         var pack = packList[i];
@@ -454,16 +629,48 @@
   }
   public virtual bool IsFinished()
   {
      return isFinished;
      if (skillEffect != null)
      {
         if (!skillEffect.IsFinished())
         {
            return false;
         }
         else
         {
            OnSkillFinished();
            skillEffect = null;
         }
      }
      if (isFinished && moveFinished)
      {
         List<BattleObject> allList = battleField.battleObjMgr.allBattleObjDict.Values.ToList<BattleObject>();
         for (int i = 0; i < allList.Count; i++)
         {
            BattleObject bo = allList[i];
            bo.layerMgr.SetFront();
            bo.heroRectTrans.SetParent(battleField.GetTeamNode(bo.Camp, bo.teamHero.positionNum), true);
            bo.heroInfoBar.SetActive(true);
         }
         battleField.battleRootNode.skillMaskNode.SetActive(false);
         return true;
      }
      else
      {
         return false;
      }
   }
   public virtual void ForceFinished()
   {
      skillEffect?.ForceFinished();
      isFinished = true;
   }
      moveFinished = true;
   public void OnSkillFinished()
   {
      while (packList.Count > 0)
      {
         var pack = packList[0];
@@ -474,11 +681,43 @@
            var combinePack = pack as CustomHB426CombinePack;
            if (combinePack.startTag.Tag.StartsWith("Skill_"))
            {
               BattleDebug.LogError("other skill casting " + combinePack.startTag.Tag);
               otherSkillAction = combinePack.CreateSkillAction();
               //   强制结束其他技能
               otherSkillAction.ForceFinish();
               return;
            }
         }
         PackageRegedit.Distribute(pack);
      }
   }
   public void OnSkillFinished()
   {
      if (skillEffect != null)
      {
         if (!skillEffect.IsFinished())
         {
            return;
         }
      }
      while (packList.Count > 0)
      {
         var pack = packList[0];
         packList.RemoveAt(0);
         if (pack is CustomHB426CombinePack)
         {
            var combinePack = pack as CustomHB426CombinePack;
            if (combinePack.startTag.Tag.StartsWith("Skill_"))
            {
               BattleDebug.LogError("other skill casting " + combinePack.startTag.Tag);
               otherSkillAction = combinePack.CreateSkillAction();
               return;
            }
         }
         PackageRegedit.Distribute(pack);
      }