using System; 
 | 
using System.Collections.Generic; 
 | 
using UnityEngine; 
 | 
using Spine.Unity; 
 | 
  
 | 
public class MotionBase 
 | 
{ 
 | 
    public static float MotionTimeScale = 1f; 
 | 
    public static List<string> AttackMotionList = new List<string> 
 | 
    { 
 | 
        MotionName.attack.ToString().ToLower(), 
 | 
        MotionName.angerSkill.ToString().ToLower(), 
 | 
        MotionName.passiveSkill.ToString().ToLower(), 
 | 
    }; 
 | 
  
 | 
    private Dictionary<Spine.TrackEntry, Action> trackEntryCallbacks = new Dictionary<Spine.TrackEntry, Action>(); 
 | 
    public Action OnAttackAnimationComplete; 
 | 
    public Action OnHitAnimationComplete; 
 | 
    private List<Action> runningActions = new List<Action>(); 
 | 
  
 | 
    public SkeletonAnimation skeletonAnim; 
 | 
    protected Spine.AnimationState animState; 
 | 
    protected Spine.Skeleton skeleton; 
 | 
    protected float defaultMixDuration = 0f; 
 | 
    private Spine.TrackEntry currentTrack; 
 | 
    private SkeletonIllusionShadow illusionShadow; 
 | 
    private bool playingSkillAnim = false; 
 | 
  
 | 
    private float pauseTime = 0f; 
 | 
  
 | 
    private float resumeTime = 0f; 
 | 
  
 | 
    public virtual void Init(SkeletonAnimation skelAnim) 
 | 
    { 
 | 
        skeletonAnim = skelAnim; 
 | 
        if (skeletonAnim == null) 
 | 
        { 
 | 
            BattleDebug.LogError("缺少SkeletonGraphic组件!"); 
 | 
            return; 
 | 
        } 
 | 
  
 | 
        animState = skeletonAnim.AnimationState; 
 | 
        skeletonAnim.timeScale = MotionTimeScale; 
 | 
        skeleton = skeletonAnim.Skeleton; 
 | 
  
 | 
        if (animState != null) 
 | 
            animState.Data.DefaultMix = defaultMixDuration; 
 | 
  
 | 
        PlayAnimation(MotionName.idle, true); 
 | 
        SetupAnimationHandlers(); 
 | 
         
 | 
        if (skelAnim.gameObject != null) 
 | 
            illusionShadow = skelAnim.gameObject.AddMissingComponent<SkeletonIllusionShadow>(); 
 | 
    } 
 | 
     
 | 
    public virtual void Release() 
 | 
    { 
 | 
        trackEntryCallbacks.Clear(); 
 | 
        if (animState != null) 
 | 
        { 
 | 
            animState.Complete -= OnAnimationComplete; 
 | 
            animState.ClearTracks(); 
 | 
            animState = null; 
 | 
        } 
 | 
        skeletonAnim = null; 
 | 
        skeleton = null; 
 | 
        currentTrack = null; 
 | 
        playingSkillAnim = false; 
 | 
    } 
 | 
  
 | 
    public virtual Spine.TrackEntry PlayAnimation(MotionName motionName, bool loop, Action onComplete = null) 
 | 
    { 
 | 
        if (playingSkillAnim || animState == null) return null; 
 | 
  
 | 
        if (currentTrack != null && !currentTrack.IsComplete && trackEntryCallbacks.TryGetValue(currentTrack, out var prevCallback)) 
 | 
        { 
 | 
            trackEntryCallbacks.Remove(currentTrack); 
 | 
            prevCallback?.Invoke(); 
 | 
            currentTrack = null; 
 | 
        } 
 | 
  
 | 
        currentTrack = animState.SetAnimation(0, motionName.ToString(), loop); 
 | 
        if (onComplete != null && currentTrack != null) 
 | 
            trackEntryCallbacks[currentTrack] = onComplete; 
 | 
  
 | 
        return currentTrack; 
 | 
    } 
 | 
  
 | 
    private void AddAction(Action action) => runningActions.Add(action); 
 | 
    private void RemoveAction(Action action) => runningActions.Remove(action); 
 | 
  
 | 
    public Spine.TrackEntry PlaySkillAnimation(SkillConfig skillConfig, SkillBase skillBase, bool isSubSkill, Action onComplete = null) 
 | 
    { 
 | 
        if (skillConfig == null) 
 | 
        { 
 | 
            Debug.LogError("技能配置为空,无法播放技能动画"); 
 | 
            return null; 
 | 
        } 
 | 
        if (animState == null || skeleton == null) 
 | 
        { 
 | 
            Debug.LogError("SkeletonGraphic或AnimationState未初始化,无法播放技能动画"); 
 | 
            return null; 
 | 
        } 
 | 
  
 | 
        if (string.IsNullOrEmpty(skillConfig.SkillMotionName)) 
 | 
        { 
 | 
            PlaySkillNoAnim(skillConfig, skillBase, onComplete, isSubSkill); 
 | 
            return null; 
 | 
        } 
 | 
  
 | 
        Spine.Animation targetAnim = FindAnim(skillConfig.SkillMotionName); 
 | 
        if (targetAnim == null) 
 | 
        { 
 | 
            skillBase.ForceFinished(); 
 | 
            return null; 
 | 
        } 
 | 
  
 | 
        return ExecuteSkillAnim(skillConfig, skillBase, onComplete, targetAnim, true, isSubSkill); 
 | 
    } 
 | 
  
 | 
    private Spine.TrackEntry ExecuteSkillAnim(SkillConfig skillConfig, SkillBase skillBase, Action onComplete, 
 | 
        Spine.Animation targetAnim, bool hasAnim, bool isSubSkill) 
 | 
    { 
 | 
  
 | 
        bool isPangdeSkill = 1003020 == skillConfig.SkillID; 
 | 
  
 | 
        int loopCount = skillConfig.LoopCount; 
 | 
        int[] activeFrames = skillConfig.ActiveFrames ?? new int[0]; 
 | 
        int frameCount = activeFrames.Length; 
 | 
        float recoveryFrame = skillConfig.RecoveryFrames; 
 | 
  
 | 
        Spine.TrackEntry skillTrack = null; 
 | 
        if (hasAnim) 
 | 
        { 
 | 
            skillTrack = animState.SetAnimation(0, targetAnim, false); 
 | 
            currentTrack = skillTrack; 
 | 
        } 
 | 
         
 | 
        playingSkillAnim = true; 
 | 
  
 | 
        int currentLoop = 0, triggerCount = 0, failCount = 0; 
 | 
        bool beginTriggered = false, finalStarted = false, finalEnded = false, middleStarted = false; 
 | 
        bool[] triggeredFrames = new bool[frameCount]; 
 | 
        float startTime = hasAnim ? 0 : Time.time; 
 | 
  
 | 
        skillBase.OnSkillStart(); 
 | 
  
 | 
        Action frameHandler = null; 
 | 
        frameHandler = () => 
 | 
        { 
 | 
            if (skillBase.IsFinished()) 
 | 
            { 
 | 
                playingSkillAnim = false; 
 | 
                RemoveAction(frameHandler); 
 | 
                return; 
 | 
            } 
 | 
  
 | 
            float trackTime = 0f; //hasAnim ? skillTrack.TrackTime * skillTrack.TimeScale : (Time.time - startTime) * MotionTimeScale; 
 | 
  
 | 
            if (hasAnim) 
 | 
            { 
 | 
                trackTime = skillTrack.TrackTime; 
 | 
            } 
 | 
            else 
 | 
            { 
 | 
                float adjustedTime = Time.time; 
 | 
                if (pauseTime > 0f && resumeTime > pauseTime) 
 | 
                { 
 | 
                    startTime = startTime + (resumeTime - pauseTime); 
 | 
                    pauseTime = 0f; 
 | 
                    resumeTime = 0f; 
 | 
                } 
 | 
                trackTime = (adjustedTime - startTime) * MotionTimeScale; 
 | 
            } 
 | 
  
 | 
            float currentFrame = trackTime * BattleConst.skillMotionFps; 
 | 
  
 | 
            if (hasAnim) 
 | 
            { 
 | 
                if (currentTrack != skillTrack) 
 | 
                { 
 | 
                    Debug.LogError("技能动画被打断,强制结束 " + skillConfig.SkillID); 
 | 
                    skillBase.ForceFinished(); 
 | 
                    RemoveAction(frameHandler); 
 | 
                    playingSkillAnim = false; 
 | 
                    return; 
 | 
                } 
 | 
                 
 | 
                if (skillTrack.TrackTime == 0) failCount++; 
 | 
                if (failCount > 100) 
 | 
                { 
 | 
                    Debug.LogError("技能动画播放失败,强制结束 " + skillConfig.SkillID); 
 | 
                    skillBase.ForceFinished(); 
 | 
                    RemoveAction(frameHandler); 
 | 
                    playingSkillAnim = false; 
 | 
                    return; 
 | 
                } 
 | 
            } 
 | 
  
 | 
            if (!beginTriggered && currentFrame >= skillConfig.StartupFrames && currentLoop == 0) 
 | 
            { 
 | 
                beginTriggered = true; 
 | 
                skillBase.OnStartSkillFrameEnd(); 
 | 
            } 
 | 
  
 | 
            if (!middleStarted && currentFrame >= skillConfig.StartupFrames && currentLoop <= loopCount) 
 | 
            { 
 | 
                middleStarted = true; 
 | 
                skillBase.OnMiddleFrameStart(currentLoop); 
 | 
            } 
 | 
  
 | 
            for (int i = 0; i < frameCount && i < triggeredFrames.Length; i++) 
 | 
            { 
 | 
                if (!triggeredFrames[i] && currentFrame >= activeFrames[i]) 
 | 
                { 
 | 
                    skillBase.OnMiddleFrameEnd(currentLoop, triggerCount++); 
 | 
                    triggeredFrames[i] = true; 
 | 
                } 
 | 
            } 
 | 
  
 | 
            bool allTriggered = Array.TrueForAll(triggeredFrames, x => x); 
 | 
  
 | 
            if (allTriggered && currentLoop < loopCount) 
 | 
            { 
 | 
                currentLoop++; 
 | 
                Array.Clear(triggeredFrames, 0, frameCount); 
 | 
                middleStarted = false; 
 | 
  
 | 
                if (currentLoop < loopCount) 
 | 
                { 
 | 
                    if (BattleConst.skillMotionFps > 0) 
 | 
                    { 
 | 
                        if (hasAnim) 
 | 
                            skillTrack.TrackTime = skillConfig.StartupFrames / BattleConst.skillMotionFps; 
 | 
                        else 
 | 
                            startTime = Time.time - (skillConfig.StartupFrames / BattleConst.skillMotionFps); 
 | 
                    } 
 | 
                    beginTriggered = false; 
 | 
                } 
 | 
                else 
 | 
                { 
 | 
                    finalStarted = false; 
 | 
                    finalEnded = false; 
 | 
                } 
 | 
            } 
 | 
  
 | 
            if (currentLoop >= loopCount) 
 | 
            { 
 | 
                if (!finalStarted && currentFrame >= recoveryFrame) 
 | 
                { 
 | 
                    finalStarted = true; 
 | 
                    skillBase.OnFinalFrameStart(); 
 | 
                } 
 | 
                if (finalStarted && !finalEnded && currentFrame >= recoveryFrame) 
 | 
                { 
 | 
                    finalEnded = true; 
 | 
                    if (!isSubSkill) 
 | 
                    { 
 | 
                        playingSkillAnim = false; 
 | 
                    } 
 | 
                    RemoveAction(frameHandler); 
 | 
                    onComplete?.Invoke(); 
 | 
                    skillBase.OnFinalFrameEnd(); 
 | 
                } 
 | 
            } 
 | 
        }; 
 | 
  
 | 
        AddAction(frameHandler); 
 | 
        return skillTrack; 
 | 
    } 
 | 
     
 | 
    private Spine.Animation FindAnim(string animName) 
 | 
    { 
 | 
        if (string.IsNullOrEmpty(animName)) return null; 
 | 
  
 | 
        Spine.Animation targetAnim = skeleton.Data.FindAnimation(animName); 
 | 
        if (targetAnim == null && skeleton.Data.Animations != null) 
 | 
        { 
 | 
            for (int i = 0; i < skeleton.Data.Animations.Count; i++) 
 | 
            { 
 | 
                var anim = skeleton.Data.Animations.Items[i]; 
 | 
                if (anim?.Name != null && anim.Name.ToLower() == animName.ToLower()) 
 | 
                { 
 | 
                    targetAnim = anim; 
 | 
                    break; 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
  
 | 
        if (targetAnim == null) 
 | 
            Debug.LogError($"找不到动画: {animName}"); 
 | 
  
 | 
        return targetAnim; 
 | 
    } 
 | 
  
 | 
    private void PlaySkillNoAnim(SkillConfig skillConfig, SkillBase skillBase, Action onComplete, bool isSubSkill) =>  
 | 
        ExecuteSkillAnim(skillConfig, skillBase, onComplete, null, false, isSubSkill); 
 | 
  
 | 
    protected virtual void SetupAnimationHandlers() 
 | 
    { 
 | 
        if (animState != null) 
 | 
            animState.Complete += OnAnimationComplete; 
 | 
    } 
 | 
  
 | 
    protected virtual void OnAnimationComplete(Spine.TrackEntry trackEntry) 
 | 
    { 
 | 
        if (trackEntry?.Animation?.Name == null) return; 
 | 
         
 | 
        string animName = trackEntry.Animation.Name.ToLower(); 
 | 
  
 | 
        if (AttackMotionList.Contains(animName)) 
 | 
        { 
 | 
            OnAttackAnimationComplete?.Invoke(); 
 | 
            PlayAnimation(MotionName.idle, true); 
 | 
        } 
 | 
        else if (animName == MotionName.hit.ToString().ToLower()) 
 | 
        { 
 | 
            OnHitAnimationComplete?.Invoke(); 
 | 
            PlayAnimation(MotionName.idle, true); 
 | 
        } 
 | 
         
 | 
        if (trackEntryCallbacks.TryGetValue(trackEntry, out var callback)) 
 | 
        { 
 | 
            trackEntryCallbacks.Remove(trackEntry); 
 | 
            callback?.Invoke(); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    public virtual void Run() 
 | 
    { 
 | 
        for (int i = runningActions.Count - 1; i >= 0; i--) 
 | 
            runningActions[i]?.Invoke(); 
 | 
  
 | 
        illusionShadow?.Run(); 
 | 
    } 
 | 
  
 | 
    public virtual void Pause() 
 | 
    { 
 | 
        if (skeletonAnim != null) skeletonAnim.timeScale = 0f; 
 | 
        pauseTime = Time.time; 
 | 
    } 
 | 
  
 | 
    public virtual void Resume() 
 | 
    { 
 | 
        if (skeletonAnim != null) skeletonAnim.timeScale = MotionTimeScale; 
 | 
        resumeTime = Time.time; 
 | 
    } 
 | 
  
 | 
    public void HaveRest() 
 | 
    { 
 | 
        trackEntryCallbacks.Clear(); 
 | 
        runningActions.Clear(); 
 | 
        playingSkillAnim = false; 
 | 
        PlayAnimation(MotionName.idle, true); 
 | 
    } 
 | 
  
 | 
    public void SetSpeedRatio(float ratio) 
 | 
    { 
 | 
        MotionTimeScale = ratio; 
 | 
        if (skeletonAnim != null) skeletonAnim.timeScale = ratio; 
 | 
    } 
 | 
  
 | 
    public void ShowIllusionShadow(bool isVisible) 
 | 
    { 
 | 
        if (illusionShadow != null) 
 | 
        { 
 | 
            illusionShadow.SetSkeletonAnimation(skeletonAnim); 
 | 
            illusionShadow.Show(isVisible); 
 | 
        } 
 | 
    } 
 | 
} 
 |