using System.Collections;
using BaseGames.Feedback;
using BaseGames.Player;
using UnityEngine;
namespace BaseGames.World
{
///
/// 同场景成对门传送组件。两扇门互相引用,玩家进入其中一扇门时被传送到另一扇。
///
/// 触发流程:
///
/// - 玩家进入触发器或按交互键 → 播放 转场反馈(淡出)
/// - 等待反馈完成后传送玩家到 的 位置,并设定朝向
/// - 播放 的 转场反馈(淡入),目标门进入冷却
///
///
/// 不涉及场景加载;适用于同房间内不同小区域之间的传送门、电梯、秘道等。
///
[RequireComponent(typeof(Collider2D))]
public class LinkedDoorTransition : MonoBehaviour, IInteractable
{
[Header("配对")]
[Tooltip("与此门配对的另一扇门。玩家进入本门后传送到此目标门的出生点。")]
[SerializeField] private LinkedDoorTransition _linkedDoor;
[Header("出生配置")]
[Tooltip("传送到此门后玩家的出生位置。拖动场景中的子节点 SpawnPoint 来调整。\n为空时回退到 GameObject 中心。")]
[SerializeField] private Transform _spawnPoint;
[Tooltip("传送到此门后玩家的朝向(+1 = 朝右,-1 = 朝左)。")]
[SerializeField] private int _facingDirectionOnArrive = 1;
[Header("触发方式")]
[Tooltip("true = 玩家进入触发器自动触发;false = 需玩家按交互键。")]
[SerializeField] private bool _autoTrigger = false;
[Header("转场反馈")]
[Tooltip("传送前播放(淡出)。留空则跳过,直接传送。")]
[SerializeField] private SceneFeedback _transitionOut;
[Tooltip("传送后播放(淡入)。留空则跳过。由目标门在玩家到达后自动播放。")]
[SerializeField] private SceneFeedback _transitionIn;
[Header("钥匙物品校验")]
[SerializeField] private bool _requiresKeyItem;
[SerializeField] private string _requiredItemId;
[Header("世界状态")]
[SerializeField] private WorldStateRegistry _worldState;
[Header("冷却")]
[Tooltip("传送完成后本门的冷却时间(秒)。防止玩家被反复来回传送。")]
[SerializeField] private float _cooldown = 1.5f;
private bool _triggered;
private float _cooldownUntil;
// ── IInteractable ─────────────────────────────────────────────────────
public bool CanInteract => !_autoTrigger;
public string InteractPrompt => "进入";
public void Interact(Transform player) => TryTrigger(player);
public void OnPlayerEnterRange(Transform player) { }
public void OnPlayerExitRange() { }
// ── 触发 ──────────────────────────────────────────────────────────────
private void OnTriggerEnter2D(Collider2D other)
{
if (!_autoTrigger) return;
if (!other.CompareTag("Player")) return;
TryTrigger(other.transform);
}
private void TryTrigger(Transform player)
{
if (_triggered) return;
if (Time.time < _cooldownUntil) return;
if (_linkedDoor == null)
{
Debug.LogWarning($"[LinkedDoorTransition] {name}:未配置 _linkedDoor,传送中止。");
return;
}
if (_requiresKeyItem && !HasItem(_requiredItemId)) return;
_triggered = true;
StartCoroutine(TransitionCoroutine(player));
}
private void OnDisable() => _triggered = false;
// ── 传送流程 ──────────────────────────────────────────────────────────
private IEnumerator TransitionCoroutine(Transform player)
{
// 1. 淡出
if (_transitionOut != null)
yield return _transitionOut.PlayAndWait();
// 2. 传送
player.position = _linkedDoor.GetSpawnWorldPosition();
// 通过 PlayerMovement 设定朝向(同步清零速度,防止旧速度覆盖朝向)
var movement = player.GetComponentInChildren()
?? player.GetComponent();
if (movement != null)
movement.SetFacingImmediate(_linkedDoor._facingDirectionOnArrive);
// 4. 淡入(目标门播放)+ 冷却
_linkedDoor.PlayTransitionIn();
_linkedDoor.StartCooldown(_cooldown);
_triggered = false;
}
/// 播放此门的淡入反馈。由传送发起方在玩家到达后调用。
public void PlayTransitionIn() => _transitionIn?.Play();
/// 启动传送冷却,在此期间本门不会再次触发传送。
public void StartCooldown(float duration) => _cooldownUntil = Time.time + duration;
// ── 辅助 ──────────────────────────────────────────────────────────────
/// 返回传送目标位置。优先使用 _spawnPoint 子节点,否则回退到门中心。
public Vector3 GetSpawnWorldPosition()
=> _spawnPoint != null ? _spawnPoint.position : transform.position;
private bool HasItem(string itemId)
{
if (string.IsNullOrEmpty(itemId)) return true;
if (_worldState == null)
{
Debug.LogWarning($"[LinkedDoorTransition] WorldStateRegistry 未配置,钥匙 {itemId} 检查跳过");
return false;
}
return _worldState.IsCollected(itemId);
}
// ── Gizmos ────────────────────────────────────────────────────────────
#if UNITY_EDITOR
private void OnDrawGizmos()
{
var col = GetComponent();
if (col != null)
{
Gizmos.color = new Color(0.2f, 0.9f, 0.9f, 0.7f);
Gizmos.DrawWireCube(col.bounds.center, col.bounds.size);
Gizmos.color = new Color(0.2f, 0.9f, 0.9f, 0.15f);
Gizmos.DrawCube(col.bounds.center, col.bounds.size);
}
Vector3 spawn = GetSpawnWorldPosition();
Gizmos.color = Color.cyan;
Gizmos.DrawWireSphere(spawn, 0.2f);
if (_linkedDoor != null)
{
Gizmos.color = new Color(0.2f, 0.9f, 0.9f, 0.8f);
Gizmos.DrawLine(spawn, _linkedDoor.GetSpawnWorldPosition());
}
Vector3 labelPos = col != null
? col.bounds.center + Vector3.up * (col.bounds.extents.y + 0.3f)
: transform.position + Vector3.up * 1.5f;
string facing = _facingDirectionOnArrive >= 0 ? "→" : "←";
string trigger = _autoTrigger ? "Auto" : "Interact";
string linkName = _linkedDoor != null ? _linkedDoor.name : "未配对";
string keyInfo = _requiresKeyItem ? $" 🔑{_requiredItemId}" : "";
string grandparent = transform.parent != null ? $"[{transform.parent.name}] " : "";
string label = $"{grandparent}{name}\n→ {linkName} | {trigger} | 到达朝向:{facing}{keyInfo}";
UnityEditor.Handles.color = Color.white;
UnityEditor.Handles.Label(labelPos, label);
}
#endif
}
}