using System;
using System.Collections.Generic;
using System.Linq;
using Cielonos.MainGame.Characters;
using Sirenix.OdinInspector;
using SLSUtilities.FunctionalAnimation;
using UnityEngine;
namespace Cielonos.MainGame
{
///
/// 角色(Player/Enemy)专属战斗动作子模块。
/// 继承通用播放器并实现 ISubmodule 接口,融合了战斗受击、硬直打断、预输入重置等复杂的动作决策机制。
///
public partial class FuncAnimSubmodule : FuncAnimPlayer, ISubmodule
{
/// 本模块的所有者(动作子控制器)
public AnimationSubcontrollerBase Owner { get; set; }
// 兼容原 owner(小写)访问
private AnimationSubcontrollerBase animationSc => Owner;
private CharacterBase character => Owner.owner;
private Player player => character as Player;
private static readonly int ActionSpeed = Animator.StringToHash("ActionSpeed");
public FuncAnimSubmodule(AnimationSubcontrollerBase owner, string animatorLayerName)
: base(owner.owner, animatorLayerName)
{
this.Owner = owner;
}
///
/// 注册战斗动作数据。如果是玩家控制的角色,还将注册动画剪辑到重载控制器中。
///
public override void Add(FuncAnimData animation)
{
base.Add(animation);
if (player != null)
{
player.animationSc.SetOverride(animation.animInfo.stateName, animation.animationClip);
}
}
///
/// 战斗特有:执行动作可播放性评估。
///
public bool CheckPlayability(RuntimeFuncAnim oldFuncAnim, RuntimeFuncAnim newFuncAnim)
{
if (oldFuncAnim != null)
{
var oldData = oldFuncAnim.funcAnimData;
var newData = newFuncAnim.funcAnimData;
// 检测是否配置了特殊动作打断区间(SpecifiedActionDisruption)
FuncAnimInterval specifiedInterval = oldData.intervals.FirstOrDefault(interval =>
interval.intervalType == IntervalType.Custom && interval.intervalName == "SpecifiedActionDisruption");
if (specifiedInterval != null)
{
if (oldData.interactions.TryGetValue("SpecifiedActionDisruption", out List interactions))
{
// 若新动作在当前动作的指定打断白名单内,只需判断是否处于该打断区间
if (interactions.Contains(newData.animInfo.animationName))
{
return animationSc.CurrentIntervals.Contains(specifiedInterval);
}
}
}
}
return CheckPlayability(newFuncAnim.funcAnimData.animInfo.disruptionType);
}
///
/// 战斗特有:评估当前角色的硬直状态与打断类型。
///
public bool CheckPlayability(DisruptionType disruptionType = DisruptionType.NormalAction)
{
if (character.statusSm.isDead) return false;
// 无前动作、死亡动作或强制动作(Must)拥有绝对执行权
if (currentRuntimeFuncAnim == null || disruptionType == DisruptionType.Death || disruptionType == DisruptionType.Must)
{
return true;
}
if (disruptionType == DisruptionType.ForcedAction)
{
return animationSc.disruptionStatus[DisruptionType.ForcedAction] ||
animationSc.disruptionStatus[DisruptionType.NormalAction] ||
animationSc.disruptionStatus[DisruptionType.Movement];
}
if (disruptionType == DisruptionType.NormalAction)
{
return animationSc.disruptionStatus[DisruptionType.NormalAction] || animationSc.disruptionStatus[DisruptionType.Movement];
}
if (disruptionType == DisruptionType.Movement)
{
return animationSc.disruptionStatus[DisruptionType.Movement];
}
throw new Exception("Invalid DisruptionType for checking playability.");
}
///
/// 战斗特有:评估当前状态机内是否存在允许打断该动作的标记。
///
public bool CheckDisruption(DisruptionType disruptionType)
{
return disruptionType switch
{
DisruptionType.None => false,
DisruptionType.Must or DisruptionType.Death => true,
_ => animationSc.disruptionStatus[disruptionType]
};
}
///
/// 覆写播放核心:融入战斗专属的可播放性检测、前置打断清理及事件控制。
///
public override bool Play(FuncAnimData funcAnimData, float animationSpeedMultiplier = 1f, float transitionDuration = 0.1f,
bool isNormalizedTransition = false, float normalizedStartTime = -1f, List runtimeStartEvents = null)
{
var newRtFuncAnim = new RuntimeFuncAnim(funcAnimData, Executor);
// 1. 战斗独有的前置条件与打断优先级检测
if (!CheckPlayability(currentRuntimeFuncAnim, newRtFuncAnim))
{
return false;
}
// 2. 如果存在正在播放的动作,且还未打断,执行战斗特有的清理
if (currentRuntimeFuncAnim != null && !currentRuntimeFuncAnim.isDisrupted)
{
ResetPlayerPreinput();
animationSc.disruptionStatus[DisruptionType.NormalExternal] = false;
animationSc.disruptionStatus[DisruptionType.NormalAction] = false;
animationSc.disruptionStatus[DisruptionType.Movement] = false;
}
// 3. 计算实际应用的速度缩放(受 affectsBySpeedMultiplier 标记约束)
float finalSpeedMultiplier = funcAnimData.animInfo.isAffectedBySpeedMultiplier ? animationSpeedMultiplier : 1f;
// 4. 委派底层物理播放器执行 CrossFade 过渡与启动
bool playSuccess = base.Play(funcAnimData, finalSpeedMultiplier, transitionDuration, isNormalizedTransition, normalizedStartTime, runtimeStartEvents);
if (playSuccess)
{
// 5. 重置运动阻断状态
animationSc.isDisablingMoveXZ = false;
animationSc.isDisablingMoveY = false;
// 6. 重置事件分发指针
currentRuntimeFuncAnim.dataAnimEventIndex = 0;
currentRuntimeFuncAnim.runtimeAnimEventIndex = 0;
currentRuntimeFuncAnim.SetUpdateUntilEventsStatus();
}
return playSuccess;
}
///
/// 通过动作名停止播放动作(支持打断级别限制)。
///
public bool Stop(string animationName, DisruptionType disruptionType = DisruptionType.Must, float transitionDuration = 0.1f)
{
if (currentRuntimeFuncAnim == null) return true;
if (currentRuntimeFuncAnim.funcAnimData.animInfo.animationName != animationName) return false;
return Stop(disruptionType, transitionDuration);
}
///
/// 覆写停止动作:注入战斗受击与强制打断的校验逻辑。
///
public bool Stop(DisruptionType disruptionType, float transitionDuration = 0.1f)
{
if (disruptionType == DisruptionType.None) return false;
if (currentRuntimeFuncAnim == null) return true;
// 1. 校验是否允许打断
if (disruptionType != DisruptionType.Death && disruptionType != DisruptionType.Must)
{
if (!CheckDisruption(disruptionType))
{
return false;
}
}
// 2. 清理预输入缓存
if (!currentRuntimeFuncAnim.isDisrupted)
{
ResetPlayerPreinput();
}
// 3. 调用底层物理播放器执行淡出到 Empty 状态
bool stopSuccess = base.Stop(transitionDuration);
if (stopSuccess)
{
// 4. 重置子控制器的全局打断标识
animationSc.disruptionStatus[DisruptionType.NormalExternal] = false;
animationSc.disruptionStatus[DisruptionType.NormalAction] = false;
animationSc.disruptionStatus[DisruptionType.Movement] = false;
}
return stopSuccess;
}
///
/// 兼容通用物理停止(默认转换为 Must 强制打断)。
///
public override bool Stop(float transitionDuration = 0.1f)
{
return Stop(DisruptionType.Must, transitionDuration);
}
///
/// 驱动更新时间轴时钟(供 Subcontroller.Update 轮询)。
///
public void UpdateTime()
{
Update(character.selfTimeSm.DeltaTime);
}
///
/// 覆写时钟推进逻辑:包含动作自然播放完毕时,对输入缓存和打断标志的战斗退场清理。
///
public override void Update(float deltaTime)
{
if (currentRuntimeFuncAnim == null)
{
currentPlaySpeedMultiplier = 1f;
return;
}
// 1. 推进播放时间
float step = deltaTime * currentData.animInfo.overridePlaySpeed * currentPlaySpeedMultiplier;
currentRuntimeFuncAnim.currentPlayTime += step;
currentRuntimeFuncAnim.currentTotalPlayTime += step;
float clipLength = currentClip != null ? currentClip.length : 0f;
// 2. 监测播放结束
if (currentPlayTime >= clipLength)
{
UpdateEvents();
if (currentClip != null && currentClip.isLooping)
{
// 循环复位
currentRuntimeFuncAnim.currentPlayTime = clipLength > 0f ? currentRuntimeFuncAnim.currentPlayTime % clipLength : 0f;
currentRuntimeFuncAnim.dataAnimEventIndex = 0;
currentRuntimeFuncAnim.runtimeAnimEventIndex = 0;
currentRuntimeFuncAnim.ResetUpdateUntilEventsStatus();
currentRuntimeFuncAnim.InvokeStartEvents();
}
else
{
// 非循环动作播放完毕:执行退场事件并注入战斗清理逻辑
if (!currentRuntimeFuncAnim.isDisrupted)
{
currentRuntimeFuncAnim.isDisrupted = true;
currentRuntimeFuncAnim.InvokeDisruptionEvents();
currentRuntimeFuncAnim.InvokeEndEvents();
ResetPlayerPreinput();
}
// 清空全局打断标志位
animationSc.disruptionStatus[DisruptionType.NormalExternal] = false;
animationSc.disruptionStatus[DisruptionType.NormalAction] = false;
animationSc.disruptionStatus[DisruptionType.Movement] = false;
currentRuntimeFuncAnim = null;
}
}
}
private void ResetPlayerPreinput() => player?.inputSc.preinputSubmodule.Reset();
}
public partial class FuncAnimSubmodule
{
///
/// 获取经播放速度缩放后的指定区间类型的持续时间。
/// 主要提供给武器及连招系统判断前摇/判定区间长度。
///
public float GetIntervalScaledDuration(IntervalType type)
{
if (currentData == null) return 0f;
FuncAnimInterval interval = currentData.intervals.Find(i => i.intervalType == type);
if (interval == null) return 0f;
float baseDuration = interval.EndTime - interval.StartTime;
float speed = currentData.animInfo.overridePlaySpeed * currentPlaySpeedMultiplier;
return speed > 0f ? baseDuration / speed : 0f;
}
}
}