From d0f0d79116b734071e7ffe47d0ecd69b4e1b486f Mon Sep 17 00:00:00 2001 From: Asriel Camora Date: Wed, 31 Jul 2024 13:06:16 -0700 Subject: [PATCH] Read ingredient HQ counts from RecipeNote --- Craftimizer/Plugin.cs | 6 +-- Craftimizer/Windows/MacroEditor.cs | 4 +- Craftimizer/Windows/MacroList.cs | 2 +- Craftimizer/Windows/RecipeNote.cs | 59 +++++++++++++++++++++++++----- Craftimizer/Windows/SynthHelper.cs | 2 +- 5 files changed, 57 insertions(+), 16 deletions(-) diff --git a/Craftimizer/Plugin.cs b/Craftimizer/Plugin.cs index 5226aab..7798f5b 100644 --- a/Craftimizer/Plugin.cs +++ b/Craftimizer/Plugin.cs @@ -104,13 +104,13 @@ public sealed class Plugin : IDalamudPlugin public void OpenEmptyMacroEditor() { var stats = GetDefaultStats(); - OpenMacroEditor(stats.Character, stats.Recipe, stats.Buffs, [], null); + OpenMacroEditor(stats.Character, stats.Recipe, stats.Buffs, null, [], null); } - public void OpenMacroEditor(CharacterStats characterStats, RecipeData recipeData, MacroEditor.CrafterBuffs buffs, IEnumerable actions, Action>? setter) + public void OpenMacroEditor(CharacterStats characterStats, RecipeData recipeData, MacroEditor.CrafterBuffs buffs, IEnumerable? ingredientHqCounts, IEnumerable actions, Action>? setter) { EditorWindow?.Dispose(); - EditorWindow = new(characterStats, recipeData, buffs, actions, setter); + EditorWindow = new(characterStats, recipeData, buffs, ingredientHqCounts, actions, setter); } [Command(name: "/craftaction", description: "Execute the suggested action in the synthesis helper. Can also be run inside a macro. This command is useful for controller players.")] diff --git a/Craftimizer/Windows/MacroEditor.cs b/Craftimizer/Windows/MacroEditor.cs index a1d5b43..0428e98 100644 --- a/Craftimizer/Windows/MacroEditor.cs +++ b/Craftimizer/Windows/MacroEditor.cs @@ -108,7 +108,7 @@ public sealed class MacroEditor : Window, IDisposable private CancellationTokenSource? popupImportUrlTokenSource; private CommunityMacros.CommunityMacro? popupImportUrlMacro; - public MacroEditor(CharacterStats characterStats, RecipeData recipeData, CrafterBuffs buffs, IEnumerable actions, Action>? setter) : base("Craftimizer Macro Editor", WindowFlags) + public MacroEditor(CharacterStats characterStats, RecipeData recipeData, CrafterBuffs buffs, IEnumerable? ingredientHqCounts, IEnumerable actions, Action>? setter) : base("Craftimizer Macro Editor", WindowFlags) { CharacterStats = characterStats; RecipeData = recipeData; @@ -116,7 +116,7 @@ public sealed class MacroEditor : Window, IDisposable MacroSetter = setter; DefaultActions = actions.ToArray(); - HQIngredientCounts = [.. Enumerable.Repeat(0, RecipeData.Ingredients.Count)]; + HQIngredientCounts = [.. ingredientHqCounts ?? Enumerable.Repeat(0, RecipeData.Ingredients.Count)]; RecalculateState(); foreach (var action in DefaultActions) diff --git a/Craftimizer/Windows/MacroList.cs b/Craftimizer/Windows/MacroList.cs index c96bdce..a1a1d08 100644 --- a/Craftimizer/Windows/MacroList.cs +++ b/Craftimizer/Windows/MacroList.cs @@ -352,7 +352,7 @@ public sealed class MacroList : Window, IDisposable private void OpenEditor(Macro? macro) { var stats = Service.Plugin.GetDefaultStats(); - Service.Plugin.OpenMacroEditor(stats.Character, stats.Recipe, stats.Buffs, macro?.Actions ?? Enumerable.Empty(), macro != null ? (actions => { macro.ActionEnumerable = actions; Service.Configuration.Save(); }) : null); + Service.Plugin.OpenMacroEditor(stats.Character, stats.Recipe, stats.Buffs, null, macro?.Actions ?? Enumerable.Empty(), macro != null ? (actions => { macro.ActionEnumerable = actions; Service.Configuration.Save(); }) : null); } private void OnMacroChanged(Macro macro) diff --git a/Craftimizer/Windows/RecipeNote.cs b/Craftimizer/Windows/RecipeNote.cs index 4ddd1e4..f1922ef 100644 --- a/Craftimizer/Windows/RecipeNote.cs +++ b/Craftimizer/Windows/RecipeNote.cs @@ -16,6 +16,7 @@ using Dalamud.Interface.Windowing; using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game.UI; +using FFXIVClientStructs.FFXIV.Client.System.String; using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Client.UI.Misc; using ImGuiNET; @@ -23,8 +24,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Numerics; -using System.Threading; -using System.Threading.Tasks; +using System.Runtime.InteropServices; using ActionType = Craftimizer.Simulator.Actions.ActionType; using ClassJob = Craftimizer.Simulator.ClassJob; using CSRecipeNote = FFXIVClientStructs.FFXIV.Client.Game.UI.RecipeNote; @@ -58,6 +58,7 @@ public sealed unsafe class RecipeNote : Window, IDisposable public AddonRecipeNote* Addon { get; private set; } public RecipeData? RecipeData { get; private set; } public CharacterStats? CharacterStats { get; private set; } + private int StartingQuality { get; set; } public CraftableStatus CraftStatus { get; private set; } private BackgroundTask<(Macro?, SimulationState?)>? SavedMacroTask { get; set; } @@ -238,7 +239,12 @@ public sealed unsafe class RecipeNote : Window, IDisposable StatsChanged = true; } - if (StatsChanged && CraftStatus == CraftableStatus.OK) + var startingQuality = RecipeData.CalculateStartingQuality(CalculateIngredientHqCounts()); + var qualityChanged = startingQuality != StartingQuality; + if (qualityChanged) + StartingQuality = startingQuality; + + if ((StatsChanged || qualityChanged) && CraftStatus == CraftableStatus.OK) { // Stats changed and we are still craftable, so we need to recalculate CalculateSavedMacro(); @@ -267,6 +273,41 @@ public sealed unsafe class RecipeNote : Window, IDisposable return true; } + [StructLayout(LayoutKind.Explicit, Size = 136)] + public struct RecipeIngredient2 + { + [FieldOffset(8)] + public byte NQCount; + + [FieldOffset(9)] + public byte HQCount; + + [FieldOffset(16)] + public Utf8String Name; + + [FieldOffset(120)] + public uint ItemId; + + [FieldOffset(124)] + public uint IconId; + + [FieldOffset(130)] + public byte Amount; + + [FieldOffset(131)] + public byte Flags; + } + + private IEnumerable CalculateIngredientHqCounts() + { + if (RecipeData == null) + throw new InvalidOperationException("RecipeData must not be null"); + + var ingredientCount = RecipeData.Ingredients.Count; + var ingredientSpan = MemoryMarshal.Cast(CSRecipeNote.Instance()->RecipeList->SelectedRecipe->Ingredients); + return ingredientSpan.ToArray().Take(ingredientCount).Select(i => (int)i.HQCount); + } + private Vector2? LastPosition { get; set; } private byte? StyleAlpha { get; set; } private byte? LastAlpha { get; set; } @@ -403,7 +444,7 @@ public sealed unsafe class RecipeNote : Window, IDisposable Service.Plugin.OpenMacroListWindow(); if (ImGui.Button("Open in Macro Editor", new(availWidth, 0))) - Service.Plugin.OpenMacroEditor(CharacterStats!, RecipeData!, new(Service.ClientState.LocalPlayer!.StatusList), [], null); + Service.Plugin.OpenMacroEditor(CharacterStats!, RecipeData!, new(Service.ClientState.LocalPlayer!.StatusList), CalculateIngredientHqCounts(), [], null); } private void DrawCharacterStats() @@ -947,7 +988,7 @@ public sealed unsafe class RecipeNote : Window, IDisposable ImGui.TableNextColumn(); { if (ImGuiUtils.IconButtonSquare(FontAwesomeIcon.Edit, miniRowHeight)) - Service.Plugin.OpenMacroEditor(CharacterStats!, RecipeData!, new(Service.ClientState.LocalPlayer!.StatusList), actions, state.MacroEditorSetter); + Service.Plugin.OpenMacroEditor(CharacterStats!, RecipeData!, new(Service.ClientState.LocalPlayer!.StatusList), CalculateIngredientHqCounts(), actions, state.MacroEditorSetter); if (ImGui.IsItemHovered()) ImGuiUtils.Tooltip("Open in Macro Editor"); if (ImGuiUtils.IconButtonSquare(FontAwesomeIcon.Paste, miniRowHeight)) @@ -1028,7 +1069,7 @@ public sealed unsafe class RecipeNote : Window, IDisposable if (PlayerState.Instance()->CurrentClassJobId != RecipeData.ClassJob.GetClassJobIndex()) return CraftableStatus.WrongClassJob; - if (RecipeData.Recipe.IsSpecializationRequired && !(CharacterStats!.IsSpecialist)) + if (RecipeData.Recipe.IsSpecializationRequired && !CharacterStats!.IsSpecialist) return CraftableStatus.SpecialistRequired; var itemRequired = RecipeData.Recipe.ItemRequired; @@ -1101,7 +1142,7 @@ public sealed unsafe class RecipeNote : Window, IDisposable SavedMacroTask?.Cancel(); SavedMacroTask = new(token => { - var input = new SimulationInput(CharacterStats!, RecipeData!.RecipeInfo); + var input = new SimulationInput(CharacterStats!, RecipeData!.RecipeInfo, StartingQuality); var state = new SimulationState(input); var config = Service.Configuration.RecipeNoteSolverConfig; var mctsConfig = new MCTSConfig(config); @@ -1132,7 +1173,7 @@ public sealed unsafe class RecipeNote : Window, IDisposable SuggestedMacroTask?.Cancel(); SuggestedMacroTask = new(token => { - var input = new SimulationInput(CharacterStats!, RecipeData!.RecipeInfo); + var input = new SimulationInput(CharacterStats!, RecipeData!.RecipeInfo, StartingQuality); var state = new SimulationState(input); var config = Service.Configuration.RecipeNoteSolverConfig; @@ -1156,7 +1197,7 @@ public sealed unsafe class RecipeNote : Window, IDisposable CommunityMacroTask?.Cancel(); CommunityMacroTask = new(token => { - var input = new SimulationInput(CharacterStats!, RecipeData!.RecipeInfo); + var input = new SimulationInput(CharacterStats!, RecipeData!.RecipeInfo, StartingQuality); var state = new SimulationState(input); var config = Service.Configuration.RecipeNoteSolverConfig; var mctsConfig = new MCTSConfig(config); diff --git a/Craftimizer/Windows/SynthHelper.cs b/Craftimizer/Windows/SynthHelper.cs index b90ec95..02e2bdd 100644 --- a/Craftimizer/Windows/SynthHelper.cs +++ b/Craftimizer/Windows/SynthHelper.cs @@ -459,7 +459,7 @@ public sealed unsafe class SynthHelper : Window, IDisposable } if (ImGui.Button("Open in Macro Editor", new(-1, 0))) - Service.Plugin.OpenMacroEditor(CharacterStats!, RecipeData!, new(Service.ClientState.LocalPlayer!.StatusList), [], null); + Service.Plugin.OpenMacroEditor(CharacterStats!, RecipeData!, new(Service.ClientState.LocalPlayer!.StatusList), null, [], null); } public bool ExecuteNextAction()