using UnityEngine;
|
using System.Collections.Generic;
|
using Cysharp.Threading.Tasks;
|
using System;
|
|
|
// 如果这个Action卡住了 大概率是因为 动作被其他动作中断了 导致没有回调
|
public class DeathRecordAction : RecordAction
|
{
|
protected List<BattleDeadPack> deadPackList = new List<BattleDeadPack>();
|
|
protected Dictionary<BattleDeadPack, SkillRecordAction> deathActionDict = new Dictionary<BattleDeadPack, SkillRecordAction>();
|
|
protected Dictionary<BattleDeadPack, Func<bool>> deadActionStatesDict = new Dictionary<BattleDeadPack, Func<bool>>();
|
|
protected Dictionary<int, bool> dropStateDict = new Dictionary<int, bool>();
|
|
protected bool hasDeathTriggerSkill = false;
|
|
// 标记是否已经分发了死亡后的包
|
protected bool hasDistributedPacksAfterDeath = false;
|
|
private SkillRecordAction parentSkillAction = null;
|
|
private SkillRecordAction playSkillRecordAction = null;
|
|
#if UNITY_EDITOR
|
private int deathDebugNotFinishedCount = 0;
|
private const int DeathDebugFirstDumpCount = 120;
|
private const int DeathDebugRepeatDumpCount = 180;
|
#endif
|
|
public DeathRecordAction(BattleField _battleField, List<BattleDeadPack> _deadPackList, SkillRecordAction _parentSkillAction = null, SkillRecordAction _playSkillRecordAction = null)
|
: base(RecordActionType.Death, _battleField, null)
|
{
|
deadPackList = _deadPackList;
|
CheckHasDeathTriggerSkill();
|
|
// for (int i = 0; i < deadPackList.Count; i++)
|
// {
|
// BattleObject battleObject = battleField.battleObjMgr.GetBattleObject((int)deadPackList[i].deadPack.ObjID);
|
// Debug.LogError($"DeathRecordAction: 初始化死亡动作,死亡对象名字={battleObject?.GetName()}, hasDeathTriggerSkill={deadPackList[i].deadTriggerSkill != null}");
|
// }
|
|
parentSkillAction = _parentSkillAction;
|
playSkillRecordAction = _playSkillRecordAction;
|
|
}
|
|
protected void CheckHasDeathTriggerSkill()
|
{
|
foreach (var battleDeadPack in deadPackList)
|
{
|
if (battleDeadPack.deadTriggerSkill != null)
|
{
|
hasDeathTriggerSkill = true;
|
break;
|
}
|
}
|
}
|
|
|
public override void Run()
|
{
|
base.Run();
|
|
// 该死的正常死
|
// 有技能的则按照顺序播放死亡技能 后再正常死
|
if (isFinish)
|
return;
|
|
|
if (!isRunOnce)
|
{
|
isRunOnce = true;
|
|
#if UNITY_EDITOR
|
// DumpDeathDebugState("首次Run");
|
#endif
|
|
SkillRecordAction waitingAnimeAction = playSkillRecordAction;
|
|
foreach (var battleDeadPack in deadPackList)
|
{
|
if (battleDeadPack.deadTriggerSkill != null)
|
{
|
SkillRecordAction skillAction = battleDeadPack.deadTriggerSkill.CreateSkillAction();
|
if (null != skillAction)
|
{
|
deathActionDict.Add(battleDeadPack, skillAction);
|
|
#if UNITY_EDITOR
|
// BattleDebug.LogError($"[DeathRecordAction诊断] 创建死亡触发技能 {FormatDeadPackForDebug(battleDeadPack)} -> {FormatSkillActionForDebug(skillAction)} waitingAnimeAction={(waitingAnimeAction == null ? "null" : FormatSkillActionForDebug(waitingAnimeAction))}");
|
#endif
|
|
// 【使用 BattleField.recordPlayer】
|
// 原因:死亡触发技能是顶层的RecordAction,不是在某个RecordAction内部产生的
|
// 虽然可能是因为技能导致的死亡,但死亡触发的技能本身应该是独立的
|
// 使用ImmediatelyPlay并设置WaitingPlay=true,可以让死亡技能等待导致死亡的技能完成
|
// 如果DeathRecordAction有父节点(导致死亡的技能),则等待那个父节点
|
// 否则等待DeathRecordAction本身
|
if (waitingAnimeAction == null)
|
{
|
battleField.recordPlayer.ImmediatelyPlay(skillAction, this, true);
|
}
|
else
|
{
|
waitingAnimeAction.GetInnerRecordPlayer().ImmediatelyPlay(skillAction, waitingAnimeAction);
|
}
|
|
if (battleDeadPack.deadTriggerSkill.NeedWaiting() || waitingAnimeAction != null)
|
{
|
waitingAnimeAction = skillAction;
|
}
|
}
|
#if UNITY_EDITOR
|
else
|
{
|
// BattleDebug.LogError($"[DeathRecordAction诊断] 死亡触发技能创建失败 {FormatDeadPackForDebug(battleDeadPack)}");
|
}
|
#endif
|
}
|
else
|
{
|
#if UNITY_EDITOR
|
// BattleDebug.LogError($"[DeathRecordAction诊断] 创建普通死亡状态 {FormatDeadPackForDebug(battleDeadPack)}");
|
#endif
|
deadActionStatesDict.Add(battleDeadPack, CreateDeadActionState(battleDeadPack));
|
}
|
}
|
}
|
else
|
{
|
List<BattleDeadPack> finishedKeys = new List<BattleDeadPack>();
|
foreach (var kv in deathActionDict)
|
{
|
if (!kv.Key.isPlaySkill)
|
{
|
// battleField.recordPlayer.ImmediatelyPlay(kv.Value);
|
kv.Key.isPlaySkill = true;
|
}
|
|
if (!kv.Value.IsFinished())
|
{
|
kv.Value.Run();
|
}
|
else
|
{
|
deadActionStatesDict.Add(kv.Key, CreateDeadActionState(kv.Key, true));
|
finishedKeys.Add(kv.Key);
|
}
|
break;
|
}
|
|
foreach (var key in finishedKeys)
|
{
|
deathActionDict.Remove(key);
|
}
|
|
if (0 == deathActionDict.Count)
|
{
|
hasDeathTriggerSkill = false;
|
}
|
|
int completeNum = 0;
|
|
foreach (var kv in deadActionStatesDict)
|
{
|
if (kv.Value())
|
{
|
completeNum++;
|
}
|
}
|
|
#if UNITY_EDITOR
|
// ReportDeathNotFinishedIfNeeded(completeNum);
|
#endif
|
|
if (completeNum == deadPackList.Count)
|
{
|
// 死亡处理完成后,分发死亡后的包
|
if (!hasDistributedPacksAfterDeath)
|
{
|
hasDistributedPacksAfterDeath = true;
|
DistributePacksAfterDeath();
|
}
|
|
isActionCompleted = true;
|
isFinish = true;
|
}
|
}
|
}
|
|
|
|
// 重写IsActionCompleted,死亡动作是否完成
|
public override bool IsActionCompleted()
|
{
|
return isActionCompleted || isFinish;
|
}
|
|
private Func<bool> CreateDeadActionState(BattleDeadPack deadPack, bool withoutAnime = false)
|
{
|
BattleObject deadObj = battleField.battleObjMgr.GetBattleObject((int)deadPack.deadPack.ObjID);
|
if (null == deadObj)
|
{
|
#if UNITY_EDITOR
|
// BattleDebug.LogError($"[DeathRecordAction诊断] 死亡对象不存在,直接视为完成 {FormatDeadPackForDebug(deadPack)}");
|
#endif
|
return () => true;
|
}
|
|
if (deadObj.IsReborning())
|
{
|
#if UNITY_EDITOR
|
// BattleDebug.LogError($"[DeathRecordAction诊断] 创建死亡状态时对象已在复活流程,直接视为完成 {FormatDeadPackForDebug(deadPack)} withoutAnime={withoutAnime}");
|
#endif
|
return () => true;
|
}
|
|
if (deadObj.GetCurHp() > 0)
|
{
|
#if UNITY_EDITOR
|
// BattleDebug.LogError($"[DeathRecordAction诊断] 创建死亡状态时对象血量已回正,直接视为完成 {FormatDeadPackForDebug(deadPack)} withoutAnime={withoutAnime}");
|
#endif
|
return () => true;
|
}
|
|
if (deadObj.IsDead())
|
{
|
#if UNITY_EDITOR
|
// BattleDebug.LogError($"[DeathRecordAction诊断] 创建死亡状态时对象已死亡,直接视为完成 {FormatDeadPackForDebug(deadPack)} withoutAnime={withoutAnime}");
|
#endif
|
return () => true;
|
}
|
|
bool playDeath = false;
|
|
bool isComplete = false;
|
|
#if UNITY_EDITOR
|
bool loggedWaitingCanStartDeath = false;
|
bool loggedReborningComplete = false;
|
bool loggedHpRestoredComplete = false;
|
bool loggedAlreadyDeadComplete = false;
|
#endif
|
|
// 如果没有释放技能 则直接死亡
|
if (deadObj.CanStartDeath())
|
{
|
PerformDrop(deadObj);
|
|
deadObj.OnDeath(() => {
|
isComplete = true;
|
#if UNITY_EDITOR
|
// BattleDebug.LogError($"[DeathRecordAction诊断] 死亡动画回调完成 {FormatDeadPackForDebug(deadPack)} withoutAnime={withoutAnime}");
|
#endif
|
}, withoutAnime);
|
|
playDeath = true;
|
#if UNITY_EDITOR
|
// BattleDebug.LogError($"[DeathRecordAction诊断] 开始播放死亡表现 {FormatDeadPackForDebug(deadPack)} withoutAnime={withoutAnime}");
|
#endif
|
}
|
#if UNITY_EDITOR
|
else
|
{
|
loggedWaitingCanStartDeath = true;
|
// BattleDebug.LogError($"[DeathRecordAction诊断] 暂不能播放死亡表现,等待 CanStartDeath {FormatDeadPackForDebug(deadPack)} deadObj.CanStartDeath={deadObj.CanStartDeath()} isReborning={deadObj.IsReborning()} isDead={deadObj.IsDead()}");
|
}
|
#endif
|
|
|
|
return () =>
|
{
|
// 还没播放死亡 并且没释放其他技能
|
if (!playDeath && deadObj.CanStartDeath())
|
{
|
PerformDrop(deadObj);
|
|
deadObj.OnDeath(() => {
|
isComplete = true;
|
#if UNITY_EDITOR
|
// BattleDebug.LogError($"[DeathRecordAction诊断] 延迟死亡动画回调完成 {FormatDeadPackForDebug(deadPack)} withoutAnime={withoutAnime}");
|
#endif
|
}, withoutAnime);
|
|
playDeath = true;
|
#if UNITY_EDITOR
|
// BattleDebug.LogError($"[DeathRecordAction诊断] 延迟开始播放死亡表现 {FormatDeadPackForDebug(deadPack)} withoutAnime={withoutAnime}");
|
#endif
|
}
|
#if UNITY_EDITOR
|
else if (!playDeath && !loggedWaitingCanStartDeath)
|
{
|
loggedWaitingCanStartDeath = true;
|
// BattleDebug.LogError($"[DeathRecordAction诊断] 仍不能播放死亡表现,等待 CanStartDeath {FormatDeadPackForDebug(deadPack)} deadObj.CanStartDeath={deadObj.CanStartDeath()} isReborning={deadObj.IsReborning()} isDead={deadObj.IsDead()}");
|
}
|
#endif
|
|
if (deadObj.IsReborning())
|
{
|
isComplete = true;
|
#if UNITY_EDITOR
|
if (!loggedReborningComplete)
|
{
|
loggedReborningComplete = true;
|
// BattleDebug.LogError($"[DeathRecordAction诊断] 死亡对象已进入复活流程,死亡状态视为完成 {FormatDeadPackForDebug(deadPack)}");
|
}
|
#endif
|
}
|
|
if (!isComplete && deadObj.GetCurHp() > 0)
|
{
|
isComplete = true;
|
#if UNITY_EDITOR
|
if (!loggedHpRestoredComplete)
|
{
|
loggedHpRestoredComplete = true;
|
// BattleDebug.LogError($"[DeathRecordAction诊断] 死亡对象血量已回正,死亡状态视为完成 {FormatDeadPackForDebug(deadPack)}");
|
}
|
#endif
|
}
|
|
if (!isComplete && deadObj.IsDead())
|
{
|
isComplete = true;
|
#if UNITY_EDITOR
|
if (!loggedAlreadyDeadComplete)
|
{
|
loggedAlreadyDeadComplete = true;
|
// BattleDebug.LogError($"[DeathRecordAction诊断] 死亡对象已处于死亡状态,死亡状态视为完成 {FormatDeadPackForDebug(deadPack)}");
|
}
|
#endif
|
}
|
|
return isComplete;
|
};
|
}
|
|
public override void ForceFinish()
|
{
|
if (isFinish)
|
return;
|
|
isFinish = true;
|
|
// 强制结束所有死亡触发技能
|
foreach (var kv in deathActionDict)
|
{
|
kv.Value.ForceFinish();
|
}
|
deathActionDict.Clear();
|
|
// 清理状态字典,防止回调继续等待
|
deadActionStatesDict.Clear();
|
|
// 直接结束所有死亡对象
|
foreach (var deadPack in deadPackList)
|
{
|
BattleObject deadObj = battleField.battleObjMgr.GetBattleObject((int)deadPack.deadPack.ObjID);
|
if (null != deadObj)
|
{
|
PerformDrop(deadObj);
|
deadObj.SetDeath();
|
}
|
}
|
|
// 强制分发死亡后的包
|
if (!hasDistributedPacksAfterDeath)
|
{
|
hasDistributedPacksAfterDeath = true;
|
DistributePacksAfterDeath();
|
}
|
|
base.ForceFinish();
|
}
|
|
private void PerformDrop(BattleObject deadObj)
|
{
|
// 只有主线掉落物品
|
if (battleField.MapID == 1 || battleField.MapID == 2)
|
{
|
if (dropStateDict.ContainsKey(deadObj.ObjID))
|
{
|
return;
|
}
|
dropStateDict.Add(deadObj.ObjID, true);
|
deadObj.PerformDrop();
|
}
|
}
|
|
protected bool HasDeathTriggerSkill()
|
{
|
return hasDeathTriggerSkill;
|
}
|
|
// 分发死亡后的包
|
protected void DistributePacksAfterDeath()
|
{
|
// 遍历所有死亡包,分发它们的 packListAfterDeath
|
foreach (var deadPack in deadPackList)
|
{
|
#if UNITY_EDITOR
|
// BattleDebug.LogError($"[DeathRecordAction诊断] 准备分发死亡后包 {FormatDeadPackForDebug(deadPack)} afterDeathCount={deadPack.packListAfterDeath?.Count ?? 0}");
|
#endif
|
if (deadPack.packListAfterDeath != null && deadPack.packListAfterDeath.Count > 0)
|
{
|
foreach (var pack in deadPack.packListAfterDeath)
|
{
|
// 获取包的类型和UID用于调试
|
#if UNITY_EDITOR
|
string packType = pack.GetType().Name;
|
ulong packUID = GetPackUIDForDebug(pack);
|
// BattleDebug.LogError($"[DeathRecordAction诊断] 分发死亡后包 objId={GetDeadObjIdForDebug(deadPack)} packType={packType} packUID={packUID} playSkillRecordAction={(playSkillRecordAction == null ? "null" : FormatSkillActionForDebug(playSkillRecordAction))}");
|
#endif
|
|
|
// 特殊处理 CustomHB426CombinePack:使用其自己的 Distribute 方法
|
if (pack is CustomHB426CombinePack combinePack)
|
{
|
combinePack.Distribute(playSkillRecordAction);
|
}
|
// 特殊处理 HB427_tagSCUseSkill:创建技能包并分发
|
else if (pack is HB427_tagSCUseSkill skillPack)
|
{
|
var skillAction = CustomHB426CombinePack.CreateSkillAction(battleField.guid, new List<GameNetPackBasic>() { skillPack });
|
if (skillAction != null)
|
{
|
if (playSkillRecordAction != null)
|
{
|
playSkillRecordAction.GetInnerRecordPlayer().PlayRecord(skillAction);
|
}
|
else
|
{
|
battleField.PlayRecord(skillAction);
|
}
|
}
|
}
|
else
|
{
|
// 【使用 causingRecordAction 或 battleField.recordPlayer】
|
// 原因:死亡后的包应该回到导致死亡的技能所在的上下文
|
// 如果有 parentSkillAction(导致死亡的技能),则分发到它的 innerRecordPlayer
|
// 否则分发到 battleField.recordPlayer(顶层)
|
if (playSkillRecordAction != null)
|
{
|
PackageRegeditEx.DistributeToRecordAction(pack, playSkillRecordAction);
|
}
|
else
|
{
|
// 如果没有 causingRecordAction,直接分发(使用原始的 PackageRegedit)
|
PackageRegedit.Distribute(pack);
|
}
|
}
|
}
|
|
// 分发完成后清理列表,防止重复分发和内存泄漏
|
deadPack.packListAfterDeath.Clear();
|
}
|
}
|
}
|
|
public override bool CanStartExecution()
|
{
|
// 如果不是WaitingPlay模式,直接可以执行
|
if (!isWaitingPlay)
|
{
|
return true;
|
}
|
|
// 如果没有父节点,也可以执行
|
if (parentSkillAction == null)
|
{
|
return true;
|
}
|
|
// 可以先执行没有死亡触发技能的死亡动作
|
if (!HasDeathTriggerSkill())
|
{
|
return true;
|
}
|
|
// WaitingPlay模式下,需要等待直接父节点的动作完成(不管父节点是否WaitingPlay)
|
if (!parentSkillAction.IsActionCompleted())
|
{
|
BattleDebug.LogError($"RecordAction.CanStartExecution: {this.GetType().Name} 等待父节点 {parentSkillAction.GetType().Name} 动作完成");
|
return false;
|
}
|
|
return true;
|
}
|
|
public override bool NeedWaitSibling()
|
{
|
return HasDeathTriggerSkill();
|
}
|
|
#if UNITY_EDITOR
|
private void ReportDeathNotFinishedIfNeeded(int completeNum)
|
{
|
if (isFinish || isActionCompleted)
|
{
|
deathDebugNotFinishedCount = 0;
|
return;
|
}
|
|
deathDebugNotFinishedCount++;
|
bool firstDump = deathDebugNotFinishedCount == DeathDebugFirstDumpCount;
|
bool repeatDump = deathDebugNotFinishedCount > DeathDebugFirstDumpCount
|
&& (deathDebugNotFinishedCount - DeathDebugFirstDumpCount) % DeathDebugRepeatDumpCount == 0;
|
if (!firstDump && !repeatDump)
|
{
|
return;
|
}
|
|
DumpDeathDebugState($"未完成 completeNum={completeNum}/{deadPackList.Count} checkCount={deathDebugNotFinishedCount}");
|
}
|
|
private void DumpDeathDebugState(string reason)
|
{
|
var sb = new System.Text.StringBuilder();
|
sb.Append($"[DeathRecordAction诊断] {reason} actionID={actionID} ");
|
sb.Append($"deadPackList.Count={deadPackList?.Count ?? 0} ");
|
sb.Append($"deathActionDict.Count={deathActionDict.Count} ");
|
sb.Append($"deadActionStatesDict.Count={deadActionStatesDict.Count} ");
|
sb.Append($"hasDeathTriggerSkill={hasDeathTriggerSkill} ");
|
sb.Append($"hasDistributedPacksAfterDeath={hasDistributedPacksAfterDeath} ");
|
sb.Append($"parentSkillAction={(parentSkillAction == null ? "null" : FormatSkillActionForDebug(parentSkillAction))} ");
|
sb.Append($"playSkillRecordAction={(playSkillRecordAction == null ? "null" : FormatSkillActionForDebug(playSkillRecordAction))}");
|
|
if (deadPackList != null)
|
{
|
for (int i = 0; i < deadPackList.Count; i++)
|
{
|
BattleDeadPack deadPack = deadPackList[i];
|
sb.Append("\n [").Append(i).Append("] ").Append(FormatDeadPackForDebug(deadPack));
|
if (deathActionDict.TryGetValue(deadPack, out var skillAction))
|
{
|
sb.Append(" deathSkill=").Append(FormatSkillActionForDebug(skillAction));
|
}
|
sb.Append(" hasState=").Append(deadActionStatesDict.ContainsKey(deadPack));
|
}
|
}
|
|
// BattleDebug.LogError(sb.ToString());
|
}
|
|
private string FormatDeadPackForDebug(BattleDeadPack deadPack)
|
{
|
ulong objId = GetDeadObjIdForDebug(deadPack);
|
BattleObject deadObj = null;
|
if (battleField != null && battleField.battleObjMgr != null && objId > 0)
|
{
|
deadObj = battleField.battleObjMgr.GetBattleObject((int)objId);
|
}
|
|
string objName = deadObj?.GetName() ?? "null";
|
bool canStartDeath = deadObj != null && deadObj.CanStartDeath();
|
bool isReborning = deadObj != null && deadObj.IsReborning();
|
bool isDead = deadObj != null && deadObj.IsDead();
|
long curHp = deadObj != null ? deadObj.GetCurHp() : 0;
|
int afterDeathCount = deadPack?.packListAfterDeath?.Count ?? 0;
|
string triggerTag = deadPack?.deadTriggerSkill?.startTag?.Tag ?? "null";
|
|
return $"objId={objId} name={objName} hp={curHp} isDead={isDead} canStartDeath={canStartDeath} isReborning={isReborning} hasDeadTriggerSkill={deadPack?.deadTriggerSkill != null} triggerTag={triggerTag} isPlaySkill={deadPack?.isPlaySkill ?? false} afterDeathCount={afterDeathCount}";
|
}
|
|
private ulong GetDeadObjIdForDebug(BattleDeadPack deadPack)
|
{
|
if (deadPack == null || deadPack.deadPack == null)
|
{
|
return 0;
|
}
|
return deadPack.deadPack.ObjID;
|
}
|
|
private string FormatSkillActionForDebug(SkillRecordAction skillAction)
|
{
|
if (skillAction == null)
|
{
|
return "null";
|
}
|
|
SkillBase skillBase = skillAction.skillBase;
|
if (skillBase == null)
|
{
|
return $"SkillRecordAction(actionID={skillAction.actionID}, skillBase=null)";
|
}
|
|
int skillId = skillBase.skillConfig != null ? skillBase.skillConfig.SkillID : 0;
|
ulong casterId = skillBase.tagUseSkillAttack != null ? skillBase.tagUseSkillAttack.ObjID : 0;
|
return $"SkillRecordAction(actionID={skillAction.actionID}, skillId={skillId}, caster={casterId}, StateFlags={skillBase.StateFlagsForDebug}, IsActionCompleted={skillAction.IsActionCompleted()})";
|
}
|
|
private ulong GetPackUIDForDebug(GameNetPackBasic pack)
|
{
|
if (pack == null)
|
{
|
return 0;
|
}
|
|
if (pack is HB422_tagMCTurnFightObjDead deadPack)
|
{
|
return deadPack.packUID;
|
}
|
|
if (pack is CustomHB426CombinePack combinePack)
|
{
|
return combinePack.packUID;
|
}
|
|
var packUIDField = pack.GetType().GetField("packUID");
|
if (packUIDField == null)
|
{
|
return 0;
|
}
|
|
object value = packUIDField.GetValue(pack);
|
if (value is ulong ulongValue)
|
{
|
return ulongValue;
|
}
|
if (value is uint uintValue)
|
{
|
return uintValue;
|
}
|
if (value is int intValue)
|
{
|
return (ulong)intValue;
|
}
|
return 0;
|
}
|
|
/// <summary>
|
/// 首次运行时打印日志(仅编辑器)
|
/// 打印死亡对象的名字
|
/// </summary>
|
protected override void PrintFirstRunLog()
|
{
|
if (deadPackList != null && deadPackList.Count > 0)
|
{
|
string deadNames = "";
|
foreach (var deadPack in deadPackList)
|
{
|
BattleObject deadObj = battleField.battleObjMgr.GetBattleObject((int)deadPack.deadPack.ObjID);
|
string name = deadObj?.GetName() ?? "Unknown";
|
deadNames += name + ",";
|
}
|
deadNames = deadNames.TrimEnd(',');
|
Debug.LogError($"[DeathRecordAction首次Run] 死亡对象:{deadNames} 数量:{deadPackList.Count}");
|
}
|
else
|
{
|
base.PrintFirstRunLog();
|
}
|
}
|
#endif
|
}
|