Initial import of BardFrontline
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
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<SkillAudit> Skills);
|
||||
|
||||
public static class DecisionAudit
|
||||
{
|
||||
public static IReadOnlyList<SkillAudit> Evaluate(CombatSnapshot? s, Settings c, ActionDecision? selected,
|
||||
string phase, Func<Skill, Actor, string?> nativeCheck, Func<Skill, Actor, bool>? targetProbe = null)
|
||||
{
|
||||
var result = new List<SkillAudit>();
|
||||
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<Actor> candidates = Actions.IsHostile(skill) ? pool
|
||||
: skill == Skill.WardensPaean ? new[] { state.Player }.Concat(c.HelpParty ? state.Party : [])
|
||||
: [state.Player];
|
||||
var failures = new HashSet<string>();
|
||||
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<Skill, Actor, string?> check) : IActionAvailability
|
||||
{ public bool CanUse(Skill skill, Actor target) => check(skill, target) == null; }
|
||||
}
|
||||
Reference in New Issue
Block a user