using System.Collections; using System.Collections.Generic; using UnityEngine; using Animancer; using BaseGames.Combat; using BaseGames.Core.Events; namespace BaseGames.Boss { /// /// 挂在 Boss GameObject 上,接收 BossOrchestrator 的指令执行指定 BossSkillSO。 /// 管理 VulnerabilityWindow 计时和 WeakPointSystem 激活。 /// public class BossSkillExecutor : MonoBehaviour { [SerializeField] private HitBox[] _hitBoxes; [SerializeField] private WeakPointSystem _weakPointSystem; [SerializeField] private AnimancerComponent _animancer; [SerializeField] private string _bossId; [SerializeField] private BossSkillEventChannelSO _onBossSkillStarted; [SerializeField] private BossSkillEventChannelSO _onBossSkillEnded; /// PlayerController 无 Instance(架构 05 §2),由 Inspector 指定。 [SerializeField] private Transform _playerTransform; private BossSkillSO _currentSkill; private bool _isExecuting; private Coroutine _activeCoroutine; public bool IsExecuting => _isExecuting; /// /// 按 float 值复用 WaitForSeconds 实例,消除协程中每次 new WaitForSeconds 的 GC 分配。 /// Domain Reload 禁用时静态缓存跨 PlayMode 会话保留,但 WaitForSeconds 是幂等值对象, /// 不会引发功能错误;[RuntimeInitializeOnLoadMethod] 确保每次进入 Play 时清空。 /// private static readonly Dictionary _wfsCache = new(); [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.SubsystemRegistration)] private static void ClearWFSCache() => _wfsCache.Clear(); private static WaitForSeconds GetWFS(float t) { if (!_wfsCache.TryGetValue(t, out var wfs)) _wfsCache[t] = wfs = new WaitForSeconds(t); return wfs; } // ── 公共 API ─────────────────────────────────────────────────────────── /// /// 执行一个 Boss 技能。若当前已在执行中则直接返回。 /// public void ExecuteSkill(BossSkillSO skill) { if (_isExecuting || skill == null) return; _activeCoroutine = StartCoroutine(ExecuteSkillCoroutine(skill)); } /// /// 立即打断正在执行的技能(阶段切换时调用)。 /// public void InterruptCurrentSkill() { if (_activeCoroutine != null) { StopCoroutine(_activeCoroutine); _activeCoroutine = null; } FinishExecution(); } // ── 主协程 ───────────────────────────────────────────────────────────── private IEnumerator ExecuteSkillCoroutine(BossSkillSO skill) { _isExecuting = true; _currentSkill = skill; _onBossSkillStarted?.Raise(new BossSkillEvent { BossId = _bossId, SkillId = skill.skillId }); // 播放技能动画 if (skill.skillAnimation != null) _animancer.Play(skill.skillAnimation); // 启动 VulnerabilityWindow 协程(与主序列并行) Coroutine vulnCoroutine = null; if (skill.vulnerabilityWindows != null && skill.vulnerabilityWindows.Length > 0) vulnCoroutine = StartCoroutine(ActivateVulnerabilityWindowsCoroutine(skill)); // 执行攻击序列(优先 sequenceOnMiss 作为默认序列) if (skill.sequenceOnMiss != null) yield return ExecuteSequenceCoroutine(skill.sequenceOnMiss); // 若弱点协程还在运行则等待其结束(避免孤立协程) if (vulnCoroutine != null) yield return vulnCoroutine; FinishExecution(); } private void FinishExecution() { _isExecuting = false; if (_currentSkill != null) { _onBossSkillEnded?.Raise(new BossSkillEvent { BossId = _bossId, SkillId = _currentSkill.skillId }); _currentSkill = null; } } // ── 序列协程 ──────────────────────────────────────────────────────────── private IEnumerator ExecuteSequenceCoroutine(SkillSequenceSO seq) { int repeatCount = 0; do { foreach (var step in seq.steps) { if (step.delayBeforeStep > 0f) yield return GetWFS(step.delayBeforeStep); if (step.pattern != null) yield return ExecutePatternCoroutine(step.pattern); } repeatCount++; if (seq.RepeatIfPlayerInRange && seq.RepeatDelay > 0f) yield return GetWFS(seq.RepeatDelay); } while (seq.RepeatIfPlayerInRange && (seq.MaxRepeatCount == 0 || repeatCount < seq.MaxRepeatCount) && IsPlayerInRange()); } private IEnumerator ExecutePatternCoroutine(AttackPatternSO pattern) { // 预备 if (pattern.WindupDuration > 0f) yield return GetWFS(pattern.WindupDuration); // 激活 HitBox(架构 06 §4:Activate(DamageSourceSO, Transform)) foreach (var hb in _hitBoxes) hb.Activate(pattern.DamageSource, transform); if (pattern.ActiveDuration > 0f) yield return GetWFS(pattern.ActiveDuration); // 关闭 HitBox foreach (var hb in _hitBoxes) hb.Deactivate(); // 后摇 if (pattern.RecoveryDuration > 0f) yield return GetWFS(pattern.RecoveryDuration); } // ── VulnerabilityWindow 协程 ───────────────────────────────────────────── private IEnumerator ActivateVulnerabilityWindowsCoroutine(BossSkillSO skill) { foreach (var window in skill.vulnerabilityWindows) { if (window.TriggerDelay > 0f) yield return GetWFS(window.TriggerDelay); bool activateSpecific = window.ActivateWeakPointHurtBox; _weakPointSystem?.SetActive(true, window.DamageMultiplier, activateSpecific); window.OpenFeedback?.Play(); yield return GetWFS(window.Duration); _weakPointSystem?.SetActive(false, 1f, activateSpecific); window.CloseFeedback?.Play(); } } // ── 工具 ─────────────────────────────────────────────────────────────── private bool IsPlayerInRange() => _playerTransform != null && Vector2.Distance(transform.position, _playerTransform.position) < 8f; } }