yyl
2026-04-22 b8f554f03a6114db353736741eda63bdd6c63854
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
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<GameNetPackBasic> packList;
 
    // ===== 命中效果 =====
    protected SkillEffect skillEffect;
 
    // ===== 子技能/子动作等待列表 =====
    protected List<RecordAction> currentWaitingSkill = new List<RecordAction>();
 
    // ===== 死亡相关临时数据 =====
    protected List<H0704_tagRolePackRefresh> dropPackList = new List<H0704_tagRolePackRefresh>();
    protected List<HB405_tagMCAddExp> expPackList = new List<HB405_tagMCAddExp>();
    private Dictionary<int, BattleDrops> tempDropList = new Dictionary<int, BattleDrops>();
    private Dictionary<int, BattleDeadPack> tempDeadPackList = new Dictionary<int, BattleDeadPack>();
 
    // ===== Buff 相关包集合,支持 HB428(刷新) 和 HB429(删除) =====
    protected List<GameNetPackBasic> buffPackCollections = new List<GameNetPackBasic>();
 
    // ===== 生命周期状态(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;
 
    /// <summary>当前技能状态位(只读,调试用)。</summary>
    protected SkillStateFlags StateFlags => _stateFlags;
 
#if UNITY_EDITOR
    /// <summary>供外部调试/诊断打印用,非编辑器下不编译。</summary>
    public string StateFlagsForDebug => _stateFlags.ToString();
#endif
 
    /// <summary>是否已进入施法阶段(OnSkillStart 调用后为 true)。</summary>
    public bool isPlay
    {
        get => (_stateFlags & SkillStateFlags.Started) != 0;
        set => SetFlag(SkillStateFlags.Started, value);
    }
 
    /// <summary>包列表是否已全部处理完。</summary>
    protected bool isFinished
    {
        get => (_stateFlags & SkillStateFlags.Finished) != 0;
        set => SetFlag(SkillStateFlags.Finished, value);
    }
 
    /// <summary>位移是否已收尾。</summary>
    protected bool moveFinished
    {
        get => (_stateFlags & SkillStateFlags.MoveCompleted) != 0;
        set => SetFlag(SkillStateFlags.MoveCompleted, value);
    }
 
    /// <summary>技能动画是否已播放完。</summary>
    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<string, string> changeListDict = new Dictionary<string, string>();
#endif
 
    // 构造函数:初始化技能基础数据
    public SkillBase(BattleObject _caster, SkillConfig _skillCfg, HB427_tagSCUseSkill vNetData, List<GameNetPackBasic> _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(); // 修复:添加空值检查
    }
}