using UnityEngine;
using System.Collections;
namespace BaseGames.Player.States
{
///
/// 地面冲刺状态(架构 05_PlayerModule §2 + §12)。
/// 施加水平位移 + 无敌帧;冲刺期间重力归零,结束后恢复;
/// 需解锁 AbilityType.Dash 才能进入(PlayerController 负责条件检查)。
///
public class DashState : PlayerStateBase
{
private float _timer;
private float _cooldownTimer;
///
/// 无敌的独立冷却计时器。由 TickCooldown 每帧减少;
/// 地面冲刺和空中冲刺共享此计时器(AerialDashState 通过 GetState<DashState>() 读写)。
///
private float _invincibilityCooldownTimer;
private int _facingDir;
public bool CanDash => _cooldownTimer <= 0f;
///
/// 无敌帧是否已冷却,即本次冲刺可以获得无敌。
/// DashState 和 AerialDashState 共享同一实例计时器实现 "spam 冲刺不能持续无敌" 机制。
///
public bool CanGrantInvincibility => _invincibilityCooldownTimer <= 0f;
/// 重置无敌冷却(DashState/AerialDashState 驾用无敌后调用)。
public void ResetInvincibilityCooldown(float cd) => _invincibilityCooldownTimer = cd;
// ── IsInvincible 不再在状态层硬编码,改为全面依赖 Stats._invincibleTimer ────────────
// 冲刺无敌窗口 = BeginInvincibility(DashInvincibilityDuration) 设定的时间限 Stats._invincibleTimer
// PlayerController.TakeDamage 已改为将 Stats.IsInvincible 纳入硬直判断,居间无敌窗口自然失效。
// 不再需要 override IsInvincible。
public DashState(PlayerController owner) : base(owner) { }
public override void OnStateEnter()
{
Debug.Assert(Cfg != null, "[DashState] MovementConfig 未配置,请在 PlayerController Inspector 中绑定 MovementConfig SO。", _owner);
_facingDir = Owner.FacingDirection;
_timer = Cfg.DashDuration;
// 无敌帧:
// 条件 1:已解锁 InvincibleDash
// 条件 2:无敌冷却已就绪(防止 spam 冲刺连序无敌)
// 窗口时长 = DashInvincibilityDuration < DashDuration,冲刺后段无保护
if (Stats != null && Stats.HasAbility(AbilityType.InvincibleDash) && CanGrantInvincibility)
{
Stats.BeginInvincibility(Cfg.DashInvincibilityDuration);
_invincibilityCooldownTimer = Cfg.DashInvincibilityCooldown;
}
// 关闭重力,施加冲刺速度
Move?.SetGravityScale(0f);
Move?.Dash(new Vector2(_facingDir, 0f), Cfg.DashSpeed);
// 播放冲刺动画
if (AnimCfg?.Dash != null) Anim?.Play(AnimCfg.Dash);
}
public override void OnStateUpdate()
{
_timer -= Time.deltaTime;
if (_timer <= 0f)
{
EndDash();
return;
}
// 撞墙立即终止冲刺(碰到实体墙立即中止,避免压墙卡住)
var wd = Owner.WallDetector;
if (wd != null && wd.IsTouchingWall && wd.WallDirection == _facingDir)
EndDash();
}
public override void OnStateExit()
{
// 恢复默认重力
Move?.SetGravityScale(Cfg.DefaultGravityScale);
_cooldownTimer = Cfg.DashCooldown;
}
public override void OnStateFixedUpdate()
{
// 冲刺期间保持冲刺速度(防止摩擦力减速)
if (_timer > 0f)
Move?.Dash(new Vector2(_facingDir, 0f), Cfg.DashSpeed);
}
private void EndDash()
{
if (Move != null && Move.IsGrounded)
Owner.TransitionTo(Owner.GetState());
else
Owner.TransitionTo(Owner.GetState());
}
/// 每帧减少冒小帮冷却(由 PlayerController.Update 调用)。
public void TickCooldown(float dt)
{
if (_cooldownTimer > 0f) _cooldownTimer -= dt;
if (_invincibilityCooldownTimer > 0f) _invincibilityCooldownTimer -= dt;
}
}
}