172 lines
7.0 KiB
C#
172 lines
7.0 KiB
C#
using System.Collections;
|
||
using Animancer;
|
||
using BaseGames.Combat;
|
||
using BaseGames.Core;
|
||
using BaseGames.Core.Pool;
|
||
using UnityEngine;
|
||
using UnityEngine.Events;
|
||
|
||
namespace BaseGames.Enemies.Boss
|
||
{
|
||
/// <summary>
|
||
/// 嘲风 Boss 主脚本。
|
||
///
|
||
/// Phase 0(地面):4 技能加权随机(回旋扇/扇形连击/小龙卷/大龙卷)。
|
||
/// Phase 1(空中):风石技能 + 击落计数机制。
|
||
///
|
||
/// 阶段过渡流程:
|
||
/// 1. BossBase.BeginPhaseTransition → OnBeginPhaseTransition(1) 立即播放过渡动画 + 开始浮空
|
||
/// 2. 无敌期结束(≥ _riseDuration+buffer)→ BossBase.EnterPhase(1) 广播阶段切换事件
|
||
///
|
||
/// ⚠️ 动画由本脚本通过 Animancer.Play() 完整控制,不在 BD 中调用 BD_PlayAnimation。
|
||
/// </summary>
|
||
public class ChaoFengBoss : BossBase
|
||
{
|
||
[Header("浮空 / 击落")]
|
||
[SerializeField] private ChaoFengFloatController _floatController;
|
||
[SerializeField] private ChaoFengKnockdownCounter _knockdownCounter;
|
||
|
||
[Header("阶段过渡动画")]
|
||
[SerializeField] private ClipTransition _phaseTransitionClip;
|
||
|
||
[Header("回旋扇收招动画")]
|
||
[SerializeField] private ClipTransition _boomerangEndClip;
|
||
|
||
[Header("弹体发射点")]
|
||
[SerializeField] private Transform _boomerangMuzzle;
|
||
[SerializeField] private Transform _tornadoMuzzle;
|
||
[SerializeField] private Transform _windStoneMuzzle;
|
||
|
||
[Header("击败演出动画")]
|
||
[SerializeField] private ClipTransition _defeatStruggleClip;
|
||
[Tooltip("倒地喘气(循环);与 ChaoFengKnockdownCounter._staggerClip 共用同一 Clip")]
|
||
[SerializeField] private ClipTransition _defeatPantClip;
|
||
[SerializeField] private ClipTransition _defeatStandUpClip;
|
||
[SerializeField] private float _defeatPantDuration = 3f;
|
||
|
||
[Header("白屏回调(可接 CameraManager / VFX Event)")]
|
||
[SerializeField] private UnityEngine.Events.UnityEvent _onDefeatWhiteFlash;
|
||
|
||
// ── 阶段过渡钩子 ─────────────────────────────────────────────────────
|
||
|
||
/// <summary>
|
||
/// 阶段过渡开始时立即播放过渡动画并启动浮空协程。
|
||
/// invincibleDuration 需在 BD_BossPhaseTransition 中配置为 ≥ _riseDuration + 缓冲(约 2.0s)。
|
||
/// </summary>
|
||
protected override void OnBeginPhaseTransition(int targetPhase)
|
||
{
|
||
if (targetPhase == 1)
|
||
{
|
||
if (_phaseTransitionClip.Clip != null)
|
||
Animancer.Play(_phaseTransitionClip);
|
||
StartCoroutine(_floatController.FloatUp());
|
||
}
|
||
}
|
||
|
||
// ── 受击转发 ─────────────────────────────────────────────────────────
|
||
|
||
/// <summary>
|
||
/// 转发受击事件至击落计数器。
|
||
/// ⚠️ HurtBox 无公开 OnDamageTaken 事件,必须通过此虚方法转发。
|
||
/// </summary>
|
||
protected override void OnDamageTaken(DamageInfo info)
|
||
{
|
||
base.OnDamageTaken(info);
|
||
_knockdownCounter?.OnBossHit(info);
|
||
}
|
||
|
||
// ── 动画事件 ─────────────────────────────────────────────────────────
|
||
|
||
/// <summary>
|
||
/// 回旋扇返回时由 ReturnProjectile 调用,触发收扇动画。
|
||
/// </summary>
|
||
public void OnBoomerangReturned()
|
||
{
|
||
if (_boomerangEndClip.Clip != null)
|
||
Animancer.Play(_boomerangEndClip);
|
||
}
|
||
|
||
// ── 弹体生成 ─────────────────────────────────────────────────────────
|
||
|
||
/// <summary>
|
||
/// 由技能动画 AnimationEvent 触发,生成对应弹体。
|
||
/// payload: "boomerang" / "tornado_small" / "tornado_large" / "wind_stone"
|
||
/// </summary>
|
||
public override void SpawnProjectile(string payload)
|
||
{
|
||
var pool = ServiceLocator.GetOrDefault<IObjectPoolService>();
|
||
if (pool == null) return;
|
||
|
||
switch (payload)
|
||
{
|
||
case "boomerang":
|
||
{
|
||
var go = pool.Spawn("PROJ_Boomerang",
|
||
_boomerangMuzzle != null ? _boomerangMuzzle.position : transform.position,
|
||
Quaternion.identity);
|
||
go?.GetComponent<ReturnProjectile>()?.SetOwner(transform);
|
||
break;
|
||
}
|
||
case "tornado_small":
|
||
pool.Spawn("PROJ_TornadoSmall",
|
||
_tornadoMuzzle != null ? _tornadoMuzzle.position : transform.position,
|
||
Quaternion.identity);
|
||
break;
|
||
|
||
case "tornado_large":
|
||
if (PlayerTransform != null)
|
||
pool.Spawn("PROJ_TornadoLarge", PlayerTransform.position, Quaternion.identity);
|
||
break;
|
||
|
||
case "wind_stone":
|
||
pool.Spawn("PROJ_WindStone",
|
||
_windStoneMuzzle != null ? _windStoneMuzzle.position : transform.position,
|
||
Quaternion.identity);
|
||
break;
|
||
}
|
||
}
|
||
|
||
// ── 击败演出 ─────────────────────────────────────────────────────────
|
||
|
||
protected override void Die()
|
||
{
|
||
StartCoroutine(DefeatSequence());
|
||
}
|
||
|
||
private IEnumerator DefeatSequence()
|
||
{
|
||
StopBehaviorTree();
|
||
_knockdownCounter?.ForceEnd();
|
||
|
||
// Phase 2(空中)先落地
|
||
if (CurrentPhase >= 1 && _floatController != null)
|
||
yield return _floatController.FallDown();
|
||
|
||
// 空中挣扎(Defeat_Struggle)
|
||
if (_defeatStruggleClip.Clip != null)
|
||
{
|
||
Animancer.Play(_defeatStruggleClip);
|
||
yield return new WaitForSeconds(_defeatStruggleClip.Clip.length);
|
||
}
|
||
|
||
// 白屏效果
|
||
_onDefeatWhiteFlash?.Invoke();
|
||
|
||
// 倒地喘气(Defeat_Pant 循环)
|
||
if (_defeatPantClip.Clip != null)
|
||
Animancer.Play(_defeatPantClip);
|
||
yield return new WaitForSeconds(_defeatPantDuration);
|
||
|
||
// 站起(Defeat_StandUp 单次)
|
||
if (_defeatStandUpClip.Clip != null)
|
||
{
|
||
Animancer.Play(_defeatStandUpClip);
|
||
yield return new WaitForSeconds(_defeatStandUpClip.Clip.length);
|
||
}
|
||
|
||
// 广播战斗结束、触发结算过场
|
||
base.Die();
|
||
}
|
||
}
|
||
}
|