using System;
using System.Collections.Generic;
using UnityEngine;
using GameFramework;
namespace IndianOceanAssets.Engine2_5D
{
///
/// 灵灯系统 —— 挂在玩家物体上。
/// 按 U 键在玩家当前位置放置灵灯,灵灯照亮周围区域,
/// 怪物碰触灵灯会被扣 1 点血,灵灯消失并被回收。
/// 灵灯有存在时间,超时后自动回收。
///
public class SpiritLanternSystem : MonoBehaviour
{
[Header("灵灯数量")]
[Tooltip("最大可持有灵灯数量")]
[SerializeField] private int maxLanterns = 5;
[Header("按键")]
[SerializeField] private KeyCode lanternKey = KeyCode.U;
[Header("冷却")]
[Tooltip("放置灵灯的冷却时间(秒)")]
[SerializeField] private float cooldown = 3.5f;
[Header("灵灯预制体")]
[SerializeField] private GameObject lanternPrefab;
private int _remainingLanterns;
private float _lastPlaceTime = -999f;
/// 跟踪所有已放置的灵灯,用于回收时取消事件订阅,防止内存泄漏
private readonly List _activeLanterns = new List();
/// 当前可释放的灵灯数量(供 UI 使用)
public int RemainingLanterns => _remainingLanterns;
/// 冷却是否就绪
public bool IsCooldownReady => Time.time > _lastPlaceTime + cooldown;
/// 冷却剩余时间(供 UI 使用)
public float CooldownRemaining => Mathf.Max(0f, (_lastPlaceTime + cooldown) - Time.time);
private void Start()
{
_remainingLanterns = maxLanterns;
}
private void Update()
{
// 按 U 或鼠标左键 且 CD 就绪 且 还有灵灯 → 放置
if ((Input.GetKeyDown(lanternKey) || Input.GetMouseButtonDown(0))
&& _remainingLanterns > 0
&& Time.time > _lastPlaceTime + cooldown
&& lanternPrefab != null)
{
PlaceLantern();
}
}
///
/// 在玩家当前位置放置一个灵灯。
///
private void PlaceLantern()
{
_remainingLanterns--;
_lastPlaceTime = Time.time;
// 播放灵灯放置音效
if (AudioManager.Instance != null)
AudioManager.Instance.PlaySFXFromResources("SFX/Lantern", 0.8f);
// 在玩家位置实例化灵灯
GameObject lanternObj = Instantiate(lanternPrefab, transform.position, Quaternion.identity);
SpiritLantern lantern = lanternObj.GetComponent();
if (lantern != null)
{
// 设置所有者引用,用于回收时取消事件订阅
lantern.SetOwner(this);
// 订阅灵灯回收事件,并跟踪该灵灯
lantern.OnLanternRecalled += OnLanternRecalled;
_activeLanterns.Add(lantern);
}
}
///
/// 灵灯被回收时调用(怪物碰触或超时)。
/// 清理已销毁的灵灯引用,防止内存泄漏。
///
private void OnLanternRecalled()
{
// 清理列表中已销毁的灵灯引用
_activeLanterns.RemoveAll(l => l == null);
_remainingLanterns = Mathf.Min(_remainingLanterns + 1, maxLanterns);
}
///
/// 显式取消订阅指定灵灯的事件并从跟踪列表移除。
/// 由 SpiritLantern 在销毁前调用,确保事件订阅被正确释放。
///
public void UnregisterLantern(SpiritLantern lantern)
{
if (lantern != null)
{
lantern.OnLanternRecalled -= OnLanternRecalled;
_activeLanterns.Remove(lantern);
}
}
///
/// 安全网:玩家销毁或场景卸载时,清理所有灵灯的事件订阅。
///
private void OnDestroy()
{
foreach (var lantern in _activeLanterns)
{
if (lantern != null)
lantern.OnLanternRecalled -= OnLanternRecalled;
}
_activeLanterns.Clear();
}
// ===== 编辑器可视化 =====
private void OnDrawGizmosSelected()
{
// 绘制冷却指示
if (Time.time < _lastPlaceTime + cooldown)
{
float remaining = (_lastPlaceTime + cooldown) - Time.time;
float ratio = remaining / cooldown;
Gizmos.color = new Color(1f, 0.6f, 0f, 0.3f);
Gizmos.DrawWireSphere(transform.position, 1f * ratio);
}
}
}
}