using System.Collections.Generic;
using Cielonos.MainGame.Characters;
using SLSUtilities.General;
using SoftCircuits.Collections;
using UnityEngine;
namespace Cielonos.MainGame.Inventory
{
///
/// 光环子模块。持续捕获指定半径内的角色,并通过 Enter / Stay / Exit 事件通知订阅方。
/// 不使用 Data ScriptableObject,由道具在代码中直接构造。
///
public class AuraSubmodule : SubmoduleBase
{
///
/// 当前处于光环内的角色集合。
///
public HashSet charactersInAura = new HashSet();
///
/// 其他角色进入光环时触发,参数为进入的角色。
///
public OrderedDictionary> onOtherEnterAura = new();
///
/// 其他角色持续处于光环内时触发(按 stayInterval 间隔),参数为在光环内的角色。
///
public OrderedDictionary> onOtherStayAura = new();
///
/// 其他角色离开光环时触发,参数为离开的角色。
///
public OrderedDictionary> onOtherExitAura = new();
///
/// 光环半径。
///
public float auraRadius;
///
/// Stay 计时器,到达 stayInterval 后对所有在光环内的角色触发 onOtherStayAura。
///
public Timer stayTimer;
///
/// 物理检测的 LayerMask。
///
private readonly int _detectionLayerMask;
private const int MaxDetectionCount = 32;
private readonly Collider[] _overlapBuffer = new Collider[MaxDetectionCount];
///
/// 用于比较前后帧角色集合的临时缓冲。
///
private readonly HashSet _currentFrameCharacters = new HashSet();
///
/// 构造光环子模块。
///
/// 所属道具。
/// 光环半径。
/// Stay 事件触发间隔(秒),默认 0.5 秒。
public AuraSubmodule(ItemBase owner, float auraRadius, float stayInterval = 0.5f) : base(owner)
{
this.auraRadius = auraRadius;
stayTimer = new Timer(stayInterval, isInfinite: true);
stayTimer.onComplete.Add(new PrioritizedAction(OnStayTimerComplete));
_detectionLayerMask = LayerMask.GetMask("HurtBox");
}
///
/// 每帧调用,驱动物理检测和事件派发。
///
public void Update(float deltaTime)
{
DetectCharacters();
stayTimer.Update(deltaTime);
}
///
/// 清理所有追踪状态,对仍在光环内的角色触发 Exit 事件。
///
public void Dispose()
{
foreach (var character in charactersInAura)
{
if (character != null)
{
onOtherExitAura.Invoke(character);
}
}
charactersInAura.Clear();
}
///
/// 执行球形物理检测,比较前后帧角色集合,触发 Enter / Exit 事件。
///
private void DetectCharacters()
{
_currentFrameCharacters.Clear();
Vector3 center = owner.player.transform.position;
int hitCount = Physics.OverlapSphereNonAlloc(center, auraRadius, _overlapBuffer, _detectionLayerMask);
for (int i = 0; i < hitCount; i++)
{
CharacterBase character = _overlapBuffer[i].GetComponentInParent();
if (character == null || character == owner.player) continue;
_currentFrameCharacters.Add(character);
}
// 检测新进入光环的角色
foreach (var character in _currentFrameCharacters)
{
if (charactersInAura.Add(character))
{
onOtherEnterAura.Invoke(character);
}
}
// 检测离开光环的角色(从旧集合中移除不在当前帧的角色)
charactersInAura.RemoveWhere(character =>
{
if (_currentFrameCharacters.Contains(character)) return false;
if (character != null)
{
onOtherExitAura.Invoke(character);
}
return true;
});
}
///
/// Stay 计时器到期时,对所有在光环内的角色触发 onOtherStayAura。
///
private void OnStayTimerComplete()
{
foreach (var character in charactersInAura)
{
if (character != null)
{
onOtherStayAura.Invoke(character);
}
}
}
}
}