using System.Collections.Generic; using UnityEngine; using DG.Tweening; using Spine; using System.Linq; using System; // SkillBase:技能运行时基类。 // 本类使用 partial 拆分为多个文件,按职责分组: // SkillBase.cs 字段、构造、公共入口(Cast/Run/OnSkillStart/各 Frame 回调 等) // SkillBase.Cast.cs 施法阶段:移动、动画、残影、高亮、攻击回合结束 // SkillBase.Hit.cs 命中阶段:OnHit 分发到主目标 / 溅射目标 / 命中提示 // SkillBase.SubSkill.cs 前置内嵌子技能的收集与投递 // SkillBase.Death.cs 死亡包与掉落/经验分配 // SkillBase.Buff.cs Buff 包(HB428/HB429)的收集与分发 // SkillBase.Finish.cs 完成判定与强制结束 public partial class SkillBase { // ===== 常量 ===== const float moveTime = 0.5f; private static readonly Color colorGreen = new Color(33f / 255f, 133f / 255f, 6f / 255f); private static readonly Color colorBlue = new Color(40f / 255f, 87f / 255f, 189f / 255f); // ===== 核心引用 ===== public HB427_tagSCUseSkill tagUseSkillAttack; public SkillConfig skillConfig; public SkillSkinConfig skillSkinConfig; public BattleObject caster = null; // 施法者 protected BattleField battleField = null; // 战场 protected RectTransform targetNode = null; // 目标节点 protected List packList; // ===== 命中效果 ===== protected SkillEffect skillEffect; // ===== 子技能/子动作等待列表 ===== protected List currentWaitingSkill = new List(); // ===== 死亡相关临时数据 ===== protected List dropPackList = new List(); protected List expPackList = new List(); private Dictionary tempDropList = new Dictionary(); private Dictionary tempDeadPackList = new Dictionary(); // ===== Buff 相关包集合,支持 HB428(刷新) 和 HB429(删除) ===== protected List buffPackCollections = new List(); // ===== 生命周期状态(4 个并行里程碑位,合并到同一 Flags 字段) ===== // Started : 已进入施法阶段(OnSkillStart 调用后) // MoveCompleted : 位移已收尾(OnAllAttackMoveFinished) // MotionCompleted: 技能动画已播放完(OnFinalFrameEnd) // Finished : 包列表已处理完(OnSkillFinished / ForceFinished 结尾) // 4 个里程碑相互独立,非线性阶段,不能用单一 state 表达。 [System.Flags] protected enum SkillStateFlags { None = 0, Started = 1 << 0, MoveCompleted = 1 << 1, MotionCompleted = 1 << 2, Finished = 1 << 3, } private SkillStateFlags _stateFlags = SkillStateFlags.None; /// 当前技能状态位(只读,调试用)。 protected SkillStateFlags StateFlags => _stateFlags; #if UNITY_EDITOR /// 供外部调试/诊断打印用,非编辑器下不编译。 public string StateFlagsForDebug => _stateFlags.ToString(); #endif /// 是否已进入施法阶段(OnSkillStart 调用后为 true)。 public bool isPlay { get => (_stateFlags & SkillStateFlags.Started) != 0; set => SetFlag(SkillStateFlags.Started, value); } /// 包列表是否已全部处理完。 protected bool isFinished { get => (_stateFlags & SkillStateFlags.Finished) != 0; set => SetFlag(SkillStateFlags.Finished, value); } /// 位移是否已收尾。 protected bool moveFinished { get => (_stateFlags & SkillStateFlags.MoveCompleted) != 0; set => SetFlag(SkillStateFlags.MoveCompleted, value); } /// 技能动画是否已播放完。 protected bool isMotionCompleted { get => (_stateFlags & SkillStateFlags.MotionCompleted) != 0; set => SetFlag(SkillStateFlags.MotionCompleted, value); } private void SetFlag(SkillStateFlags flag, bool value) { #if UNITY_EDITOR // 记录状态变更:卡死/卡活的排查利器。 // 编辑器下只在值真正发生改变时打印,避免刷屏。 bool oldValue = (_stateFlags & flag) != 0; if (oldValue != value) { int skillId = skillConfig != null ? skillConfig.SkillID : 0; ulong casterId = tagUseSkillAttack != null ? tagUseSkillAttack.ObjID : 0UL; BattleDebug.LogError( $"SkillBase.StateFlags 变更:skillId={skillId} caster={casterId} " + $"{flag}: {oldValue} -> {value} (before={_stateFlags})"); } #endif if (value) _stateFlags |= flag; else _stateFlags &= ~flag; } // ===== 父子关系 ===== public SkillBase fromSkill; // 父RecordAction(SkillRecordAction),用于子技能建立父子关系 protected SkillRecordAction ownRecordAction; // ===== 移动速度(残影加速时会改变) ===== private float MoveSpeed = 750f; #if UNITY_EDITOR public static Dictionary changeListDict = new Dictionary(); #endif // 构造函数:初始化技能基础数据 public SkillBase(BattleObject _caster, SkillConfig _skillCfg, HB427_tagSCUseSkill vNetData, List _packList, BattleField _battleField = null) { caster = _caster; skillConfig = _skillCfg; tagUseSkillAttack = vNetData; battleField = _battleField; packList = _packList; if (_caster is HeroBattleObject heroBattleObject) { skillSkinConfig = skillConfig.GetSkillSkinConfig(heroBattleObject.teamHero.SkinID); if (null == skillSkinConfig) { Debug.LogError("找不到技能皮肤表 " + "skillId: " + skillConfig.SkillID + " skinId: " + heroBattleObject.teamHero.SkinID); } } else { skillSkinConfig = skillConfig.GetOriginSkinConfig(); } // 注册正在释放的技能 if (battleField != null && caster != null) { battleField.AddCastingSkill(caster.ObjID, this); } SafetyCheck(); } public virtual void AfterAddToQueue() { } // 设置父RecordAction public void SetOwnRecordAction(SkillRecordAction recordAction) { ownRecordAction = recordAction; } private void PinrtHB427Hp() { #if UNITY_EDITOR string skillDetail = "SkillCaster : " + tagUseSkillAttack.ObjID + " -> cast SkillID: " + skillConfig.SkillID + "\n"; skillDetail += "------------------ HurtList ------------------\n"; for (int i = 0; i < tagUseSkillAttack.HurtCount; i++) { var Hurt = tagUseSkillAttack.HurtList[i]; BattleObject battleObject = caster.battleField.battleObjMgr.GetBattleObject((int)Hurt.ObjID); string targetName = battleObject != null ? battleObject.GetName() : "Unknown"; long hurtHp = GeneralDefine.GetFactValue(Hurt.HurtHP, Hurt.HurtHPEx); long curHp = GeneralDefine.GetFactValue(Hurt.CurHP, Hurt.CurHPEx); skillDetail += $" [{i}] Target: {targetName} (ObjID:{Hurt.ObjID})\n"; skillDetail += $" HurtHP: {hurtHp}\n"; skillDetail += $" CurHP: {curHp}\n"; skillDetail += $" SuckHP: {Hurt.SuckHP}\n"; skillDetail += $" BounceHP: {Hurt.BounceHP}\n"; skillDetail += $" AttackTypes: {Hurt.AttackTypes}\n"; if (Hurt.HurtListEx != null && Hurt.HurtListEx.Length > 0) { skillDetail += $" HurtListEx ({Hurt.HurtListEx.Length}):\n"; for (int j = 0; j < Hurt.HurtListEx.Length; j++) { var hurtEx = Hurt.HurtListEx[j]; long hurtExHp = GeneralDefine.GetFactValue(hurtEx.HurtHP, hurtEx.HurtHPEx); long curExHp = GeneralDefine.GetFactValue(hurtEx.CurHP, hurtEx.CurHPEx); skillDetail += $" [{j}] ObjID:{hurtEx.ObjID} HurtHP:{hurtExHp} CurHP:{curExHp} SuckHP:{hurtEx.SuckHP} AttackTypes:{hurtEx.AttackTypes}\n"; } } } skillDetail += "------------------ HurtListEx ------------------\n"; if (tagUseSkillAttack.HurtListEx != null) { for (int i = 0; i < tagUseSkillAttack.HurtListEx.Length; i++) { var HurtEx = tagUseSkillAttack.HurtListEx[i]; BattleObject battleObject = caster.battleField.battleObjMgr.GetBattleObject((int)HurtEx.ObjID); string targetName = battleObject != null ? battleObject.GetName() : "Unknown"; long hurtHp = GeneralDefine.GetFactValue(HurtEx.HurtHP, HurtEx.HurtHPEx); long curHp = GeneralDefine.GetFactValue(HurtEx.CurHP, HurtEx.CurHPEx); skillDetail += $" [{i}] Target: {targetName} (ObjID:{HurtEx.ObjID})\n"; skillDetail += $" HurtHP: {hurtHp}\n"; skillDetail += $" CurHP: {curHp}\n"; skillDetail += $" SuckHP: {HurtEx.SuckHP}\n"; skillDetail += $" AttackTypes: {HurtEx.AttackTypes}\n"; } } skillDetail += "------------------ END ------------------\n"; if (changeListDict.ContainsKey(caster.battleField.guid)) { string origin = changeListDict[caster.battleField.guid]; origin += skillDetail; changeListDict[caster.battleField.guid] = origin; } else changeListDict.Add(caster.battleField.guid, skillDetail); Debug.LogError("skillDetail : " + skillDetail); #endif } private void SafetyCheck() { #if UNITY_EDITOR if (Launch.Instance.isOpenSkillLogFile) { PinrtHB427Hp(); } #endif bool safety = caster != null && skillConfig != null && tagUseSkillAttack != null && battleField != null; if (!safety) { Debug.LogError("SkillBase SafetyCheck failed! Caster or SkillConfig or TagUseSkillAttack or BattleField is null, or Caster is dead."); ForceFinished(); } } // 技能运行主逻辑:处理技能效果和其他技能动作 public virtual void Run() { if (skillEffect != null) { if (skillEffect.IsFinished()) { skillEffect = null; OnSkillFinished(); } else { skillEffect.Run(); } return; } } // 技能开始回调:处理死亡、子技能、技能效果初始化 public void OnSkillStart() { if (isPlay) { Debug.LogError(" play twice OnSkillStart skillId :" + skillConfig.SkillID); return; } // 先把死亡包收集了 HandleDead(); // 再处理 内嵌技能 ProcessSubSkill(); skillEffect = SkillEffectFactory.CreateSkillEffect(this, caster, skillConfig, skillSkinConfig, tagUseSkillAttack); skillEffect.Play(OnHitTargets); isPlay = true; } // ===== 技能节拍回调 ===== // 技能前摇结束回调 public virtual void OnStartSkillFrameEnd() { } // 技能中摇开始回调:通知技能效果处理中摇开始 public virtual void OnMiddleFrameStart(int times) { skillEffect?.OnMiddleFrameStart(times); // 修复:添加空值检查 } // 技能中摇结束回调:通知技能效果处理中摇结束 public virtual void OnMiddleFrameEnd(int times, int hitIndex) { skillEffect?.OnMiddleFrameEnd(times, hitIndex); // 修复:添加空值检查 } // 技能后摇开始回调:通知技能效果处理后摇开始 public virtual void OnFinalFrameStart() { skillEffect?.OnFinalFrameStart(); // 修复:添加空值检查 } // 技能后摇结束回调:通知技能效果处理后摇结束 public virtual void OnFinalFrameEnd() { // 标记动画播放完成 isMotionCompleted = true; BattleDebug.LogError($"SkillBase.OnFinalFrameEnd: 技能 {skillConfig?.SkillID} 动画播放完成"); skillEffect?.OnFinalFrameEnd(); // 修复:添加空值检查 } }