diff --git a/Craftimizer/Configuration.cs b/Craftimizer/Configuration.cs index 1580d69..d567b67 100644 --- a/Craftimizer/Configuration.cs +++ b/Craftimizer/Configuration.cs @@ -188,9 +188,11 @@ public partial class Configuration public bool SuggestMacroAutomatically { get; set; } public bool ShowCommunityMacros { get; set; } = true; public bool SearchCommunityMacroAutomatically { get; set; } - public int SynthHelperStepCount { get; set; } = 3; + public int SynthHelperStepCount { get; set; } = 1; + public int SynthHelperMaxDisplayCount { get; set; } = 5; public bool SynthHelperDisplayOnlyFirstStep { get; set; } public bool SynthHelperAbilityAnts { get; set; } + public bool CheckDelineations { get; set; } = true; public bool PinSynthHelperToWindow { get; set; } = true; public bool PinRecipeNoteToWindow { get; set; } = true; diff --git a/Craftimizer/Utils/Gearsets.cs b/Craftimizer/Utils/Gearsets.cs index 2ec7d8a..449886f 100644 --- a/Craftimizer/Utils/Gearsets.cs +++ b/Craftimizer/Utils/Gearsets.cs @@ -98,10 +98,10 @@ public static unsafe class Gearsets }; } - public static CharacterStats CalculateCharacterStats(GearsetItem[] gearsetItems, int characterLevel, bool canUseManipulation) => - CalculateCharacterStats(CalculateGearsetStats(gearsetItems), gearsetItems, characterLevel, canUseManipulation); + public static CharacterStats CalculateCharacterStats(GearsetItem[] gearsetItems, int characterLevel, bool canUseManipulation, bool checkDelineations) => + CalculateCharacterStats(CalculateGearsetStats(gearsetItems), gearsetItems, characterLevel, canUseManipulation, checkDelineations); - public static CharacterStats CalculateCharacterStats(GearsetStats gearsetStats, GearsetItem[] gearsetItems, int characterLevel, bool canUseManipulation) => + public static CharacterStats CalculateCharacterStats(GearsetStats gearsetStats, GearsetItem[] gearsetItems, int characterLevel, bool canUseManipulation, bool checkDelineations) => new() { CP = gearsetStats.CP, @@ -110,9 +110,12 @@ public static unsafe class Gearsets Level = characterLevel, CanUseManipulation = canUseManipulation, HasSplendorousBuff = gearsetItems.Any(IsSplendorousTool), - IsSpecialist = gearsetItems.Any(IsSpecialistSoulCrystal), + IsSpecialist = gearsetItems.Any(IsSpecialistSoulCrystal) && (!checkDelineations || HasDelineations()), }; + public static bool HasDelineations() => + InventoryManager.Instance()->GetInventoryItemCount(28724) > 0; + public static bool IsItem(GearsetItem item, uint itemId) => item.ItemId == itemId; diff --git a/Craftimizer/Utils/SimulatedMacro.cs b/Craftimizer/Utils/SimulatedMacro.cs index 28fb485..cb9753f 100644 --- a/Craftimizer/Utils/SimulatedMacro.cs +++ b/Craftimizer/Utils/SimulatedMacro.cs @@ -213,10 +213,13 @@ internal sealed class SimulatedMacro TryRecalculateFrom(Math.Min(fromIdx, toIdx)); } - public int Enqueue(ActionType action) + public int Enqueue(ActionType action, int? maxSize = null) { lock (QueueLock) { + if (maxSize is { } size && QueuedSteps.Count + Macro.Count >= size) + return size; + QueuedSteps.Add(new(action)); return QueuedSteps.Count + Macro.Count; } diff --git a/Craftimizer/Windows/RecipeNote.cs b/Craftimizer/Windows/RecipeNote.cs index 35f34b8..670d945 100644 --- a/Craftimizer/Windows/RecipeNote.cs +++ b/Craftimizer/Windows/RecipeNote.cs @@ -223,7 +223,7 @@ public sealed unsafe class RecipeNote : Window, IDisposable gearItems = Gearsets.GetGearsetItems(container); - var characterStats = Gearsets.CalculateCharacterStats(gearStats, gearItems, RecipeData.ClassJob.GetPlayerLevel(), RecipeData.ClassJob.CanPlayerUseManipulation()); + var characterStats = Gearsets.CalculateCharacterStats(gearStats, gearItems, RecipeData.ClassJob.GetPlayerLevel(), RecipeData.ClassJob.CanPlayerUseManipulation(), Service.Configuration.CheckDelineations); if (characterStats != CharacterStats) { CharacterStats = characterStats; diff --git a/Craftimizer/Windows/Settings.cs b/Craftimizer/Windows/Settings.cs index 7c14b3d..df3bfb8 100644 --- a/Craftimizer/Windows/Settings.cs +++ b/Craftimizer/Windows/Settings.cs @@ -96,10 +96,10 @@ public sealed class Settings : Window, IDisposable private static void DrawOption(string label, string tooltip, string value, Action setter, ref bool isDirty) { ImGui.SetNextItemWidth(OptionWidth); - var text = value.ToString(); + var text = value; if (ImGui.InputText(label, ref text, 255, ImGuiInputTextFlags.AutoSelectAll)) { - if (value != text) + if (!string.Equals(value, text, StringComparison.Ordinal)) { setter(text); isDirty = true; @@ -223,6 +223,15 @@ public sealed class Settings : Window, IDisposable ref isDirty ); + DrawOption( + "Check For Delineations", + "Your inventory will be checked to ensure that you have delineations available " + + "before suggesting any specialist acitons.", + Config.CheckDelineations, + v => Config.CheckDelineations = v, + ref isDirty + ); + DrawOption( "Reliability Trial Count", "When testing for reliability of a macro in the editor, this many trials will be " + @@ -943,8 +952,9 @@ public sealed class Settings : Window, IDisposable ); DrawOption( - "Step Count", - "The minimum number of future steps to solve for during an in-game craft.", + "Solver Step Count", + "The minimum number of future steps to solve for during an in-game craft. " + + "The solver may still give more than this amount if it's at no cost to you.", Config.SynthHelperStepCount, 1, 100, @@ -952,6 +962,17 @@ public sealed class Settings : Window, IDisposable ref isDirty ); + DrawOption( + "Max Step Display Count", + "Enforces a maximum number of steps to display in the synth helper to " + + "get rid of clutter.", + Config.SynthHelperMaxDisplayCount, + 1, + 100, + v => Config.SynthHelperMaxDisplayCount = v, + ref isDirty + ); + ImGuiHelpers.ScaledDummy(5); ImGui.Separator(); ImGuiHelpers.ScaledDummy(5); diff --git a/Craftimizer/Windows/SynthHelper.cs b/Craftimizer/Windows/SynthHelper.cs index 0b6f8aa..3eac878 100644 --- a/Craftimizer/Windows/SynthHelper.cs +++ b/Craftimizer/Windows/SynthHelper.cs @@ -66,10 +66,9 @@ public sealed unsafe class SynthHelper : Window, IDisposable private SimulationState currentState; private SimulatedMacro Macro { get; } = new(); - private CancellationTokenSource? HelperTaskTokenSource { get; set; } - private Exception? HelperTaskException { get; set; } - private Solver.Solver? HelperTaskObject { get; set; } - private bool HelperTaskRunning => HelperTaskTokenSource != null; + private BackgroundTask? SolverTask { get; set; } + private bool SolverRunning => (!SolverTask?.Completed) ?? false; + private Solver.Solver? SolverObject { get; set; } private IFontHandle AxisFont { get; } @@ -124,7 +123,7 @@ public sealed unsafe class SynthHelper : Window, IDisposable if (WasCalculatable) { IsCrafting = false; - HelperTaskTokenSource?.Cancel(); + SolverTask?.Cancel(); } else if (Macro.Count == 0) { @@ -263,7 +262,7 @@ public sealed unsafe class SynthHelper : Window, IDisposable DrawMacroActions(); - if (HelperTaskRunning && HelperTaskObject is { } solver) + if (SolverRunning && SolverObject is { } solver) { ImGuiHelpers.ScaledDummy(5); DrawHelperTaskProgress(solver); @@ -334,7 +333,7 @@ public sealed unsafe class SynthHelper : Window, IDisposable lastState = state; } - var rows = (int)Math.Max(1, MathF.Ceiling(Service.Configuration.SynthHelperStepCount / itemsPerRow)); + var rows = (int)Math.Max(1, MathF.Ceiling(Service.Configuration.SynthHelperMaxDisplayCount / itemsPerRow)); for (var i = 0; i < rows; ++i) { if (count <= i * itemsPerRow) @@ -452,9 +451,9 @@ public sealed unsafe class SynthHelper : Window, IDisposable private void DrawMacroActions() { - if (HelperTaskRunning) + if (SolverRunning) { - if (HelperTaskTokenSource?.IsCancellationRequested ?? false) + if (SolverTask?.Cancelling ?? false) { using var _disabled = ImRaii.Disabled(); ImGui.Button("Stopping", new(-1, 0)); @@ -464,7 +463,7 @@ public sealed unsafe class SynthHelper : Window, IDisposable else { if (ImGui.Button("Stop", new(-1, 0))) - HelperTaskTokenSource?.Cancel(); + SolverTask?.Cancel(); } } else @@ -511,7 +510,7 @@ public sealed unsafe class SynthHelper : Window, IDisposable var gearItems = Gearsets.GetGearsetItems(container); - var characterStats = Gearsets.CalculateCharacterStats(gearStats, gearItems, RecipeData.ClassJob.GetPlayerLevel(), RecipeData.ClassJob.CanPlayerUseManipulation()); + var characterStats = Gearsets.CalculateCharacterStats(gearStats, gearItems, RecipeData.ClassJob.GetPlayerLevel(), RecipeData.ClassJob.CanPlayerUseManipulation(), Service.Configuration.CheckDelineations); if (characterStats != CharacterStats) { CharacterStats = characterStats; @@ -606,9 +605,7 @@ public sealed unsafe class SynthHelper : Window, IDisposable private void CalculateBestMacro() { - HelperTaskTokenSource?.Cancel(); - HelperTaskTokenSource = new(); - HelperTaskException = null; + SolverTask?.Cancel(); Macro.ClearQueue(); Macro.Clear(); @@ -619,55 +616,34 @@ public sealed unsafe class SynthHelper : Window, IDisposable Macro.RecalculateState(); } - var token = HelperTaskTokenSource.Token; var state = CurrentState; - var task = Task.Run(() => CalculateBestMacroTask(state, token), token); - _ = task.ContinueWith(t => - { - if (token == HelperTaskTokenSource.Token) - { - HelperTaskTokenSource = null; - HelperTaskObject = null; - } - }); - _ = task.ContinueWith(t => - { - if (token.IsCancellationRequested) - return; - - try - { - t.Exception!.Flatten().Handle(ex => ex is TaskCanceledException or OperationCanceledException); - } - catch (AggregateException e) - { - HelperTaskException = e; - Log.Error(e, "Calculating macro failed"); - } - }, TaskContinuationOptions.OnlyOnFaulted); + SolverTask = new(token => CalculateBestMacroTask(state, token)); + SolverTask.Start(); } - private void CalculateBestMacroTask(SimulationState state, CancellationToken token) + private int CalculateBestMacroTask(SimulationState state, CancellationToken token) { var config = Service.Configuration.SynthHelperSolverConfig; token.ThrowIfCancellationRequested(); - using (HelperTaskObject = new Solver.Solver(config, state) { Token = token }) - { - HelperTaskObject.OnLog += Log.Debug; - HelperTaskObject.OnNewAction += EnqueueAction; - HelperTaskObject.Start(); - _ = HelperTaskObject.GetTask().GetAwaiter().GetResult(); - } + var solver = new Solver.Solver(config, state) { Token = token }; + solver.OnLog += Log.Debug; + solver.OnNewAction += EnqueueAction; + SolverObject = solver; + solver.Start(); + _ = solver.GetTask().GetAwaiter().GetResult(); token.ThrowIfCancellationRequested(); + + return 0; } private void EnqueueAction(ActionType action) { - if (Macro.Enqueue(action) >= Service.Configuration.SynthHelperStepCount) - HelperTaskTokenSource?.Cancel(); + var newSize = Macro.Enqueue(action, Service.Configuration.SynthHelperMaxDisplayCount); + if (newSize >= Service.Configuration.SynthHelperStepCount || newSize >= Service.Configuration.SynthHelperMaxDisplayCount) + SolverTask?.Cancel(); } private static Sim CreateSim(in SimulationState state) =>