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(); // 修复:添加空值检查
|
}
|
}
|