hch
2025-08-15 7bd85eb8d0b9664f4645534b42c82ab0f32754d3
Main/System/Battle/Skill/SkillBase.cs
@@ -3,11 +3,13 @@
using System;
using DG.Tweening;
using Spine;
using System.Linq;
using PlasticGui.WorkspaceWindow.BranchExplorer;
public class SkillBase
{
   protected H0604_tagUseSkillAttack tagUseSkillAttack;
   protected HB427_tagSCUseSkill tagUseSkillAttack;
   protected SkillConfig skillConfig;
@@ -27,9 +29,13 @@
   protected List<GameNetPackBasic> packList;
   protected SkillBase otherSkill;
   protected SkillRecordAction otherSkillAction;
   public SkillBase(BattleObject _caster, SkillConfig _skillCfg, H0604_tagUseSkillAttack vNetData, List<GameNetPackBasic> _packList, BattleField _battleField = null)
   protected List<H0704_tagRolePackRefresh> dropPackList = new List<H0704_tagRolePackRefresh>();
   protected List<HB405_tagMCAddExp> expPackList = new List<HB405_tagMCAddExp>();
   public SkillBase(BattleObject _caster, SkillConfig _skillCfg, HB427_tagSCUseSkill vNetData, List<GameNetPackBasic> _packList, BattleField _battleField = null)
   {
      caster = _caster;
      skillConfig = _skillCfg;
@@ -37,7 +43,7 @@
      battleField = _battleField;
      packList = _packList;
   }
   }
   public virtual void Run()
   {
@@ -45,22 +51,23 @@
      {
         curFrame++;
      }
      if (otherSkillAction != null)
      {
         if (otherSkillAction.IsFinished())
         {
            otherSkillAction = null;
            OnSkillFinished();
         }
         else
         {
            otherSkillAction.Run();
         }
      }
   }
   protected virtual void OnActiveSkillFrame()
   {
   }
   protected virtual void OnStartSkillFrame()
   {
   }
   protected virtual void OnEndSkillFrame()
   {
   }
   public void Pause()
   {
@@ -88,51 +95,45 @@
            PlayCastAnimation(() => DoSkillLogic(OnSkillFinished));
            break;
         case SkillCastMode.Enemy:
            MoveToTarget(caster.GetEnemyCamp(), skillConfig.CastIndexNum, _onComplete: () => TurnBack(() => PlayCastAnimation(() => DoSkillLogic(() => { BackToOrigin(OnSkillFinished); }))));
            MoveToTarget(caster.GetEnemyCamp(), skillConfig, _onComplete: () => TurnBack(() => PlayCastAnimation(() => DoSkillLogic(() => { BackToOrigin(OnSkillFinished); }))));
            break;
         case SkillCastMode.Target:
            // 目标是敌方主目标
            BattleObject mainTarget = battleField.battleObjMgr.GetBattleObject((int)tagUseSkillAttack.AttackID);
            if (mainTarget == null)
            if (tagUseSkillAttack.HurtCount <= 0)
            {
               Debug.LogError("目标为空 mainTarget == null AttackID : " + tagUseSkillAttack.AttackID);
               Debug.LogError("技能攻击包没有目标 HurtCount <= 0");
               OnSkillFinished();
               return;
            }
            MoveToTarget(mainTarget.Camp, mainTarget.teamHero.positionNum, _onComplete: () => TurnBack(() => PlayCastAnimation(() => DoSkillLogic(() => { BackToOrigin(OnSkillFinished); }))));
            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); }))));
            break;
         case SkillCastMode.Allies:
            MoveToTarget(caster.Camp, skillConfig.CastIndexNum, _onComplete: () => TurnBack(() => PlayCastAnimation(() => DoSkillLogic(() => { BackToOrigin(OnSkillFinished); }))));
            MoveToTarget(caster.Camp, skillConfig, _onComplete: () => TurnBack(() => PlayCastAnimation(() => DoSkillLogic(() => { BackToOrigin(OnSkillFinished); }))));
            break;
         // case SkillCastMode.DashCast:
         //    DashToTarget(() => BackToOrigin(OnSkillFinished));
         //    break;
         default:
            Debug.LogError("暂时不支持其他的方式释放 有需求请联系策划");
            Debug.LogError("暂时不支持其他的方式释放 有需求请联系策划" + skillConfig.SkillID);
            OnSkillFinished();
            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 TrackEntry PlayCastAnimation(Action onComplete = null)
@@ -143,13 +144,24 @@
            OnActiveSkillFrame);//攻击中摇结束
   }
   public void MoveToTarget(BattleCamp camp, int index, float duration = 0.2f, Action _onComplete = null)
   public void MoveToTarget(BattleCamp camp, BattleObject target, float duration = 0.2f, Action _onComplete = null)
   {
      // GetTargetNode();
      targetNode = battleField.GetTeamNode(camp, index);
      targetNode = battleField.GetTeamNode(camp, target);
      Vector2 offset = new Vector2(skillConfig.CastDistance, 0);
      RectTransform selfRect = caster.heroGo.transform as RectTransform;
      RectTransform selfRect = caster.heroRectTrans;
      RectTransform targetRect = targetNode;
      var tweener = BattleUtility.MoveToTarget(selfRect, targetRect, offset, duration, _onComplete);
      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);
@@ -168,7 +180,7 @@
   public void BackToOrigin(Action _onComplete = null)
   {
      RectTransform selfRect = caster.heroGo.transform as RectTransform;
      RectTransform selfRect = caster.heroRectTrans;
      Vector2 targetAnchoredPos = Vector2.zero;
      var tween = selfRect.DOAnchorPos(targetAnchoredPos, 0.2f)
         .SetEase(Ease.Linear)
@@ -188,18 +200,17 @@
      HashSet<BattleObject> highlightList = new HashSet<BattleObject>(battleField.battleObjMgr.GetBattleObjList(tagUseSkillAttack));
      highlightList.Add(caster);
      //   把这些BO全高亮 或者说把除了这些的都放在遮罩后面
      //   YYL TODO
   }
   //   命中目标后的回调 正常是以各技能的方式来处理的
   protected virtual void OnHitTargets(int _hitIndex, List<H0604_tagUseSkillAttack.tagSkillHurtObj> hitList)
   protected virtual void OnHitTargets(int _hitIndex, List<HB427_tagSCUseSkill.tagSCUseSkillHurt> hitList)
   {
      for (int i = 0; i < hitList.Count; i++)
      {
         H0604_tagUseSkillAttack.tagSkillHurtObj hurt = hitList[i];
         HB427_tagSCUseSkill.tagSCUseSkillHurt hurt = hitList[i];
         BattleObject target = caster.battleField.battleObjMgr.GetBattleObject((int)hurt.ObjID);
         if (target == null)
@@ -208,28 +219,146 @@
            continue;
         }
         // 伤害结算
         // 伤害分布 (万分比)
         int[] damageDivide = skillConfig.DamageDivide[_hitIndex];
         List<long> damageList = new List<long>();
         long totalDamage = GeneralDefine.GetFactValue(hurt.HurtHP, hurt.HurtHPEx);
         for (int j = 0; j < damageDivide.Length; j++)
         {
            long damage = totalDamage * damageDivide[j] / 10000;
            damageList.Add(damage);
         }
         // 保证所有分配项加起来等于totalDamage,避免因整除导致的误差
         List<long> damageList = DivideDamageToList(damageDivide, totalDamage);
         OnHitEachTarget(target, damageList, ref hurt);
         OnHitEachTarget(target, totalDamage, damageList, ref hurt);
      }
      HandleDead();
   }
   protected virtual void OnHitEachTarget(BattleObject target, List<long> damageList, ref H0604_tagUseSkillAttack.tagSkillHurtObj hurt)
   protected void HandleDead()
   {
      target.Hurt(damageList, hurt.AttackType);
      var deadPackList = FindDeadPack();
      CheckAfterDeadhPack();
            //   击中目标的时候,不管近战远程 都确认一下是否有爆炸特效 然后播放
      // 处理掉落包 提前distribute之后 PackManager才有掉落物 所以不跟assignexp一样distribute
      foreach (var _dropPack in dropPackList)
      {
         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);
      int deadCount = deadPackList.Count;
      // 分配掉落和经验
      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);
      }
      // 分发死亡包
      foreach (var deadPack in deadPackList)
      {
         PackageRegedit.Distribute(deadPack);
         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;
   }
   /// <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)
   {
      target.Hurt(damageList, totalDamage, hurt.AttackTypes);
      //   击中目标的时候,不管近战远程 都确认一下是否有爆炸特效 然后播放
      if (skillConfig.ExplosionEffectId > 0)
      {
         // 播放爆炸特效
@@ -241,6 +370,111 @@
      }
   }
   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 List<HB422_tagMCTurnFightObjDead> FindDeadPack()
   {
      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;
   }
   protected void CheckAfterDeadhPack()
   {
      List<int> removeIndexList = new List<int>();
      for (int i = 0; i < packList.Count; i++)
      {
         var pack = packList[i];
         //    复活基本都靠技能包
         if (pack is CustomHB426CombinePack)
         {
            var combinePack = pack as CustomHB426CombinePack;
            if (combinePack.startTag.Tag.StartsWith("Skill_"))
            {
               break; // 找到技能包就不需要再处理了
            }
         }
         else if (pack is H0704_tagRolePackRefresh)
         {
            var h0704Pack = pack as H0704_tagRolePackRefresh;
            if (h0704Pack.PackType == (byte)PackType.DropItem)
            {
               //   掉落的
               if (h0704Pack.IsBind == 1)
               {
                  //   掉落的物品
                  dropPackList.Add(h0704Pack);
                  removeIndexList.Add(i);
               }
               else if (h0704Pack.IsBind == 0)
               {
                  //   替换的
               }
            }
         }
         else if (pack is HB405_tagMCAddExp)
         {
            var h405Pack = pack as HB405_tagMCAddExp;
            //B4 05 获得经验 #tagMCAddExp 通知获得的经验,
            //可用于做经验获得表现 Source = 2 时为主线击杀怪物获得经验
            if (h405Pack.Source == 2)
            {
               expPackList.Add(h405Pack);
               removeIndexList.Add(i);
            }
         }
      }
      for (int i = removeIndexList.Count - 1; i >= 0; i--)
      {
         packList.RemoveAt(removeIndexList[i]);
      }
   }
   public virtual bool IsFinished()
   {
      return isFinished;
@@ -253,6 +487,40 @@
   public void OnSkillFinished()
   {
      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_"))
            {
               otherSkillAction = combinePack.CreateSkillAction();
               return;
            }
         }
         PackageRegedit.Distribute(pack);
      }
      isFinished = true;
   }
   protected virtual void OnActiveSkillFrame()
   {
   }
   protected virtual void OnStartSkillFrame()
   {
   }
   protected virtual void OnEndSkillFrame()
   {
   }
}