namespace BardFrontline.Core; public sealed record SkillAudit(Skill Skill, ulong TargetId, string Result); public sealed record DecisionFrame(long Number, double Time, string Phase, ActionDecision? Decision, IReadOnlyList Skills); public static class DecisionAudit { public static IReadOnlyList Evaluate(CombatSnapshot? s, Settings c, ActionDecision? selected, string phase, Func nativeCheck, Func? targetProbe = null) { var result = new List(); var cache = new Dictionary<(Skill, ulong), string?>(); string? Check(Skill skill, Actor target) { if (!cache.TryGetValue((skill, target.Id), out var value)) cache[(skill, target.Id)] = value = nativeCheck(skill, target); return value; } var hasEnemy = s != null && s.Enemies.Any(a => Actions.All.Any(skill => TargetSelector.Relevant(skill, s, c) && TargetSelector.Allowed(a, c, skill) && (targetProbe?.Invoke(skill, a) ?? Check(skill, a) == null))); var pool = s == null ? [] : TargetSelector.CombatCandidates(s, c, new Availability(Check)); foreach (var skill in Actions.All) { if (selected?.Skill == skill) { result.Add(new(skill, selected.TargetId, $"本轮选中 → {selected.TargetId:X}:{selected.Reason}")); continue; } var reason = s == null ? phase : Strategy.PolicyReason(skill, s, c); if (reason == null && skill is Skill.Bravery or Skill.Dervish or Skill.FinalFantasia && !hasEnemy) reason = "没有可攻击的增益目标"; if (reason == null && skill == Skill.Sprint && hasEnemy) reason = "存在有效攻击目标,不自动冲刺"; if (reason != null) { result.Add(new(skill, 0, reason)); continue; } var state = s!; IEnumerable candidates = Actions.IsHostile(skill) ? pool : skill == Skill.WardensPaean ? new[] { state.Player }.Concat(c.HelpParty ? state.Party : []) : [state.Player]; var failures = new HashSet(); Actor? usable = null; foreach (var target in TargetSelector.Ordered(candidates, c, state.CurrentTarget).OrderBy(a => a.IsObjective)) { if (Actions.IsHostile(skill) && !TargetSelector.Allowed(target, c, skill)) { failures.Add("目标被死亡 / 防御 / 免疫 / 分类设置排除"); continue; } if (skill == Skill.EagleEyeShot && c.EagleStrategy == EagleStrategy.FinisherOnly && target.EffectiveHp > (ulong)c.EagleFinisherHp) { failures.Add("严格收割:有效 HP 高于阈值"); continue; } if (skill == Skill.WardensPaean && (!target.Alive || !target.Targetable || !Statuses.Controls.Any(target.Has))) continue; var failure = Check(skill, target); if (failure != null) { failures.Add(failure); continue; } usable = target; break; } result.Add(usable == null ? new(skill, 0, failures.Count == 0 ? "没有符合策略的目标" : string.Join(";", failures.Take(4))) : new(skill, usable.Id, selected != null ? $"可用 → {usable.Name};本轮优先选择 {Actions.Name(selected.Skill)}" : $"就绪候选 → {usable.Name};当前执行阶段:{phase}")); } return result; } private sealed class Availability(Func check) : IActionAvailability { public bool CanUse(Skill skill, Actor target) => check(skill, target) == null; } }