54 lines
2.9 KiB
C#
54 lines
2.9 KiB
C#
namespace BardFrontline.Core;
|
|
|
|
public sealed class ManualControl
|
|
{
|
|
private bool initialized;
|
|
private ulong lastTarget;
|
|
private ushort lastSequence;
|
|
private double until;
|
|
public bool Forced { get; set; }
|
|
public double TargetChangedAt { get; private set; }
|
|
public bool Active(double now) => Forced || now < until;
|
|
public void Observe(double now, ulong target, ushort sequence, bool pluginPending, Settings c)
|
|
{
|
|
if (initialized && (target != lastTarget || sequence != lastSequence && !pluginPending) && c.DetectManualControl)
|
|
until = now + c.ManualControlSeconds;
|
|
if (!initialized || target != lastTarget) TargetChangedAt = now;
|
|
initialized = true; lastTarget = target; lastSequence = sequence;
|
|
if (!c.DetectManualControl) until = 0;
|
|
}
|
|
public void OwnTarget(ulong id, double now) { if (id != lastTarget) TargetChangedAt = now; lastTarget = id; }
|
|
public void OwnAction(ushort sequence) => lastSequence = sequence;
|
|
public void Resume() { Forced = false; until = 0; initialized = false; }
|
|
public void ResetObservations() { initialized = false; until = 0; TargetChangedAt = 0; }
|
|
}
|
|
|
|
public sealed class WeaveTiming
|
|
{
|
|
private readonly Queue<float> samples = new();
|
|
private (double Start, double? Ack, double Unlock)? pending;
|
|
public int SampleCount => samples.Count;
|
|
public float Budget => samples.Count < 3 ? .65f : Math.Clamp(samples.Order().ElementAt((int)Math.Ceiling(samples.Count * .9) - 1) + .05f, .5f, 1.25f);
|
|
public void Submitted(double now, bool cast) { pending = cast ? null : (now, null, now); }
|
|
public void Observe(double now, bool acknowledged, float animationLock, bool queued, bool casting)
|
|
{
|
|
if (pending is not { } p) return;
|
|
if (now - p.Start > 2 || casting) { pending = null; return; }
|
|
if (acknowledged && p.Ack == null) p.Ack = now;
|
|
p.Unlock = Math.Max(p.Unlock, now + Math.Max(0, animationLock));
|
|
pending = p;
|
|
if (p.Ack == null || animationLock > 0 || queued) return;
|
|
var duration = (float)(Math.Max(p.Ack.Value, p.Unlock) - p.Start);
|
|
pending = null;
|
|
if (duration is < .05f or > 1.5f) return;
|
|
samples.Enqueue(duration); while (samples.Count > 20) samples.Dequeue();
|
|
}
|
|
public bool Allows(float remaining, Skill skill, bool adaptive, BurstStrategy strategy = BurstStrategy.Normal, bool casting = false) =>
|
|
skill == Skill.EncoreOfLight || remaining <= 0 || remaining >= (adaptive ? Budget : .65f)
|
|
// This only waives our extra GCD reservation; native locks/recasts still gate dispatch.
|
|
|| strategy == BurstStrategy.Pressure && !casting && skill is (Skill.HarmonicArrow or Skill.PitchPerfect
|
|
or Skill.BlastArrow or Skill.ApexArrow or Skill.SilentNocturne or Skill.EagleEyeShot
|
|
or Skill.Bravery or Skill.Dervish or Skill.FinalFantasia);
|
|
public void Reset() { samples.Clear(); pending = null; }
|
|
}
|