Initial import of BardFrontline

This commit is contained in:
2026-10-13 22:55:05 +08:00
commit f3ab7c58c3
24 changed files with 2917 additions and 0 deletions
+50
View File
@@ -0,0 +1,50 @@
namespace BardFrontline.Core;
public sealed record RuntimeConditions(bool Enabled, bool LoggedIn, bool ChangingArea, BattlefieldMode Mode,
bool DutyStarted, bool DutyCompleted, bool Bard, bool Alive, bool Mounted, bool Occupied, bool CoreReady, bool Piloting = false);
public static class ExecutionPolicy
{
public static string? PauseReason(RuntimeConditions s)
{
if (!s.Enabled) return "已关闭";
if (!s.LoggedIn) return "等待角色登录";
if (s.ChangingArea) return "切换区域";
if (s.Mode is not (BattlefieldMode.Frontline or BattlefieldMode.RivalWings)) return "仅在纷争前线 / 烈羽争锋运行";
if (!s.DutyStarted || s.DutyCompleted) return "等待对局开始 / 对局已结束";
if (s.Piloting) return "驾驶机甲中,暂停诗人循环";
if (!s.Bard) return "仅支持吟游诗人";
if (!s.Alive) return "角色死亡";
if (s.Mounted) return "坐骑上暂停";
if (s.Occupied) return "角色正在交互";
return s.CoreReady ? null : "核心技能数据缺失";
}
public static bool CanDispatch(bool queued, float animationLock, bool casting, bool pending, bool guard) =>
!queued && animationLock <= 0 && !casting && !pending && !guard;
public static bool CanWeave(float sharedRecastRemaining) => sharedRecastRemaining <= 0 || sharedRecastRemaining >= .65f;
// Encore is a time-limited LB follow-up. An extra GCD reservation must not starve it
// under stacked haste; the runtime still enforces casting, animation lock and native recast.
public static bool CanWeave(float sharedRecastRemaining, Skill skill) =>
skill == Skill.EncoreOfLight || CanWeave(sharedRecastRemaining);
public static bool SharesGcd(int primary, int additional, int shared) => shared >= 0 && (primary == shared || additional == shared);
public static float RecastRemaining(bool active, float total, float elapsed) =>
active && float.IsFinite(total) && float.IsFinite(elapsed) ? Math.Max(0, total - elapsed) : 0;
public static bool OwnsCast(uint ownedId, ulong ownedTarget, ushort ownedSequence,
uint castId, ulong castTarget, ushort usedSequence) =>
ownedId == castId && ownedTarget == castTarget && ownedSequence == usedSequence;
public static bool ShouldCancelCast(Skill castSkill, bool owned, bool invalidTarget, bool enemyGuard,
bool ignoreGuard, ActionDecision? emergency, bool nearbyThreat, bool hasControl)
{
if (!owned) return false;
if (Actions.IsHostile(castSkill) && (invalidTarget || enemyGuard && !ignoreGuard && castSkill != Skill.EagleEyeShot)) return true;
if (emergency == null) return false;
// Recovering HP is the point of elixir. Do not cancel a safe drink just because Recuperate came off CD.
return castSkill != Skill.Elixir || nearbyThreat || hasControl;
}
}