using WebmrAPI.Utils.Sequences; namespace WebmrAPI.Utils { public sealed class ScrollAmountGenerator : BaseGenerator { public enum Direction { Up, Down } public class CorrectionData { public Direction Dir { get; set; } public int Order { get; set; } } private double _fJitter = 0.12; private double _fAccuracy = 0.83; private int _nCorrections = 0; private int _bAmount = 120; public int TotalAmount { get; private set; } public int CurrentAmount { get; private set; } public int BaseAmount { get => _bAmount; private set => UpdateSpread(value, FJitter); } public double FJitter { get => _fJitter; private set => UpdateSpread(BaseAmount, Math.Clamp(Math.Abs(value), 0.0, 1.0)); } public int CorrectionLimit { get; private set; } = 1; public override double Progress { get => (double)CurrentAmount / TotalAmount; protected set => base.Progress = value; } public double FAccuracy { get => _fAccuracy; private set => _fAccuracy = Math.Clamp(Math.Abs(value), 0.0, 1.0); } public Direction Dir { get; private set; } public override double Step { get; protected set; } public delegate void CorrectionHandler(object sender, CorrectionData data); public event CorrectionHandler? Correction; public ScrollAmountGenerator SetBaseAmount(int baseAmount) { BaseAmount = baseAmount; return this; } public ScrollAmountGenerator SetJitterFactor(double jitterFactor) { FJitter = jitterFactor; return this; } public ScrollAmountGenerator SetCorrectionLimit(int limit) { CorrectionLimit = limit; return this; } public ScrollAmountGenerator SetAccuracyFactor(double accuracyFactor) { FAccuracy = accuracyFactor; return this; } private void ToggleDirection() { Dir = Dir == Direction.Up ? Direction.Down : Direction.Up; } private void UpdateSpread(int baseAmount, double jitterFactor) { var offset = baseAmount * jitterFactor; var amount = Math.Abs(Double.CreateChecked(baseAmount)); _bAmount = baseAmount; _fJitter = jitterFactor; Min = (int)(amount - offset); Max = (int)(amount + offset); } protected override int Calc() { int value; if (Generate(0, 100000) > (int)(FAccuracy * 100000)) value = Generate(Min, Max); else value = BaseAmount; if (Dir == Direction.Down) { value = -value; } CurrentAmount += value; return value; } public override int Next() { if (Progress < 0) { Progress = 0; return First(); } if (!HasValues || IsZero(1.0 - Progress)) { HasValues = false; Progress = 1.0; return Last(); } else if (Progress > 1.0 && _nCorrections < CorrectionLimit) { ToggleDirection(); Correction?.Invoke(this, new CorrectionData { Dir = Dir, Order = ++_nCorrections }); } return Calc(); } public override ISequenceGenerator Reset() { HasValues = true; if ((_nCorrections & 1) != 0) { ToggleDirection(); } _nCorrections = 0; return this; } public ScrollAmountGenerator(int totalAmount, Direction dir) : base(default, default) { TotalAmount = (dir == Direction.Up) ? totalAmount : -totalAmount; Dir = dir; UpdateSpread(BaseAmount, FJitter); } } }