using System.Collections; using System.Collections.Generic; using UnityEngine; using BaseGames.Boss; using BaseGames.Combat; using BaseGames.Core.Events; using BaseGames.Parry; namespace BaseGames.Enemies { /// /// Boss 敌人基类。扩展 以支持多阶段切换、技能执行与战斗结束广播。 /// 具体 Boss 继承此类并重写 。 /// public class BossBase : EnemyBase { [Header("Boss 配置")] [SerializeField] private string _bossId; [SerializeField] private BoolEventChannelSO _onBossFightEnded; [SerializeField] private BossPhaseEventChannelSO _onBossPhaseChanged; [Header("技能执行器")] [SerializeField] private BossSkillExecutor _skillExecutor; [Header("资源组件(可选)")] [SerializeField] private BossResource _bossResource; [Header("玩家反制事件(可选)")] [Tooltip("订阅此频道以响应玩家弹反成功事件")] [SerializeField] private ParryInfoEventChannelSO _onParrySuccess; public string BossId => _bossId; /// 当前是否有 Boss 技能正在执行(BD_UseBossSkill 轮询此值)。 public bool IsBossSkillExecuting => _skillExecutor != null && _skillExecutor.IsExecuting; protected int _currentPhase = 0; /// 当前 Boss 阶段索引(BD Task 可直接查询)。 public int CurrentPhase => _currentPhase; private Coroutine _counterStaggerCoroutine; // 缓存加权候选列表,避免 UseBossSkillWeighted() 每次 new List → GC 分配 private readonly List<(BossSkillSO skill, float w)> _weightedCandidates = new(8); // 单元素缓冲数组,供 ApplyCounterResponse 缓存当前技能,避免 new[] 分配 private readonly BossSkillSO[] _singleSkillBuf = new BossSkillSO[1]; protected override void Awake() { base.Awake(); // includeInactive:true 确保禁用状态的子组件也能被发现(如分阶段按需启用的执行器) if (_skillExecutor == null) _skillExecutor = GetComponentInChildren(true); if (_bossResource == null) _bossResource = GetComponentInChildren(true); } protected override void OnEnable() { base.OnEnable(); _onParrySuccess?.Subscribe(HandleParrySuccess).AddTo(_subs); } /// /// 阶段过渡期间完全无敌( 的 IsInvincible 检查由此路由)。 /// public override bool IsInvincible => IsPhaseTransitioning || base.IsInvincible; /// /// 上一次成功执行的技能 ID。 对其施加权重惩罚,防止相同技能连续重复。 /// public string LastUsedSkillId { get; private set; } // ── 技能执行(BD Task 调用入口)───────────────────────────────────── /// /// 通过技能 ID 执行 Boss 技能。 /// 若技能未找到、执行器忙或冷却中则返回 false,否则返回 true。 /// public bool UseBossSkill(string skillId) { if (_skillExecutor == null || string.IsNullOrEmpty(skillId)) return false; if (IsPhaseTransitioning) return false; var skill = _skillExecutor.FindSkill(skillId); if (skill == null) { Debug.LogWarning($"[BossBase] 未找到技能 '{skillId}'(Boss: {_bossId})", this); return false; } if (!_skillExecutor.CanUseSkill(skillId)) return false; if (!CheckResourceCost(skill)) return false; _skillExecutor.ExecuteSkill(skill); _bossResource?.OnBossUseSkill(); return true; } /// /// 在当前阶段可用且冷却就绪的技能中,按 加权随机选择一个并执行。 /// 若上一次已使用某技能,则对该技能施加 0.3× 权重惩罚,降低连续重复的概率。 /// 若无可用技能或执行器忙则返回 false。 /// public bool UseBossSkillWeighted() { if (_skillExecutor == null || _skillExecutor.IsExecuting) return false; if (IsPhaseTransitioning) return false; var skills = _skillExecutor.Skills; if (skills == null || skills.Length == 0) return false; // 筛选:在当前阶段可用 + 冷却就绪 + weight > 0 _weightedCandidates.Clear(); float totalWeight = 0f; foreach (var s in skills) { if (s == null || s.weight <= 0f) continue; if (!_skillExecutor.CanUseSkill(s.skillId)) continue; if (!IsSkillAvailableInPhase(s)) continue; // 防重复:上一个技能权重打折 float w = s.skillId == LastUsedSkillId ? s.weight * 0.3f : s.weight; _weightedCandidates.Add((s, w)); totalWeight += w; } if (_weightedCandidates.Count == 0 || totalWeight <= 0f) return false; // 加权随机抽取 float roll = UnityEngine.Random.Range(0f, totalWeight); BossSkillSO selected = null; float accum = 0f; foreach (var (skill, w) in _weightedCandidates) { accum += w; if (roll <= accum) { selected = skill; break; } } selected ??= _weightedCandidates[_weightedCandidates.Count - 1].skill; if (!CheckResourceCost(selected)) return false; _skillExecutor.ExecuteSkill(selected); LastUsedSkillId = selected.skillId; _bossResource?.OnBossUseSkill(); return true; } /// 检查技能的 availablePhaseIndices 是否包含当前阶段(空数组 = 全阶段可用)。 private bool IsSkillAvailableInPhase(BossSkillSO skill) { if (skill.availablePhaseIndices == null || skill.availablePhaseIndices.Length == 0) return true; foreach (int p in skill.availablePhaseIndices) if (p == _currentPhase) return true; return false; } /// /// 检查 Boss 资源是否满足技能的 minRequired 门槛。 /// 未配置资源组件或 minRequired <= 0 时视为通过。 /// private bool CheckResourceCost(BossSkillSO skill) { if (_bossResource == null) return true; float min = skill.resourceCost.minRequired; if (min <= 0f) return true; return _bossResource.CurrentValue >= min; } // ── 阶段 ────────────────────────────────────────────────────────────── /// 当前是否处于阶段过渡(无敌帧 + 过渡演出)期间。 public bool IsPhaseTransitioning { get; private set; } private Coroutine _phaseTransitionCoroutine; /// /// 进入指定阶段。自动打断当前执行中的技能,广播 供 UI / 音乐系统响应。 /// 子类可重写以添加额外过渡逻辑(动画、无敌帧等)。 /// public virtual void EnterPhase(int phase) { // 阶段切换必须先打断正在执行的技能,确保原子性 _skillExecutor?.InterruptCurrentSkill(); _currentPhase = phase; LastUsedSkillId = null; // 新阶段重置权重惩罚,防止跨阶段漂移 _onBossPhaseChanged?.Raise(new BossPhaseEvent { BossId = _bossId, Phase = phase, }); } /// /// 启动阶段过渡演出:无敌帧 + 可选定格时间,结束后自动调用 。 /// BD_BossPhaseTransition 检查 来等待过渡完成。 /// /// 过渡目标阶段索引。 /// 无敌帧持续时间(秒)。 public void BeginPhaseTransition(int targetPhase, float invincibleDuration = 1.5f) { if (IsPhaseTransitioning) { Debug.LogWarning( $"[BossBase] '{_bossId}' 已在阶段过渡中(当前阶段 {_currentPhase})," + $"忽略跳转至阶段 {targetPhase} 的请求。请检查行为树逻辑是否重复触发阶段切换。", this); return; } if (_phaseTransitionCoroutine != null) StopCoroutine(_phaseTransitionCoroutine); _phaseTransitionCoroutine = StartCoroutine(PhaseTransitionCoroutine(targetPhase, invincibleDuration)); } private IEnumerator PhaseTransitionCoroutine(int targetPhase, float duration) { IsPhaseTransitioning = true; // 打断技能 + 停止移动 _skillExecutor?.InterruptCurrentSkill(); StopMovement(); // 无敌帧期间接受的伤害由 IsInvincible 属性屏蔽(子类重写 IsInvincible 或在此处理) float elapsed = 0f; while (elapsed < duration) { elapsed += Time.deltaTime; yield return null; } EnterPhase(targetPhase); IsPhaseTransitioning = false; _phaseTransitionCoroutine = null; } /// /// 立即终止阶段过渡协程并清除标志位。 /// 死亡时调用,防止 IsPhaseTransitioning 永久为 true 影响对象池复用。 /// private void AbortPhaseTransition() { if (_phaseTransitionCoroutine != null) { StopCoroutine(_phaseTransitionCoroutine); _phaseTransitionCoroutine = null; } IsPhaseTransitioning = false; } /// 检查当前 HP 是否低于指定百分比(0~1)。 public bool IsHPBelow(float ratio) { if (_stats == null || _stats.MaxHP <= 0) return false; return (float)_stats.CurrentHP / _stats.MaxHP < ratio; } protected override void OnDamageTaken(DamageInfo info) { _bossResource?.OnBossTakeDamage(); } protected override void Die() { // 死亡时立即中止阶段过渡,防止 IsPhaseTransitioning 标志永久锁死(影响对象池复用) AbortPhaseTransition(); base.Die(); _onBossFightEnded?.Raise(true); } // ── 玩家反制响应 ────────────────────────────────────────────────────── private void HandleParrySuccess(ParryInfo info) { if (!IsAlive) return; var counterType = info.IsPerfect ? CounterType.PerfectParry : CounterType.Parry; ApplyCounterResponse(counterType, string.Empty); } /// /// 根据 counterType 查找当前技能(或所有技能)的 PlayerCounterResponse 并应用效果。 /// 可由外部系统(闪避穿越、弱点命中等)直接调用。 /// public void ApplyCounterResponse(CounterType counterType, string requiredSkillId) { if (_skillExecutor == null) return; // 优先检查当前正在执行的技能的反制规则 BossSkillSO activeSkill = _skillExecutor.IsExecuting ? _skillExecutor.FindCurrentSkill() : null; BossSkillSO[] candidates; if (activeSkill != null) { _singleSkillBuf[0] = activeSkill; candidates = _singleSkillBuf; } else { candidates = _skillExecutor.Skills; } if (candidates == null) return; foreach (var skill in candidates) { if (skill?.counterResponses == null) continue; foreach (var resp in skill.counterResponses) { if (resp.counterType != counterType) continue; if (!string.IsNullOrEmpty(resp.requiredSkillId) && !string.IsNullOrEmpty(requiredSkillId) && resp.requiredSkillId != requiredSkillId) continue; ExecuteCounterEffect(resp); return; // 每次反制只触发第一条匹配规则 } } } private void ExecuteCounterEffect(in PlayerCounterResponse resp) { if (resp.interruptSkill) _skillExecutor?.InterruptCurrentSkill(); if (resp.bossStaggerDuration > 0f) { if (_counterStaggerCoroutine != null) StopCoroutine(_counterStaggerCoroutine); _counterStaggerCoroutine = StartCoroutine(CounterStaggerCoroutine(resp.bossStaggerDuration)); } if (resp.openVulnWindow) { float duration = Mathf.Max(resp.bossStaggerDuration, 1f); float multiplier = 1f + resp.bossDamageBonus; _skillExecutor?.OpenVulnerabilityWindow(duration, multiplier); } resp.counterFeedback?.Play(); } private IEnumerator CounterStaggerCoroutine(float duration) { ForceState(EnemyStateType.Stagger); // 时长固定且较短,直接 new WFY 即可;若需优化可接入 WFS 缓存 yield return new WaitForSeconds(duration); if (IsAlive && CurrentState == EnemyStateType.Stagger) ForceState(EnemyStateType.Controlled); _counterStaggerCoroutine = null; } public override void OnSpawn() { base.OnSpawn(); LastUsedSkillId = null; _currentPhase = 0; _skillExecutor?.ResetAllCooldowns(); } } }