using System.Collections.Generic; using UnityEngine; namespace BaseGames.Enemies.Abilities { /// /// 多攻击选招器:从候选(category==Attack)里按"射程 + 冷却 + LOS + 着地"过滤, /// 再按 选一个。纯逻辑、无 MonoBehaviour 依赖,可单测。 /// public sealed class EnemyAttackSelector { private readonly List _candidates; public EnemyAttackSelector(IEnumerable candidates) => _candidates = new List(candidates); 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) { float total = 0f; for (int i = 0; i < _candidates.Count; i++) { var c = _candidates[i]; if (Eligible(c, hasLOS, grounded)) total += Mathf.Max(0f, c.Weight); } if (total <= 0f) return SelectByPriority(hasLOS, grounded); // 权重全 0 → 退化为确定性取首个合格 float roll = Random.value * total; for (int i = 0; i < _candidates.Count; i++) { var c = _candidates[i]; if (!Eligible(c, hasLOS, grounded)) continue; roll -= Mathf.Max(0f, c.Weight); if (roll <= 0f) return c; } return null; // 理论不达(浮点边界兜底) } } }