using System.Collections; using UnityEngine; using Animancer; using BaseGames.Core; using BaseGames.Core.Pool; namespace BaseGames.Enemies.Abilities { /// /// 敌人能力抽象基类(架构 07_EnemyModule §8.4)。 /// 责任:生命周期管理、冷却计时、中断分发。子类只实现 。 /// /// 设计要点: /// - 单一执行实例:同一能力同时只有一个协程在跑。 /// - 协程内 yield WaitForSeconds 复用 (无 GC)。 /// - 受击/死亡时由 调用 。 /// [DisallowMultipleComponent] public abstract class EnemyAbilityBase : MonoBehaviour { [Header("配置 SO")] [SerializeField] protected EnemyAbilitySO _config; // 缓存依赖(Awake 填入,热路径无 GetComponent) protected EnemyBase _enemy; protected AnimancerComponent _animancer; protected Transform _transform; private Coroutine _runner; private float _cooldownEndTime = -1f; private bool _isRunning; // ── 公共状态 ───────────────────────────────────────────────────── public EnemyAbilitySO Config => _config; public bool IsRunning => _isRunning; public AbilityRunState Phase { get; protected set; } = AbilityRunState.Idle; public float CooldownRemaining => Mathf.Max(0f, _cooldownEndTime - Time.time); public bool IsOnCooldown => CooldownRemaining > 0f; /// 能力被外部中断时触发(BD Task / 状态机订阅用)。 public event System.Action Interrupted; /// BD 任务统一查询入口:当前是否可用(冷却完毕且未执行中)。 public virtual bool CanUse => !_isRunning && !IsOnCooldown && _enemy != null && _enemy.IsAlive; protected virtual void Awake() { _enemy = GetComponentInParent(); _animancer = _enemy != null ? _enemy.Animancer : GetComponentInParent(); _transform = transform; if (_enemy == null) Debug.LogError($"[EnemyAbilityBase] {GetType().Name} 找不到 EnemyBase。", this); if (_animancer == null) Debug.LogWarning($"[EnemyAbilityBase] {GetType().Name} 找不到 AnimancerComponent,动画能力将无法播放动画。", this); } protected virtual void OnDisable() { if (_isRunning) Interrupt(InterruptReason.ExternalRequest); } // ── 执行 ───────────────────────────────────────────────────────── /// /// 启动能力。重复调用、冷却中或已运行将返回 false。 /// 若 非空,会先中断同组其他能力(互斥)。 /// public bool Execute() { if (!CanUse) return false; // 互斥组:启动前中断同组正在运行的其他能力 if (_config != null && !string.IsNullOrEmpty(_config.exclusionGroup)) _enemy?.Abilities.InterruptGroup(_config.exclusionGroup, InterruptReason.ExternalRequest); _runner = StartCoroutine(RunInternal()); return true; } /// /// 强制启动能力,忽略冷却检查(连段语义:外部组合技调用子能力时使用)。 /// 若能力正在运行则先中断再重启。 /// public bool ForceExecute() { if (_enemy == null || !_enemy.IsAlive) return false; if (_isRunning) Interrupt(InterruptReason.ExternalRequest); _runner = StartCoroutine(RunInternal()); return true; } private IEnumerator RunInternal() { _isRunning = true; Phase = AbilityRunState.Telegraph; try { if (_config != null && _config.telegraphDuration > 0f) yield return TelegraphRoutine(); Phase = AbilityRunState.Windup; yield return ExecuteCoroutine(); Phase = AbilityRunState.Recovery; } finally { _isRunning = false; _runner = null; _cooldownEndTime = Time.time + (_config != null ? _config.cooldown : 0f); if (Phase != AbilityRunState.Interrupted) Phase = AbilityRunState.Idle; OnAbilityEnded(); } } /// 子类实现:能力主体。可分多段、含 HitBox 激活/弹幕生成/物理推进等。 protected abstract IEnumerator ExecuteCoroutine(); /// 预警阶段(默认生成 VFX 后等待 telegraphDuration)。子类可重写。 protected virtual IEnumerator TelegraphRoutine() { if (!string.IsNullOrEmpty(_config.telegraphVfxKey)) { var pool = ServiceLocator.GetOrDefault(); pool?.Spawn(_config.telegraphVfxKey, _transform.position, Quaternion.identity); } yield return EnemyAbilityWaits.Get(_config.telegraphDuration); } /// 能力结束钩子(被中断或正常结束都会调用)。 protected virtual void OnAbilityEnded() { } /// 中断当前执行。冷却仍会按配置计入。 public void Interrupt(InterruptReason reason) { if (!_isRunning) return; if (_config != null) { if (reason == InterruptReason.Hurt && !_config.interruptOnHurt) return; if (reason == InterruptReason.Stagger && !_config.interruptOnStagger) return; } if (_runner != null) StopCoroutine(_runner); _runner = null; _isRunning = false; Phase = AbilityRunState.Interrupted; OnInterrupted(reason); Interrupted?.Invoke(reason); OnAbilityEnded(); _cooldownEndTime = Time.time + (_config != null ? _config.cooldown * 0.5f : 0f); } protected virtual void OnInterrupted(InterruptReason reason) { } /// 子类辅助:朝向目标(写入输入信号,下一 FixedUpdate 由 EnemyMovement 消费)。 protected void FaceTarget(Transform target) { if (target == null || _enemy == null) return; _enemy.FaceTarget(target.position); } } /// WaitForSeconds 池(架构 §10 GC 优化)。能力协程统一通过此获取等待指令。 internal static class EnemyAbilityWaits { private const int MaxCacheSize = 64; private static readonly System.Collections.Generic.Dictionary _cache = new System.Collections.Generic.Dictionary(32); public static WaitForSeconds Get(float seconds) { if (seconds <= 0f) return null; if (!_cache.TryGetValue(seconds, out var w)) { if (_cache.Count < MaxCacheSize) { w = new WaitForSeconds(seconds); _cache[seconds] = w; } else { return new WaitForSeconds(seconds); } } return w; } } }