using System.Collections.Generic;
using UnityEngine;
namespace BaseGames.Enemies.Abilities
{
///
/// 多攻击选招器:从候选(category==Attack)里按"射程 + 冷却 + LOS + 着地"过滤,
/// 再按 选一个。纯逻辑、无 MonoBehaviour 依赖,可单测。
///
public sealed class EnemyAttackSelector
{
private readonly List _candidates;
// 权重缓冲区(与 _candidates 等长,每次选招复用):避免每次选招 new List 造成 GC
private readonly List _weightBuf;
public EnemyAttackSelector(IEnumerable candidates)
{
_candidates = new List(candidates);
_weightBuf = new List(_candidates.Count);
}
public int Count => _candidates.Count;
private static bool Eligible(IAttackCandidate c, bool hasLOS, bool grounded)
=> c != null && c.CanUse && c.InAttackRange()
&& (!c.RequiresLineOfSight || hasLOS)
&& (!c.RequiresGrounded || grounded);
public bool HasEligible(bool hasLOS, bool grounded)
{
for (int i = 0; i < _candidates.Count; i++)
if (Eligible(_candidates[i], hasLOS, grounded)) return true;
return false;
}
public IAttackCandidate Select(bool hasLOS, bool grounded, AttackSelectionMode mode)
=> mode == AttackSelectionMode.Priority
? SelectByPriority(hasLOS, grounded)
: SelectByWeight(hasLOS, grounded);
private IAttackCandidate SelectByPriority(bool hasLOS, bool grounded)
{
IAttackCandidate best = null;
for (int i = 0; i < _candidates.Count; i++)
{
var c = _candidates[i];
if (!Eligible(c, hasLOS, grounded)) continue;
if (best == null || c.Priority > best.Priority) best = c; // 并列取首个
}
return best;
}
private IAttackCandidate SelectByWeight(bool hasLOS, bool grounded)
{
// 有效权重:不合格候选填 0(由共享原语保证零权重项绝不被选中)。缓冲区复用 → 选招零 GC。
_weightBuf.Clear();
for (int i = 0; i < _candidates.Count; i++)
{
var c = _candidates[i];
_weightBuf.Add(Eligible(c, hasLOS, grounded) ? Mathf.Max(0f, c.Weight) : 0f);
}
int idx = BaseGames.Core.WeightedPick.Index(_weightBuf);
if (idx >= 0) return _candidates[idx];
// 无正权重(合格候选权重全 0)→ 退化为按 Priority 选(并列取首个)
return SelectByPriority(hasLOS, grounded);
}
}
}