using UnityEngine;
using System.Linq;
using Animancer;
using BaseGames.Core.Events;
using BaseGames.Input;
using BaseGames.Combat;
using BaseGames.Feedback;
using BaseGames.Parry;
using BaseGames.Skills;
namespace BaseGames.Player.States
{
///
/// 玩家主控制器(协调器)。位于 Player/States/ 程序集,以便引用所有具体状态类型。
/// 实现 IDamageable + IPoiseSource(架构 06_CombatModule §13)。
/// 依赖注入:同节点组件由 RequireComponent + Awake 自动获取;跨节点引用通过 [SerializeField] 绑定。
///
[DefaultExecutionOrder(-100)]
[RequireComponent(typeof(InputBuffer))]
[RequireComponent(typeof(PlayerMovement))]
[RequireComponent(typeof(PlayerStats))]
[RequireComponent(typeof(AnimancerComponent))]
public class PlayerController : MonoBehaviour, IDamageable, IPoiseSource
{
// ── 同节点组件(由 RequireComponent 保证存在,Awake 中自动获取)─────
private PlayerMovement _movement;
private PlayerStats _stats;
private AnimancerComponent _animancer;
// ── 配置 SO ───────────────────────────────────────────────────────────
[Header("配置")]
[SerializeField] private PlayerMovementConfigSO _movementConfig;
[SerializeField] private PlayerAnimationConfigSO _animConfig;
[SerializeField] private InputReaderSO _inputReader;
[SerializeField] private FormConfigSO _formConfig;
// ── 战斗组件 ──────────────────────────────────────────────────────────
[Header("战斗")]
[SerializeField] private PlayerFeedback _feedback;
[SerializeField] private PlayerCombat _combat;
[SerializeField] private FormController _formController;
[SerializeField] private WeaponManager _weaponManager;
[SerializeField] private SkillManager _skillManager;
[SerializeField] private SpringSystem _springSystem;
[SerializeField] private ParrySystem _parrySystem;
[SerializeField] private HurtBox _hurtBox;
[SerializeField] private ShieldComponent _shield;
[SerializeField] private PlayerWallDetector _wallDetector;
// ── 事件频道 ──────────────────────────────────────────────────────────
[Header("事件频道")]
[SerializeField] private VoidEventChannelSO _onPlayerDied;
///
/// Start() 时广播玩家 Transform(EnemyBase / ProjectileManager 等订阅此频道)。
/// 替代每个敌人在 Awake 中独立 FindWithTag 的 O(n) 全场景扫描。
///
[SerializeField] private TransformEventChannelSO _onPlayerSpawned;
// ── 运行时 ────────────────────────────────────────────────────────────
private InputBuffer _inputBuffer;
private bool _missingDependencyLogged;
private bool _dependenciesReady;
// DashState 在 Update 每帧访问(TickCooldown + CanDash),提前缓存避免重复 Dictionary 查找
private DashState _dashState;
///
/// 当前腾空可用的额外跳跃次数(二段跳)。
/// 由 IdleState/RunState.OnStateEnter 落地时通过 ResetAirJumps() 重置;
/// JumpState/FallState 判断 HasAbility(DoubleJump) 后消耗。
///
private int _airJumpsLeft;
#if UNITY_EDITOR
[Header("调试")]
[SerializeField] private bool _debugValidateTransitions = true;
[Header("── 运行时状态 ──")]
[SerializeField] private string _dbg_CurrentState;
[SerializeField] private bool _dbg_IsGrounded;
[SerializeField] private int _dbg_AirJumpsLeft;
[SerializeField] private bool _dbg_CanDash;
[SerializeField] private bool _dbg_IsInvincible;
#endif
// Overlay Layer(Layer 1):供 SpringState / SoulSkill 等叠加层动画使用
// Base Layer(Layer 0):移动/攻击/受伤/死亡等全身状态动画
private AnimancerLayer _overlayLayer;
// ── 状态实例 ──────────────────────────────────────────────────────────
private PlayerStateBase _currentState;
private readonly System.Collections.Generic.Dictionary _states = new();
// ── IDamageable 实现 ──────────────────────────────────────────────────
public bool IsAlive => _stats != null && _stats.IsAlive;
public bool IsInvincible => _stats != null && _stats.IsInvincible;
public int Defense => 0;
public void TakeDamage(DamageInfo info)
{
if (_stats == null) return;
_stats.TakeDamage(info.FinalDamage);
// 当前状态标记为无敌(如旧版冲刺状态),或 Stats 层无敌窗口仍激活
// (冲刺无敌帧 DashInvincibilityDuration 内:跳过受击硬直;窗口过期后可被打断)
if (_currentState?.IsInvincible == true || (_stats != null && _stats.IsInvincible)) return;
if (_stats.IsAlive)
{
GetState()?.Initialize(info);
TransitionTo(GetState());
}
else
{
TransitionTo(GetState());
_onPlayerDied?.Raise();
}
}
// ── IPoiseSource 实现(架构 06_CombatModule §13)─────────────────────
///
/// 玩家不拥有霸体,始终返回 。
/// 设计决策:玩家不拥有霸体,始终返回 。
/// 玩家依靠走位和弹反规避伤害而非硬吃,以保持战斗的负担感和张力。
/// 若未来需要临时霸体(如特定技能动作),请通过独立的覆盖标记实现,
/// 而非在此处引入状态,以保持接口语义清晰。
///
public PoiseLevel GetCurrentPoiseLevel() => PoiseLevel.None;
// ── 公开属性(供状态类访问)──────────────────────────────────────────
public PlayerMovement Movement => _movement;
public PlayerStats Stats => _stats;
public AnimancerComponent Animancer => _animancer;
public PlayerMovementConfigSO MovConfig => _movementConfig;
public PlayerAnimationConfigSO AnimConfig => _animConfig;
public InputReaderSO Input => _inputReader;
public InputBuffer Buffer => _inputBuffer;
public IFeedbackPlayer Feedback => _feedback != null ? (IFeedbackPlayer)_feedback : NullFeedbackPlayer.Instance;
public PlayerCombat Combat => _combat;
public FormController Form => _formController;
public WeaponManager Weapon => _weaponManager;
public SkillManager Skill => _skillManager;
public SpringSystem Spring => _springSystem;
public ParrySystem Parry => _parrySystem;
public HurtBox HurtBox => _hurtBox;
public ShieldComponent Shield => _shield;
public PlayerWallDetector WallDetector => _wallDetector;
public bool IsGrounded => _movement != null && _movement.IsGrounded;
public int FacingDirection => _movement != null ? _movement.FacingDirection : 1;
// ── 空中跳跃 API(二段跳)────────────────────────────────────────────
public int AirJumpsLeft => _airJumpsLeft;
public void UseAirJump() => _airJumpsLeft = Mathf.Max(0, _airJumpsLeft - 1);
///
/// 落地时重置空中跳跃次数(由 IdleState/RunState.OnStateEnter 调用)。
/// 若解锁 DoubleJump 则重置为 MovConfig.MaxAirJumps,否则为 0。
///
public void ResetAirJumps() =>
_airJumpsLeft = (_stats != null && _stats.HasAbility(AbilityType.DoubleJump))
? (_movementConfig != null ? _movementConfig.MaxAirJumps : 1)
: 0;
// ── 抓墙高度记忆 API ──────────────────────────────────────────────────
// wallGrabY:首次抓住某面墙壁时记录的 Y 坐标;用于防止单面墙无限向上爬。
// wallGrabDir:当前记录所属墙壁方向(+1=右墙,-1=左墙,0=无)。
// isPostWallJump:蹬墙跳后未落地标记,允许靠近墙壁时自动抓墙。
private float _wallGrabY = float.NegativeInfinity;
private int _wallGrabDir = 0;
private bool _isPostWallJump;
public float WallGrabY => _wallGrabY;
public int WallGrabDir => _wallGrabDir;
public bool IsPostWallJump => _isPostWallJump;
///
/// 进入 WallSlideState 时调用。
/// 不同墙壁(dir != _wallGrabDir)→ 重置并记录新高度;
/// 同一面墙壁 → 保留原记录(防止攀爬重置)。
///
public void RecordWallGrab(int dir, float y)
{
if (dir != _wallGrabDir)
{
_wallGrabDir = dir;
_wallGrabY = y;
}
// 同一面墙:保留 _wallGrabY,不更新
}
/// 落地时重置抓墙记录(由 IdleState/RunState.OnStateEnter 调用)。
public void ResetWallGrab()
{
_wallGrabY = float.NegativeInfinity;
_wallGrabDir = 0;
}
///
/// 设置蹬墙跳后自动抓墙标记。
/// true:由 WallJumpState.OnStateEnter 设置;
/// false:由 IdleState/RunState.OnStateEnter(落地)或 WallSlideState.OnStateEnter(已消耗)清除。
///
public void SetPostWallJump(bool value) => _isPostWallJump = value;
// ── Overlay Layer API(供 SpringState / SoulSkill 等叠加动画使用)─────
///
/// 在 Overlay Layer(Layer 1)播放动画,叠加于当前 Base Layer 动画之上。
/// 适用于灵泉使用、魂技能等需要与移动动画并行的上半身动作。
///
public AnimancerState PlayOnOverlay(AnimationClip clip)
=> _overlayLayer?.Play(clip);
/// 停止 Overlay Layer 动画,淡出回 Base Layer。
public void StopOverlay(float fadeDuration = 0.1f)
=> _overlayLayer?.StartFade(0f, fadeDuration);
// ── Unity Lifecycle ───────────────────────────────────────────────────
private void Awake()
{
Debug.Assert(_movementConfig != null, "[PlayerController] _movementConfig 未赋值,请在 Inspector 中指定 PlayerMovementConfigSO。", this);
ResolveDependencies();
// 初始化 Animancer 双层动画:Layer 0 = Base(全身状态),Layer 1 = Overlay(叠加层)
if (_animancer != null)
{
// 确保 Layer 1 存在;AnimancerComponent 默认只有 Layer 0
while (_animancer.Layers.Count <= 1)
_animancer.Layers.Add();
_overlayLayer = _animancer.Layers[1];
}
// 注入 HurtBox 依赖(覆盖所有子节点 HurtBox,支持多形状受击区)
foreach (var hb in GetComponentsInChildren(true))
{
if (_shield != null) hb.SetShieldable(_shield);
if (_parrySystem != null) hb.SetParrySystem(_parrySystem);
hb.SetPoiseSource(this);
}
// 将唯一配置点(_inputReader)注入到 ParrySystem。
// ParrySystem 不再需要在 Inspector 单独配置 InputReaderSO。
if (_parrySystem != null)
_parrySystem.SetInputReader(_inputReader);
InitializeStates();
// 订阅 ParrySystem C# 事件
if (_parrySystem != null)
{
_parrySystem.OnParryActivated += OnParryActivated;
_parrySystem.OnParryConsumed += OnParryConsumedHandler;
}
// 订阅灵泉使用输入
if (_inputReader != null)
_inputReader.UseSpringEvent += OnUseSpring;
}
private void OnDestroy()
{
if (_parrySystem != null)
{
_parrySystem.OnParryActivated -= OnParryActivated;
_parrySystem.OnParryConsumed -= OnParryConsumedHandler;
}
if (_inputReader != null)
_inputReader.UseSpringEvent -= OnUseSpring;
}
/// 弹反输入激活时由 ParrySystem 触发 → 转换到 ParryState。
private void OnParryActivated()
{
if (_states.ContainsKey(typeof(ParryState)))
TransitionTo(GetState());
}
/// 弹反命中成功时由 ParrySystem 触发 → 发放灵力并恢复护盾。
private void OnParryConsumedHandler(BaseGames.Parry.ParryInfo info)
{
_stats?.AddSoul(info.SoulGained);
_shield?.OnParrySuccess();
Feedback.PlayParrySuccess();
}
/// 灵泉输入:地面且有剩余充能时转入 SpringState 使用一次。
private void OnUseSpring()
{
if (_stats == null || _stats.CurrentSpringCharges <= 0) return;
if (_movement != null && !_movement.IsGrounded) return;
if (_states.ContainsKey(typeof(SpringState)))
TransitionTo(GetState());
}
private void Start()
{
if (!HasRequiredStateDependencies())
return;
// 广播玩家 Transform:EnemyBase / ProjectileManager 等订阅者将通过事件接收引用
// (必须在 Start 中调用,确保所有 Awake/OnEnable 订阅已就绪)
_onPlayerSpawned?.Raise(transform);
TransitionTo(GetState());
}
private void Update()
{
if (!HasRequiredStateDependencies())
return;
// 冲刺冷却计时
_dashState?.TickCooldown(Time.deltaTime);
_currentState?.OnStateUpdate();
#if UNITY_EDITOR
_dbg_CurrentState = _currentState?.GetType().Name ?? "None";
_dbg_IsGrounded = _movement != null && _movement.IsGrounded;
_dbg_AirJumpsLeft = _airJumpsLeft;
_dbg_CanDash = _dashState?.CanDash ?? false;
_dbg_IsInvincible = _stats != null && _stats.IsInvincible;
#endif
}
private void FixedUpdate()
{
_currentState?.OnStateFixedUpdate();
}
private void LateUpdate()
{
_movement?.UpdateFacing();
}
// ── 状态机 ────────────────────────────────────────────────────────────
public void TransitionTo(PlayerStateBase newState)
{
#if UNITY_EDITOR
if (_debugValidateTransitions && _currentState != null && newState != null)
{
var allowed = _currentState.ValidTransitions;
if (allowed.Count > 0 && !allowed.Contains(newState.GetType()))
Debug.LogWarning(
$"[PlayerController] 非预期转换: {_currentState.GetType().Name} → {newState.GetType().Name}",
this);
}
#endif
_currentState?.OnStateExit();
_currentState = newState;
_currentState?.OnStateEnter();
}
private void InitializeStates()
{
_states[typeof(IdleState)] = new IdleState(this);
_states[typeof(RunState)] = new RunState(this);
_states[typeof(JumpState)] = new JumpState(this);
_states[typeof(FallState)] = new FallState(this);
_states[typeof(AttackState)] = new AttackState(this);
_states[typeof(DashState)] = new DashState(this);
_states[typeof(DownDashState)] = new DownDashState(this);
_states[typeof(WallSlideState)] = new WallSlideState(this);
_states[typeof(WallJumpState)] = new WallJumpState(this);
_states[typeof(AirAttackState)] = new AirAttackState(this);
_states[typeof(DownAttackState)] = new DownAttackState(this);
_states[typeof(UpAttackState)] = new UpAttackState(this);
_states[typeof(HurtState)] = new HurtState(this);
_states[typeof(DeadState)] = new DeadState(this);
_states[typeof(SpringState)] = new SpringState(this);
_states[typeof(ParryState)] = new ParryState(this);
_states[typeof(SwimState)] = new SwimState(this);
_dashState = (DashState)_states[typeof(DashState)];
}
///
/// 按类型获取状态实例。未注册时返回 null(供可选状态调用方安全使用)。
///
public T GetState() where T : PlayerStateBase
=> _states.TryGetValue(typeof(T), out var s) ? (T)s : null;
private void ResolveDependencies()
{
if (_movement == null)
_movement = GetComponent();
if (_stats == null)
_stats = GetComponent();
if (_animancer == null)
_animancer = GetComponent();
if (_inputBuffer == null)
_inputBuffer = GetComponent();
// 将唯一配置点(_inputReader)注入到同一 GameObject 上的 InputBuffer。
// InputBuffer 不再需要在 Inspector 单独配置 InputReaderSO。
if (_inputBuffer != null)
_inputBuffer.Init(_inputReader);
}
private bool HasRequiredStateDependencies()
{
if (_dependenciesReady) return true;
bool ok = _movement != null && _animancer != null && _inputBuffer != null && _inputReader != null;
if (ok)
{
_dependenciesReady = true;
_missingDependencyLogged = false;
return true;
}
if (!_missingDependencyLogged)
{
Debug.LogError($"[PlayerController] Missing required dependencies. " +
$"Movement={(_movement != null ? "OK" : "NULL")}, " +
$"Animancer={(_animancer != null ? "OK" : "NULL")}, " +
$"InputBuffer={(_inputBuffer != null ? "OK" : "NULL")}, " +
$"InputReader={(_inputReader != null ? "OK" : "NULL")}");
_missingDependencyLogged = true;
}
return false;
}
// ── 状态访问器 ────────────────────────────────────────────────────────
// 使用 GetState() 按类型获取任意状态实例,不再暴露具名属性。
}
}