using UnityEngine; using BaseGames.Core; using BaseGames.Core.Events; using BaseGames.Input; using BaseGames.Player; using BaseGames.Player.States; using BaseGames.Progression; namespace BaseGames.Support.AntiSoftlock { /// /// 防软锁系统(架构 16_SupportingModules §5)。 /// 检测玩家长时间静止且无法移动的情况,提供逃脱选项。 /// public class AntiSoftlockSystem : MonoBehaviour { [Header("配置")] [Tooltip("玩家静止超过此秒数后显示逃脱提示")] [SerializeField] private float _stuckThresholdSeconds = 30f; [Tooltip("移动速度低于此值视为静止")] [SerializeField] private float _minVelocityThreshold = 0.1f; [Header("逃脱路径列表(按优先级降序排列)")] [SerializeField] private RoomEscapeInfoSO[] _escapeOptions; [Header("事件频道")] [SerializeField] private SceneLoadRequestEventChannelSO _onSceneLoadRequest; [SerializeField] private VoidEventChannelSO _onShowEscapeUI; [SerializeField] private VoidEventChannelSO _onHideEscapeUI; /// /// 玩家生成事件频道(PlayerController.Start() 广播)。 /// 替代 FindFirstObjectByType 的全场景扫描。 /// [SerializeField] private TransformEventChannelSO _onPlayerSpawned; [Header("引用")] [SerializeField] private InputReaderSO _input; private PlayerController _player; private Rigidbody2D _playerRb; private float _stuckTimer; private bool _escapeUIVisible; private Vector2 _lastPos; private readonly BaseGames.Core.Events.CompositeDisposable _subs = new(); private void OnEnable() { if (_onPlayerSpawned != null) _onPlayerSpawned.Subscribe(OnPlayerSpawned).AddTo(_subs); } private void OnDisable() => _subs.Clear(); private void OnPlayerSpawned(Transform playerTransform) { if (playerTransform == null) return; _player = playerTransform.GetComponent(); _playerRb = playerTransform.GetComponent(); _lastPos = playerTransform.position; _stuckTimer = 0f; } private void Update() { if (_player == null || !_player.Stats.IsAlive) return; Vector2 pos = _player.transform.position; float vel = _playerRb != null ? _playerRb.velocity.magnitude : Vector2.Distance(pos, _lastPos) / Time.deltaTime; if (vel > _minVelocityThreshold) { _stuckTimer = 0f; _lastPos = pos; if (_escapeUIVisible) HideEscapeUI(); return; } _lastPos = pos; _stuckTimer += Time.deltaTime; if (_stuckTimer >= _stuckThresholdSeconds && !_escapeUIVisible) ShowEscapeUI(); } private void ShowEscapeUI() { _escapeUIVisible = true; _onShowEscapeUI?.Raise(); } private void HideEscapeUI() { _escapeUIVisible = false; _onHideEscapeUI?.Raise(); } /// 执行最优先的逃脱路径(由 UI 按钮调用)。 public void ExecuteEscape() { var escape = ChooseBestEscape(); HideEscapeUI(); var sm = ServiceLocator.GetOrDefault(); string scene = string.IsNullOrEmpty(escape?.targetScene) ? sm?.LastCheckpointScene : escape.targetScene; string spawn = string.IsNullOrEmpty(escape?.spawnId) ? sm?.LastCheckpointSpawnId : escape.spawnId; _onSceneLoadRequest?.Raise(new SceneLoadRequest { SceneName = scene, EntryId = spawn, TransitionType = TransitionType.Scene, IsRespawn = true, }); } private RoomEscapeInfoSO ChooseBestEscape() { if (_escapeOptions == null || _escapeOptions.Length == 0) return null; RoomEscapeInfoSO best = null; int bestPriority = int.MinValue; foreach (var opt in _escapeOptions) { if (opt == null) continue; if (opt.requiredAbility != BaseGames.Player.AbilityType.None && (_player?.Stats.HasAbility(opt.requiredAbility) == false)) continue; if (opt.priority > bestPriority) { bestPriority = opt.priority; best = opt; } } return best; } } }