using UnityEngine;
namespace BaseGames.Player.States
{
///
/// 蹬墙跳状态(架构 05_PlayerModule §2,自定义蹬墙跳设计)。
///
/// 从 WallSlideState 进入(正常模式下按跳跃键触发)。
/// 分为两种子类型,由 PrepareEnter 时的水平输入决定:
/// - 背墙跳(Jump Away):无输入或反方向输入 → 远离墙壁斜上方弹出(WallJumpAwayForce)。
/// - 对墙跳(Jump Toward):朝向墙壁方向输入 → 沿墙壁方向斜上方弹出(WallJumpTowardForce)。
///
/// 公共规则:
/// 视为第一段跳(不消耗空中跳跃次数);支持可变高度(提前松键截断);
/// 蹬墙后标记 PostWallJump,允许空中靠近墙壁时自动抓墙;
/// 上升结束后转 FallState。
///
public class WallJumpState : PlayerStateBase
{
private float _inputLockTimer;
private int _wallDir;
private bool _isAwayJump; // true = 背墙跳;false = 对墙跳
public WallJumpState(PlayerController owner) : base(owner) { }
///
/// 由 WallSlideState 在调用 TransitionTo 之前调用。
/// wallDir:抓墙方向(+1 右墙 / -1 左墙)。
/// moveInputX:触发跳跃时的水平输入值。
///
public void PrepareEnter(int wallDir, float moveInputX)
{
_wallDir = wallDir;
// 无输入或输入方向与墙壁反向 → 背墙跳;输入方向与墙壁同向 → 对墙跳
int inputDir = moveInputX > 0.1f ? 1 : (moveInputX < -0.1f ? -1 : 0);
_isAwayJump = (inputDir == 0 || inputDir != _wallDir);
}
public override void OnStateEnter()
{
// 施加对应类型的速度
if (_isAwayJump)
{
Move?.WallJumpAway(_wallDir);
// 背墙跳:立即转向远离墙壁的方向(不清零速度)
Move?.FlipFacing(-_wallDir);
}
else
Move?.WallJumpToward(_wallDir);
// 蹬墙跳视为第一段跳,重置空中跳跃次数(使二段跳可再次使用)
Owner.ResetAirJumps();
_inputLockTimer = Cfg.WallJumpInputLockDuration;
// 标记蹬墙跳后自动抓墙(在 FallState/JumpState 中消耗)
Owner.SetPostWallJump(true);
// 蹬墙成功后立即恢复空中冲刺次数
Owner.GetState()?.ResetDashCharge();
// 播放蹬墙跳动画:背墙跳/对墙跳使用各自专属 Clip,留空时回退到 Jump 动画
var wallJumpClip = _isAwayJump
? (AnimCfg?.WallJumpAway ?? AnimCfg?.Jump)
: (AnimCfg?.WallJumpToward ?? AnimCfg?.Jump);
if (wallJumpClip != null) Anim?.Play(wallJumpClip);
Input.JumpCancelledEvent += OnJumpCancelled;
}
public override void OnStateExit()
{
Input.JumpCancelledEvent -= OnJumpCancelled;
}
public override void OnStateUpdate()
{
// 输入锁结束后检查是否贴墙:自动抓墙(优先于下落判断)
if (_inputLockTimer <= 0f)
{
var wd = Owner.WallDetector;
if (wd != null && wd.IsTouchingWall && !Move.IsGrounded)
{
// 传入真实墙壁方向,确保切换到对面墙时 WallSlideState 能正确刷新抓墙高度
var wss = Owner.GetState();
if (wss != null)
{
wss.PrepareEnter(wd.WallDirection);
Owner.TransitionTo(wss);
}
return;
}
}
// 上升结束 → 下落(isPostWallJump 标记保留,FallState 中继续支持自动抓墙)
if (!Move.IsRising)
{
Owner.TransitionTo(Owner.GetState());
return;
}
}
public override void OnStateFixedUpdate()
{
_inputLockTimer -= Time.fixedDeltaTime;
// 输入锁结束后允许水平控制
if (_inputLockTimer <= 0f && Mathf.Abs(Input.MoveInput.x) > 0.01f)
Move?.Move(Input.MoveInput.x * Cfg.RunSpeed);
}
private void OnJumpCancelled() => Move?.CutJump();
}
}