diff --git a/Craftimizer/Plugin.cs b/Craftimizer/Plugin.cs index 60c9a44..cbb5534 100644 --- a/Craftimizer/Plugin.cs +++ b/Craftimizer/Plugin.cs @@ -25,7 +25,6 @@ public sealed class Plugin : IDalamudPlugin public Settings SettingsWindow { get; } public Craftimizer.Windows.RecipeNote RecipeNoteWindow { get; } public Craft SynthesisWindow { get; } - public Windows.Simulator? SimulatorWindow { get; set; } public Configuration Configuration { get; } public Hooks Hooks { get; } @@ -56,16 +55,6 @@ public sealed class Plugin : IDalamudPlugin Service.PluginInterface.UiBuilder.OpenConfigUi += OpenSettingsWindow; } - public void OpenSimulatorWindow(Item item, bool isExpert, SimulationInput input, ClassJob classJob, Macro? macro) - { - if (SimulatorWindow != null) - { - SimulatorWindow.IsOpen = false; - WindowSystem.RemoveWindow(SimulatorWindow); - } - SimulatorWindow = new(item, isExpert, input, classJob, macro); - } - public void OpenSettingsWindow() { SettingsWindow.IsOpen = true; diff --git a/Craftimizer/Windows/CraftingLog.cs b/Craftimizer/Windows/CraftingLog.cs deleted file mode 100644 index 09e0533..0000000 --- a/Craftimizer/Windows/CraftingLog.cs +++ /dev/null @@ -1,578 +0,0 @@ -using Craftimizer.Plugin.Utils; -using Craftimizer.Simulator; -using Craftimizer.Simulator.Actions; -using Dalamud; -using Dalamud.Interface; -using Dalamud.Interface.Components; -using Dalamud.Interface.Windowing; -using Dalamud.Utility; -using FFXIVClientStructs.FFXIV.Client.Game; -using FFXIVClientStructs.FFXIV.Client.UI.Misc; -using FFXIVClientStructs.FFXIV.Component.GUI; -using ImGuiNET; -using Lumina.Excel.GeneratedSheets; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using System.Runtime.InteropServices; -using System.Text; -using ActionType = Craftimizer.Simulator.Actions.ActionType; -using RecipeNote = Craftimizer.Utils.RecipeNote; - -namespace Craftimizer.Plugin.Windows; - -public unsafe class CraftingLog : Window -{ - private const ImGuiWindowFlags WindowFlags = ImGuiWindowFlags.NoDecoration - | ImGuiWindowFlags.AlwaysAutoResize - | ImGuiWindowFlags.NoSavedSettings - | ImGuiWindowFlags.NoFocusOnAppearing - | ImGuiWindowFlags.NoNavFocus; - - private static Configuration Config => Service.Configuration; - - private const int LeftSideWidth = 350; - - // If relative, increase stat by Value's % (rounded down), and cap increase to Max - // If not relative, increase stat by Value, and ignore Max - [StructLayout(LayoutKind.Auto)] - private record struct FoodStat(bool IsRelative, sbyte Value, short Max, sbyte ValueHQ, short MaxHQ); - private sealed record Food(Item Item, string Name, string NameHQ, FoodStat? Craftsmanship, FoodStat? Control, FoodStat? CP); - - private static Food[] FoodItems { get; } - private static Food[] MedicineItems { get; } - private static Random Random { get; } - - private static RecipeNote RecipeUtils => Service.Plugin.RecipeNote; - private ushort OldRecipeId { get; set; } - - // Set in CalculateCharacterStats (in PreDraw) - private Gearsets.GearsetItem[] CharacterEquipment { get; set; } = null!; - private CharacterStats CharacterStatsNoConsumable { get; set; } = null!; - private Gearsets.GearsetStats CharacterConsumableBonus { get; set; } - private CharacterStats CharacterStatsConsumable { get; set; } = null!; - private CannotCraftReason CharacterCannotCraftReason { get; set; } - private SimulationInput CharacterSimulationInput { get; set; } = null!; - - // Set in UI - private int QualityNotches { get; set; } - private int StartingQuality => - RecipeUtils.HQIngredientCount == 0 ? - 0 : - (int)((float)QualityNotches * RecipeUtils.MaxStartingQuality / RecipeUtils.HQIngredientCount); - - private Food? SelectedFood { get; set; } - private bool SelectedFoodHQ { get; set; } - - private Food? SelectedMedicine { get; set; } - private bool SelectedMedicineHQ { get; set; } - - static CraftingLog() - { - var foods = new List(); - var medicines = new List(); - foreach (var item in LuminaSheets.ItemSheet) - { - var isFood = item.ItemUICategory.Row == 46; - var isMedicine = item.ItemUICategory.Row == 44; - if (!isFood && !isMedicine) - continue; - - if (item.ItemAction.Value == null) - continue; - - if (!(item.ItemAction.Value.Type is 844 or 845 or 846)) - continue; - - var itemFood = LuminaSheets.ItemFoodSheet.GetRow(item.ItemAction.Value.Data[1]); - if (itemFood == null) - continue; - - FoodStat? craftsmanship = null, control = null, cp = null; - foreach (var stat in itemFood.UnkData1) - { - if (stat.BaseParam == 0) - continue; - var foodStat = new FoodStat(stat.IsRelative, stat.Value, stat.Max, stat.ValueHQ, stat.MaxHQ); - switch (stat.BaseParam) - { - case Gearsets.ParamCraftsmanship: craftsmanship = foodStat; break; - case Gearsets.ParamControl: control = foodStat; break; - case Gearsets.ParamCP: cp = foodStat; break; - default: continue; - } - } - - if (craftsmanship != null || control != null || cp != null) - { - var name = item.Name.ToDalamudString().TextValue ?? $"Unknown ({item.RowId})"; - var food = new Food(item, name, $"{name} (HQ)", craftsmanship, control, cp); - if (isFood) - foods.Add(food); - if (isMedicine) - medicines.Add(food); - } - } - foods.Sort((a, b) => b.Item.LevelItem.Row.CompareTo(a.Item.LevelItem.Row)); - medicines.Sort((a, b) => b.Item.LevelItem.Row.CompareTo(a.Item.LevelItem.Row)); - FoodItems = foods.ToArray(); - MedicineItems = medicines.ToArray(); - - Random = new(); - } - - public CraftingLog() : base("Craftimizer RecipeNoteHelper", WindowFlags, true) - { - Service.WindowSystem.AddWindow(this); - - IsOpen = true; - } - - private void CalculateCharacterStats() - { - var container = InventoryManager.Instance()->GetInventoryContainer(InventoryType.EquippedItems); - if (container == null) - return; - - CharacterEquipment = Gearsets.GetGearsetItems(container); - CharacterStatsNoConsumable = Gearsets.CalculateCharacterStats(CharacterEquipment, RecipeUtils.CharacterLevel, RecipeUtils.CanUseManipulation); - CharacterConsumableBonus = CalculateConsumableBonus(CharacterStatsNoConsumable); - CharacterStatsConsumable = CharacterStatsNoConsumable with - { - Craftsmanship = CharacterStatsNoConsumable.Craftsmanship + CharacterConsumableBonus.Craftsmanship, - Control = CharacterStatsNoConsumable.Control + CharacterConsumableBonus.Control, - CP = CharacterStatsNoConsumable.CP + CharacterConsumableBonus.CP, - }; - CharacterCannotCraftReason = Config.OverrideUncraftability ? CannotCraftReason.OK : CanCraftRecipe(CharacterEquipment, CharacterStatsConsumable); - - CharacterSimulationInput = new(CharacterStatsConsumable, RecipeUtils.Info, StartingQuality, Random); - } - - public override void Draw() - { - ImGui.BeginTable("craftlog", 2, ImGuiTableFlags.BordersInnerV); - - ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed, LeftSideWidth); - ImGui.TableNextColumn(); - DrawCraftInfo(); - - ImGui.TableNextColumn(); - DrawGearsets(); - - ImGui.EndTable(); - } - - private void DrawCraftInfo() - { - ImGui.BeginTable("craftinfo", 2, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.SizingStretchSame); - - ImGui.TableNextColumn(); - DrawRecipeInfo(); - - ImGui.TableNextColumn(); - DrawCharacterInfo(); - ImGui.EndTable(); - - ImGui.Separator(); - - DrawCraftParameters(); - DrawMacros(); - } - - private void DrawRecipeInfo() - { - var s = new StringBuilder(); - s.AppendLine($"{RecipeUtils.ClassJob.GetName()} {new string('★', RecipeUtils.Table.Stars)}"); - s.AppendLine($"Level {RecipeUtils.Table.ClassJobLevel} (RLvl {RecipeUtils.Info.RLvl})"); - s.AppendLine($"Durability: {RecipeUtils.Info.MaxDurability}"); - s.AppendLine($"Progress: {RecipeUtils.Info.MaxProgress}"); - s.AppendLine($"Quality: {RecipeUtils.Info.MaxQuality}"); - ImGui.Text(s.ToString()); - } - - private void DrawCharacterInfo() - { - if (CharacterCannotCraftReason != CannotCraftReason.OK) - { - ImGui.TextWrapped(GetCannotCraftReasonText(CharacterCannotCraftReason)); - return; - } - - ImGui.Text(GetCharacterStatsText(CharacterStatsConsumable)); - } - - private void DrawCraftParameters() - { - ImGui.BeginDisabled(RecipeUtils.HQIngredientCount == 0); - var qualityNotches = QualityNotches; - ImGui.SetNextItemWidth(LeftSideWidth - 115); - if (ImGui.SliderInt("Starting Quality", ref qualityNotches, 0, RecipeUtils.HQIngredientCount, StartingQuality.ToString(), ImGuiSliderFlags.NoInput | ImGuiSliderFlags.AlwaysClamp)) - QualityNotches = qualityNotches; - ImGui.EndDisabled(); - - ImGui.BeginTable("craftfood", 2, ImGuiTableFlags.BordersInnerV); - - ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed, LeftSideWidth - 120); - ImGui.TableNextColumn(); - - if (ImGui.BeginCombo("Food", SelectedFood != null ? (SelectedFoodHQ ? SelectedFood.NameHQ : SelectedFood.Name) : "None")) - { - if (ImGui.Selectable("None", SelectedFood == null)) - { - SelectedFood = null; - SelectedFoodHQ = false; - } - - foreach (var food in FoodItems) - { - if (ImGui.Selectable(food.Name, food == SelectedFood && !SelectedFoodHQ)) - { - SelectedFood = food; - SelectedFoodHQ = false; - } - else if (ImGui.Selectable($"{food.Name} (HQ)", food == SelectedFood && SelectedFoodHQ)) - { - SelectedFood = food; - SelectedFoodHQ = true; - } - } - - ImGui.EndCombo(); - } - - if (ImGui.BeginCombo("Medicine", SelectedMedicine != null ? (SelectedMedicineHQ ? SelectedMedicine.NameHQ : SelectedMedicine.Name) : "None")) - { - if (ImGui.Selectable("None", SelectedMedicine == null)) - { - SelectedMedicine = null; - SelectedMedicineHQ = false; - } - - - foreach (var food in MedicineItems) - { - if (ImGui.Selectable(food.Name, food == SelectedMedicine && !SelectedMedicineHQ)) - { - SelectedMedicine = food; - SelectedMedicineHQ = false; - } - else if (ImGui.Selectable($"{food.Name} (HQ)", food == SelectedMedicine && SelectedMedicineHQ)) - { - SelectedMedicine = food; - SelectedMedicineHQ = true; - } - } - - ImGui.EndCombo(); - } - - ImGui.TableNextColumn(); - - var s = new StringBuilder(); - s.AppendLine($"+{CharacterConsumableBonus.Craftsmanship} Craftsmanship"); - s.AppendLine($"+{CharacterConsumableBonus.Control} Control"); - s.AppendLine($"+{CharacterConsumableBonus.CP} CP"); - ImGui.Text(s.ToString()); - - ImGui.EndTable(); - } - - private void DrawMacros() - { - var padding = ImGui.GetStyle().FramePadding; - - var fontSize = ImGui.GetFontSize(); - var height = fontSize + (padding.Y * 2); - var width = ImGui.GetContentRegionAvail().X; - var size = new Vector2(width, height); - var infoColWidth = Simulator.TooltipProgressBarSize.X; - var infoButtonCount = 3; - var infoButtonWidth = (infoColWidth - ImGui.GetStyle().ItemSpacing.X * (infoButtonCount - 1)) / infoButtonCount; - var infoButtonSize = new Vector2(infoButtonWidth, height); - var actionColWidth = width - infoColWidth - ImGui.GetStyle().FramePadding.X * 2; - var actionCount = 6; - var actionSize = new Vector2((actionColWidth - (ImGui.GetStyle().ItemSpacing.X * (actionCount - 1))) / actionCount); - - if (ImGui.Button("Open Simulator", size)) - OpenSimulatorWindow(null); - ImGui.SameLine(); - ImGui.Button("Generate a new macro", size); - - ImGui.BeginTable("macrotable", 2, ImGuiTableFlags.BordersInner); - ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed, infoColWidth); - ImGui.TableSetupColumn(""); - var simulation = new SimulatorNoRandom(new(CharacterSimulationInput)); - for (var i = 0; i < Config.Macros.Count; ++i) - { - var macro = Config.Macros[i]; - ImGui.PushID(i); - ImGui.TableNextRow(); - - SimulationState? state = null; - if (CharacterCannotCraftReason == CannotCraftReason.OK) - { - state = new(CharacterSimulationInput); - foreach (var action in macro.Actions) - (_, state) = simulation.Execute(state.Value, action); - } - - ImGui.TableNextColumn(); - ImGui.TextWrapped(macro.Name); - if (state.HasValue) - Simulator.DrawAllProgressTooltips(state!.Value); - - if (ImGuiUtils.IconButtonSized(FontAwesomeIcon.Copy, infoButtonSize)) - CopyMacroToClipboard(macro); - if (ImGui.IsItemHovered()) - ImGui.SetTooltip("Copy macro to clipboard\nHold Shift to exclude wait modifiers"); - ImGui.SameLine(); - if (ImGuiUtils.IconButtonSized(FontAwesomeIcon.ShareSquare, infoButtonSize)) - OpenSimulatorWindow(macro); - if (ImGui.IsItemHovered()) - ImGui.SetTooltip("Open macro in simulator"); - ImGui.SameLine(); - if (ImGuiUtils.IconButtonSized(FontAwesomeIcon.Trash, infoButtonSize)) - Config.Macros.RemoveAt(i); - if (ImGui.IsItemHovered()) - ImGui.SetTooltip("Delete macro"); - - ImGui.TableNextColumn(); - var j = 0; - foreach (var action in macro.Actions) - { - ImGui.Image(action.GetIcon(RecipeUtils.ClassJob).ImGuiHandle, actionSize); - if (j++ % actionCount != actionCount - 1) - ImGui.SameLine(); - if (j == actionCount * 2) - break; - } - ImGui.Dummy(Vector2.Zero); - ImGui.PopID(); - } - ImGui.EndTable(); - } - - private void OpenSimulatorWindow(Macro? macro) - { - Service.Plugin.OpenSimulatorWindow(RecipeUtils.Recipe.ItemResult.Value!, RecipeUtils.Recipe.IsExpert, CharacterSimulationInput, RecipeUtils.ClassJob, macro); - } - - private string GetMacroCommand(ActionType action, bool addWaitTimes) - { - var actionBase = action.Base(); - if (actionBase is BaseComboAction comboActionBase) - return $"{GetMacroCommand(comboActionBase.ActionTypeA, addWaitTimes)}\n{GetMacroCommand(comboActionBase.ActionTypeB, addWaitTimes)}"; - if (addWaitTimes) - return $"/ac \"{action.GetName(RecipeUtils.ClassJob)}\" "; - else - return $"/ac \"{action.GetName(RecipeUtils.ClassJob)}\""; - } - - private void CopyMacroToClipboard(Macro macro) - { - var s = new StringBuilder(); - if (ImGui.IsKeyDown(ImGuiKey.ModShift)) - { - foreach (var action in macro.Actions) - s.AppendLine(GetMacroCommand(action, false)); - } - else - { - foreach (var action in macro.Actions) - s.AppendLine(GetMacroCommand(action, true)); - s.AppendLine($"/echo Macro Complete! "); - } - ImGui.SetClipboardText(s.ToString()); - } - - private void DrawGearsets() - { - ImGui.Text("Available Gearsets"); - - var inst = RaptureGearsetModule.Instance(); - - for (var i = 0; i < 100; i++) - { - var gearset = inst->EntriesSpan[i]; - if (gearset.ID != i) - continue; - if (!gearset.Flags.HasFlag(RaptureGearsetModule.GearsetFlag.Exists)) - continue; - - if (ClassJobUtils.GetClassJobFromIdx(gearset.ClassJob) != RecipeUtils.ClassJob) - continue; - - var items = Gearsets.GetGearsetItems(&gearset); - var stats = Gearsets.CalculateCharacterStats(items, RecipeUtils.CharacterLevel, RecipeUtils.CanUseManipulation); - var gearsetId = gearset.ID + 1; - - ImGuiUtils.BeginGroupPanel($"{SafeMemory.ReadString((nint)gearset.Name, 47)} ({gearsetId})"); - ImGui.Text(GetCharacterStatsText(stats)); - ImGui.SameLine(); - if (ImGuiComponents.IconButton($"SwapGearset{gearsetId}", FontAwesomeIcon.SyncAlt)) - Chat.SendMessage($"/gearset change {gearsetId}"); - if (ImGui.IsItemHovered()) - ImGui.SetTooltip($"Swap to gearset {gearsetId}"); - ImGuiUtils.EndGroupPanel(); - } - } - - public override bool DrawConditions() - { - if (!RecipeUtils.HasValidRecipe) - return false; - - if (OldRecipeId != RecipeUtils.RecipeId) - QualityNotches = 0; - OldRecipeId = RecipeUtils.RecipeId; - - if (RecipeUtils.AddonRecipe == null) - return false; - - // Check if RecipeNote addon is visible - if (RecipeUtils.AddonRecipe->AtkUnitBase.WindowNode == null) - return false; - - // Check if RecipeNote has a visible selected recipe - if (!RecipeUtils.AddonRecipe->Unk258->IsVisible) - return false; - - return base.DrawConditions(); - } - - public override unsafe void PreDraw() - { - var addon = RecipeUtils.AddonRecipe; - ref var unit = ref addon->AtkUnitBase; - var scale = unit.Scale; - var pos = new Vector2(unit.X, unit.Y); - var size = new Vector2(unit.WindowNode->AtkResNode.Width, unit.WindowNode->AtkResNode.Height) * scale; - - var node = (AtkResNode*)addon->Unk458; // unit.GetNodeById(59); - var nodeParent = addon->Unk258; // unit.GetNodeById(57); - - Position = pos + new Vector2(size.X, (nodeParent->Y + node->Y) * scale); - SizeConstraints = new WindowSizeConstraints - { - MinimumSize = new(-1), - MaximumSize = new(10000, 10000) - }; - - CalculateCharacterStats(); - - base.PreDraw(); - } - - private Gearsets.GearsetStats CalculateConsumableBonus(CharacterStats stats) - { - static int CalculateBonus(int param, bool isHq, FoodStat? stat) - { - if (stat == null) - return 0; - - var foodStat = stat.Value; - var (value, max) = isHq ? (foodStat.ValueHQ, foodStat.MaxHQ) : (foodStat.Value, foodStat.Max); - - if (!foodStat.IsRelative) - return value; - - return Math.Min((int)MathF.Floor((float)value * param), max); - } - - Gearsets.GearsetStats ret = new(); - - if (SelectedFood != null) - { - ret.CP += CalculateBonus(stats.CP, SelectedFoodHQ, SelectedFood.CP); - ret.Craftsmanship += CalculateBonus(stats.Craftsmanship, SelectedFoodHQ, SelectedFood.Craftsmanship); - ret.Control += CalculateBonus(stats.Control, SelectedFoodHQ, SelectedFood.Control); - } - - if (SelectedMedicine != null) - { - ret.CP += CalculateBonus(stats.CP, SelectedMedicineHQ, SelectedMedicine.CP); - ret.Craftsmanship += CalculateBonus(stats.Craftsmanship, SelectedMedicineHQ, SelectedMedicine.Craftsmanship); - ret.Control += CalculateBonus(stats.Control, SelectedMedicineHQ, SelectedMedicine.Control); - } - - return ret; - } - - private enum CannotCraftReason - { - OK, - WrongClassJob, - SpecialistRequired, - RequiredItem, - RequiredStatus, - CraftsmanshipTooLow, - ControlTooLow, - } - - private CannotCraftReason CanCraftRecipe(Gearsets.GearsetItem[] items, CharacterStats stats) - { - if (ClassJobUtils.GetClassJobFromIdx((byte)Service.ClientState.LocalPlayer!.ClassJob.Id) != RecipeUtils.ClassJob) - return CannotCraftReason.WrongClassJob; - - var recipe = RecipeUtils.Recipe; - - if (recipe.IsSpecializationRequired && !stats.IsSpecialist) - return CannotCraftReason.SpecialistRequired; - - if (recipe.ItemRequired.Row != 0) - { - if (recipe.ItemRequired.Value != null) - { - if (!items.Any(i => Gearsets.IsItem(i, recipe.ItemRequired.Row))) - { - return CannotCraftReason.RequiredItem; - } - } - } - - if (recipe.StatusRequired.Row != 0) - { - if (recipe.StatusRequired.Value != null) - { - if (!Service.ClientState.LocalPlayer.StatusList.Any(s => s.StatusId == recipe.StatusRequired.Row)) - return CannotCraftReason.RequiredStatus; - } - } - - if (recipe.RequiredCraftsmanship > stats.Craftsmanship) - return CannotCraftReason.CraftsmanshipTooLow; - - if (recipe.RequiredControl > stats.Control) - return CannotCraftReason.ControlTooLow; - - return CannotCraftReason.OK; - } - - private static string GetCannotCraftReasonText(CannotCraftReason reason) => - reason switch - { - CannotCraftReason.OK => "You can craft this recipe.", - CannotCraftReason.WrongClassJob => "Your current class cannot craft this recipe.", - CannotCraftReason.SpecialistRequired => "You must be a specialist to craft this recipe.", - CannotCraftReason.RequiredItem => "You do not have the required item to craft this recipe.", - CannotCraftReason.RequiredStatus => "You do not have the required status effect to craft this recipe.", - CannotCraftReason.CraftsmanshipTooLow => "Your craftsmanship is too low to craft this recipe.", - CannotCraftReason.ControlTooLow => "Your control is too low to craft this recipe.", - _ => "Unknown reason.", - }; - - private static string GetCharacterStatsText(CharacterStats stats) - { - var s = new StringBuilder(); - s.AppendLine($"Level {stats.Level} (CLvl {stats.CLvl})"); - s.AppendLine($"Craftsmanship {stats.Craftsmanship}"); - s.AppendLine($"Control {stats.Control}"); - s.AppendLine($"CP {stats.CP}"); - if (stats.IsSpecialist) - s.AppendLine($" + Specialist"); - if (stats.HasSplendorousBuff) - s.AppendLine($" + Splendorous Tool"); - return s.ToString(); - } -} diff --git a/Craftimizer/Windows/RecipeNote.cs b/Craftimizer/Windows/RecipeNote.cs index 02ab6d4..185c489 100644 --- a/Craftimizer/Windows/RecipeNote.cs +++ b/Craftimizer/Windows/RecipeNote.cs @@ -68,7 +68,7 @@ public sealed unsafe class RecipeNote : Window, IDisposable private IDalamudTextureWrap NoManipulationBadge { get; } private GameFontHandle AxisFont { get; } - public RecipeNote() : base("Craftimizer RecipeNode", WindowFlags, false) + public RecipeNote() : base("Craftimizer RecipeNote", WindowFlags, false) { ExpertBadge = Service.IconManager.GetAssemblyTexture("Graphics.expert_badge.png"); CollectibleBadge = Service.IconManager.GetAssemblyTexture("Graphics.collectible_badge.png"); diff --git a/Craftimizer/Windows/Simulator.cs b/Craftimizer/Windows/Simulator.cs deleted file mode 100644 index 11f81e8..0000000 --- a/Craftimizer/Windows/Simulator.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Craftimizer.Simulator; -using Craftimizer.Simulator.Actions; -using Dalamud.Interface.Windowing; -using ImGuiNET; -using Lumina.Excel.GeneratedSheets; -using System; -using System.Collections.Generic; -using ClassJob = Craftimizer.Simulator.ClassJob; - -namespace Craftimizer.Plugin.Windows; - -public sealed partial class Simulator : Window, IDisposable -{ - private const ImGuiWindowFlags WindowFlags = ImGuiWindowFlags.AlwaysAutoResize; - - private static Configuration Config => Service.Configuration; - - private Item Item { get; } - private bool IsExpert { get; } - private SimulationInput Input { get; } - private ClassJob ClassJob { get; } - private Macro? Macro { get; set; } - private string MacroName { get; set; } - // State is the state of the simulation *after* its corresponding action is executed. - private List<(ActionType Action, string Tooltip, ActionResponse Response, SimulationState State)> Actions { get; } - private Craftimizer.Simulator.Simulator Sim { get; set; } - - private SimulationState LatestState => Actions.Count == 0 ? new(Input) : Actions[^1].State; - - // Simulator is set by ResetSimulator() -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public Simulator(Item item, bool isExpert, SimulationInput input, ClassJob classJob, Macro? macro) : base("Craftimizer Simulator", WindowFlags) -#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - { - Service.WindowSystem.AddWindow(this); - - Item = item; - IsExpert = isExpert; - Input = input; - ClassJob = classJob; - Macro = macro; - MacroName = Macro?.Name ?? $"Macro {Config.Macros.Count + 1}"; - Actions = new(); - ResetSimulator(); - - IsOpen = true; - - CollapsedCondition = ImGuiCond.Appearing; - Collapsed = false; - - SizeCondition = ImGuiCond.Appearing; - Size = SizeConstraints?.MinimumSize ?? new(10); - - if (Macro != null) - foreach (var action in Macro.Actions) - AppendAction(action); - } - - private void ResetSimulator() - { - Sim = Config.CreateSimulator(LatestState); - ReexecuteAllActions(); - } -} diff --git a/Craftimizer/Windows/SimulatorActions.cs b/Craftimizer/Windows/SimulatorActions.cs deleted file mode 100644 index 604a2e7..0000000 --- a/Craftimizer/Windows/SimulatorActions.cs +++ /dev/null @@ -1,89 +0,0 @@ -using Craftimizer.Simulator.Actions; -using Dalamud.Interface.Windowing; -using System; - -namespace Craftimizer.Plugin.Windows; - -public sealed partial class Simulator : Window, IDisposable -{ - private void AppendAction(ActionType action) - { - OnActionsChanged(); - - AppendGeneratedAction(action); - } - - private void AppendGeneratedAction(ActionType action) - { - var actionBase = action.Base(); - if (actionBase is BaseComboAction comboActionBase) - { - AppendGeneratedAction(comboActionBase.ActionTypeA); - AppendGeneratedAction(comboActionBase.ActionTypeB); - } - else - { - var tooltip = actionBase.GetTooltip(Sim, false); - var (response, state) = Sim.Execute(LatestState, action); - Actions.Add((action, tooltip, response, state)); - } - } - - private void RemoveAction(int actionIndex) - { - OnActionsChanged(); - - // Remove action - Actions.RemoveAt(actionIndex); - - // Take note of all actions afterwards - Span succeedingActions = stackalloc ActionType[Actions.Count - actionIndex]; - for (var i = 0; i < succeedingActions.Length; i++) - succeedingActions[i] = Actions[i + actionIndex].Action; - - // Remove all future actions - Actions.RemoveRange(actionIndex, succeedingActions.Length); - - // Re-execute all future actions - foreach (var action in succeedingActions) - AppendAction(action); - } - - private void InsertAction(int actionIndex, ActionType action) - { - OnActionsChanged(); - - // Take note of all actions afterwards - Span succeedingActions = stackalloc ActionType[Actions.Count - actionIndex]; - for (var i = 0; i < succeedingActions.Length; i++) - succeedingActions[i] = Actions[i + actionIndex].Action; - - // Remove all future actions - Actions.RemoveRange(actionIndex, succeedingActions.Length); - - // Execute new action - AppendAction(action); - - // Re-execute all future actions - foreach (var succeededAction in succeedingActions) - AppendAction(succeededAction); - } - - private void ClearAllActions() - { - OnActionsChanged(); - - Actions.Clear(); - } - - private void ReexecuteAllActions() - { - Span actions = stackalloc ActionType[Actions.Count]; - for (var i = 0; i < actions.Length; i++) - actions[i] = Actions[i].Action; - - Actions.Clear(); - foreach (var action in actions) - AppendAction(action); - } -} diff --git a/Craftimizer/Windows/SimulatorDrawer.cs b/Craftimizer/Windows/SimulatorDrawer.cs deleted file mode 100644 index 615eadc..0000000 --- a/Craftimizer/Windows/SimulatorDrawer.cs +++ /dev/null @@ -1,440 +0,0 @@ -using Craftimizer.Simulator; -using Craftimizer.Simulator.Actions; -using Dalamud.Game.Text; -using Dalamud.Interface; -using Dalamud.Interface.Components; -using Dalamud.Interface.Internal; -using Dalamud.Interface.Utility; -using Dalamud.Interface.Windowing; -using Dalamud.Utility; -using ImGuiNET; -using System; -using System.Linq; -using System.Numerics; - -namespace Craftimizer.Plugin.Windows; - -public sealed partial class Simulator : Window, IDisposable -{ - private const int ActionColumnSize = 260; - - private static readonly Vector2 ProgressBarSize = new(200, 20); - private static readonly Vector2 DurabilityBarSize = new(100, 20); - private static readonly Vector2 ConditionBarSize = new(20, 20); - private static readonly Vector2 ProgressBarSizeOld = new(200, 20); - public static readonly Vector2 TooltipProgressBarSize = new(100, 5); - - public static readonly Vector4 ProgressColor = new(0.44f, 0.65f, 0.18f, 1f); - public static readonly Vector4 QualityColor = new(0.26f, 0.71f, 0.69f, 1f); - public static readonly Vector4 DurabilityColor = new(0.13f, 0.52f, 0.93f, 1f); - public static readonly Vector4 HQColor = new(0.592f, 0.863f, 0.376f, 1f); - public static readonly Vector4 CPColor = new(0.63f, 0.37f, 0.75f, 1f); - - private static readonly Vector4 BadActionImageTint = new(1f, .5f, .5f, 1f); - private static readonly Vector4 BadActionImageColor = new(1f, .3f, .3f, 1f); - - private static readonly Vector4 BadActionTextColor = new(1f, .2f, .2f, 1f); - - private static readonly (ActionCategory Category, ActionType[] Actions)[] SortedActions; - - static Simulator() - { - SortedActions = Enum.GetValues() - .Where(a => a.Category() != ActionCategory.Combo) - .GroupBy(a => a.Category()) - .Select(g => (g.Key, g.OrderBy(a => a.Level()).ToArray())) - .ToArray(); - } - - public override void Draw() - { - while (SolverActionQueue.TryDequeue(out var poppedAction)) - AppendGeneratedAction(poppedAction); - - ImGui.BeginTable("simulatorWindow", 2, ImGuiTableFlags.BordersInnerV); - ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed, ActionColumnSize); - ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthStretch); - ImGui.TableNextColumn(); - DrawActions(); - ImGui.TableNextColumn(); - DrawSimulation(); - ImGui.EndTable(); - } - - private void DrawActions() - { - var hideUnlearnedActions = Config.HideUnlearnedActions; - if (ImGui.Checkbox("Show only learned actions", ref hideUnlearnedActions)) - { - Config.HideUnlearnedActions = hideUnlearnedActions; - Config.Save(); - } - - Sim.SetState(LatestState); - - var actionSize = new Vector2((ActionColumnSize / 5) - ImGui.GetStyle().ItemSpacing.X * (6f / 5)); - ImGui.PushStyleColor(ImGuiCol.Button, Vector4.Zero); - ImGui.PushStyleColor(ImGuiCol.ButtonActive, Vector4.Zero); - ImGui.PushStyleColor(ImGuiCol.ButtonHovered, Vector4.Zero); - ImGui.BeginDisabled(!CanModifyActions); - - foreach (var (category, actions) in SortedActions) - { - var i = 0; - ImGuiUtils.BeginGroupPanel(category.GetDisplayName(), ActionColumnSize); - foreach (var action in actions) - { - var baseAction = action.Base(); - - var cannotUse = action.Level() > Input.Stats.Level || (action == ActionType.Manipulation && !Input.Stats.CanUseManipulation); - if (cannotUse && Config.HideUnlearnedActions) - continue; - - var shouldNotUse = !baseAction.CanUse(Sim) || Sim.IsComplete; - - ImGui.BeginDisabled(cannotUse); - - if (ImGui.ImageButton(action.GetIcon(ClassJob).ImGuiHandle, actionSize, Vector2.Zero, Vector2.One, 0, default, shouldNotUse ? BadActionImageTint : Vector4.One)) - AppendAction(action); - - if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) - ImGui.SetTooltip($"{action.GetName(ClassJob)}\n{baseAction.GetTooltip(Sim, true)}"); - - ImGui.EndDisabled(); - - if (++i % 5 != 0) - ImGui.SameLine(); - } - if (i == 0) - ImGui.Dummy(actionSize); - ImGuiUtils.EndGroupPanel(); - } - - ImGui.EndDisabled(); - ImGui.PopStyleColor(3); - } - - private void DrawSimulation() - { - var drawParams = CalculateSynthDrawParams(); - - DrawSimulationHeader(); - DrawSimulationBars(drawParams); - ImGuiHelpers.ScaledDummy(5); - DrawSimulationEffects(drawParams); - ImGuiHelpers.ScaledDummy(5); - DrawSimulationActions(drawParams); - var bottom = ImGui.GetContentRegionAvail().Y - ImGui.GetStyle().FramePadding.Y * 2; - var buttonHeight = ImGui.GetFrameHeightWithSpacing() * 2 + ImGui.GetFrameHeight(); - ImGuiHelpers.ScaledDummy(bottom - buttonHeight); - DrawSimulationButtons(drawParams); - } - - private void DrawSimulationHeader() - { - var imageSize = new Vector2(ImGui.GetFontSize() * 2.25f); - - ImGui.Image(Service.IconManager.GetIcon(Item.Icon).ImGuiHandle, imageSize); - ImGui.SameLine(); - ImGui.SetCursorPosY(ImGui.GetCursorPosY() + (imageSize.Y - ImGui.GetFontSize()) / 2f); - ImGui.TextUnformatted(Item.Name.ToDalamudString().ToString()); - if (Item.IsCollectable) - { - ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X); - ImGui.SetCursorPosY(ImGui.GetCursorPosY() + (imageSize.Y - ImGui.GetFontSize()) / 2f); - ImGui.TextColored(new(0.98f, 0.98f, 0.61f, 1), SeIconChar.Collectible.ToIconString()); - if (ImGui.IsItemHovered()) - ImGui.SetTooltip("Collectable"); - } - if (IsExpert) - { - ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X); - ImGui.SetCursorPosY(ImGui.GetCursorPosY() + (imageSize.Y - ImGui.GetFontSize()) / 2f); - // Using ItemLevel icon instead of '◈' because the game fonts hate - // me and I can't bother to include a font just for this one icon. - ImGui.TextColored(new(0.93f, 0.59f, 0.45f, 1), SeIconChar.ItemLevel.ToIconString()); - if (ImGui.IsItemHovered()) - ImGui.SetTooltip("Expert Recipe"); - } - var availWidth = ImGui.GetContentRegionAvail().X; - var text = $"Step {LatestState.StepCount + 1}"; - var textWidth = ImGui.CalcTextSize(text).X; - ImGui.SameLine(availWidth - textWidth); - ImGui.SetCursorPosY(ImGui.GetCursorPosY() + (imageSize.Y - ImGui.GetFontSize()) / 2f); - ImGui.TextUnformatted(text); - ImGui.Separator(); - } - - private void DrawSimulationBars(SynthDrawParams drawParams) - { - var state = LatestState; - - var (leftColumn, rightColumn, leftText, rightText) = (drawParams.LeftColumn, drawParams.RightColumn, drawParams.LeftText, drawParams.RightText); - - ImGui.BeginTable("simSynth", 2); - - ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed, leftColumn); - ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed, rightColumn); - ImGui.TableNextColumn(); - - DrawSynthProgress("Durability", state.Durability, Input.Recipe.MaxDurability, DurabilityBarSize, DurabilityColor, leftText); - - DrawSynthCircle("Condition", state.Condition.Name(), ConditionBarSize, new Vector4(.35f, .35f, .35f, 0) + state.Condition.GetColor(DateTime.UtcNow.TimeOfDay), DurabilityBarSize, leftText); - if (ImGui.IsItemHovered()) - ImGui.SetTooltip(state.Condition.Description(state.Input.Stats.HasSplendorousBuff)); - - if (Item.IsCollectable) - { - var collectibility = Math.Max(state.Quality / 10, 1); - DrawSynthBar("Collectability", collectibility, Input.Recipe.MaxQuality / 10, $"{collectibility}", DurabilityBarSize, HQColor, leftText); - } - else - DrawSynthBar("HQ %", state.HQPercent, 100, $"{state.HQPercent}%", DurabilityBarSize, HQColor, leftText); - - ImGui.TableNextColumn(); - - DrawSynthProgress("Progress", state.Progress, Input.Recipe.MaxProgress, ProgressBarSize, ProgressColor, rightText); - DrawSynthProgress("Quality", state.Quality, Input.Recipe.MaxQuality, ProgressBarSize, QualityColor, rightText); - DrawSynthProgress("CP", state.CP, Input.Stats.CP, ProgressBarSize, CPColor, rightText); - - ImGui.EndTable(); - } - - private void DrawSimulationEffects(SynthDrawParams drawParams) - { - ImGuiUtils.BeginGroupPanel("Effects", drawParams.Total); - - var effectHeight = ImGui.GetFontSize() * 2f; - Vector2 GetEffectSize(IDalamudTextureWrap icon) => new(icon.Width * effectHeight / icon.Height, effectHeight); - - ImGui.Dummy(new(0, effectHeight)); - ImGui.SameLine(0, 0); - foreach (var effect in Enum.GetValues()) - { - var duration = Sim.GetEffectDuration(effect); - if (duration == 0) - continue; - - var strength = Sim.GetEffectStrength(effect); - var icon = effect.GetIcon(strength); - var iconSize = GetEffectSize(icon); - - ImGui.Image(icon.ImGuiHandle, iconSize); - if (ImGui.IsItemHovered()) - ImGui.SetTooltip(effect.GetTooltip(strength, duration)); - if (duration != 0) - { - ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X); - ImGui.SetCursorPosY(ImGui.GetCursorPosY() + (effectHeight - ImGui.GetFontSize()) / 2f); - ImGui.Text($"{duration}"); - } - ImGui.SameLine(); - } - ImGui.Dummy(Vector2.Zero); - - ImGuiUtils.EndGroupPanel(); - } - - private void DrawSimulationActions(SynthDrawParams drawParams) - { - ImGuiUtils.BeginGroupPanel("Actions", drawParams.Total); - - ImGui.PushStyleColor(ImGuiCol.Button, Vector4.Zero); - ImGui.PushStyleColor(ImGuiCol.ButtonActive, Vector4.Zero); - ImGui.PushStyleColor(ImGuiCol.ButtonHovered, Vector4.Zero); - var actionSize = new Vector2((drawParams.Total / 10) - ImGui.GetStyle().ItemSpacing.X * (11f / 10)); - ImGui.Dummy(new(0, actionSize.Y)); - ImGui.SameLine(0, 0); - for (var i = 0; i < Actions.Count; ++i) - { - var (action, tooltip, response, state) = Actions[i]; - ImGui.PushID(i); - if (ImGui.ImageButton(action.GetIcon(ClassJob).ImGuiHandle, actionSize, Vector2.Zero, Vector2.One, 0, default, response != ActionResponse.UsedAction ? BadActionImageTint : Vector4.One)) - if (CanModifyActions) - RemoveAction(i); - if (CanModifyActions) - { - if (ImGui.BeginDragDropSource()) - { - unsafe { ImGui.SetDragDropPayload("simulationAction", (nint)(&i), sizeof(int)); } - ImGui.ImageButton(Actions[i].Action.GetIcon(ClassJob).ImGuiHandle, actionSize); - ImGui.EndDragDropSource(); - } - if (ImGui.BeginDragDropTarget()) - { - var payload = ImGui.AcceptDragDropPayload("simulationAction"); - bool isValidPayload; - unsafe { isValidPayload = payload.NativePtr != null; } - if (isValidPayload) - { - int draggedIdx; - unsafe { draggedIdx = *(int*)payload.Data; } - var draggedAction = Actions[draggedIdx].Action; - RemoveAction(draggedIdx); - InsertAction(i, draggedAction); - } - ImGui.EndDragDropTarget(); - } - } - if (ImGui.IsItemHovered()) - { - ImGui.BeginTooltip(); - var responseText = response switch - { - ActionResponse.SimulationComplete => "Recipe Complete", - ActionResponse.ActionNotUnlocked => "Action Not Unlocked", - ActionResponse.NotEnoughCP => "Not Enough CP", - ActionResponse.NoDurability => "No More Durability", - ActionResponse.CannotUseAction => "Cannot Use", - _ => string.Empty, - }; - if (response != ActionResponse.UsedAction) - ImGui.TextColored(BadActionTextColor, responseText); - ImGui.Text($"{action.GetName(ClassJob)}\n{tooltip}"); - DrawAllProgressTooltips(state); - if (CanModifyActions) - ImGui.Text("Click to Remove\nDrag to Move"); - ImGui.EndTooltip(); - } - ImGui.PopID(); - if (i % 10 != 9) - ImGui.SameLine(); - } - ImGui.PopStyleColor(3); - - ImGuiUtils.EndGroupPanel(); - } - - private void DrawSimulationButtons(SynthDrawParams drawParams) - { - var totalWidth = drawParams.Total; - var halfWidth = (totalWidth - ImGui.GetStyle().ItemSpacing.X) / 2f; - var quarterWidth = (halfWidth - ImGui.GetStyle().ItemSpacing.X) / 2f; - var halfButtonSize = new Vector2(halfWidth, ImGui.GetFrameHeight()); - var quarterButtonSize = new Vector2(quarterWidth, ImGui.GetFrameHeight()); - - var conditionRandomnessText = "Condition Randomness"; - var conditionRandomness = Config.ConditionRandomness; - ImGui.BeginDisabled(!CanModifyActions); - if (ImGui.Checkbox(conditionRandomnessText, ref conditionRandomness)) - { - Config.ConditionRandomness = conditionRandomness; - Config.Save(); - ResetSimulator(); - } - ImGui.EndDisabled(); - if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) - ImGui.SetTooltip("Allows the condition to fluctuate randomly like a real craft.\nTurns off when generating a macro."); - - var labelSize = ImGui.CalcTextSize(conditionRandomnessText); - var checkboxWidth = ImGui.GetFrameHeight() + (labelSize.X > 0 ? ImGui.GetStyle().ItemInnerSpacing.X + labelSize.X : 0); - ImGui.PushFont(UiBuilder.IconFont); - var cogWidth = ImGui.CalcTextSize(FontAwesomeIcon.Cog.ToIconString()).X; - ImGui.PopFont(); - ImGui.SameLine(0, totalWidth - ImGui.GetStyle().ItemSpacing.X - checkboxWidth - cogWidth); - if (ImGuiComponents.IconButton("simSettingsButton", FontAwesomeIcon.Cog)) - Service.Plugin.OpenSettingsTab(Settings.TabSimulator); - - // - - var macroName = MacroName; - ImGui.SetNextItemWidth(halfWidth); - if (ImGui.InputTextWithHint("", "Macro Name", ref macroName, 64)) - MacroName = macroName; - - ImGui.SameLine(); - - DrawSimulationGenerateButton(halfButtonSize); - - // - - ImGui.BeginDisabled(!CanModifyActions); - if (Macro != null) - { - if (ImGui.Button("Save", quarterButtonSize)) - { - Macro.Name = MacroName; - Macro.Actions = Actions.Select(a => a.Action).ToList(); - Config.Save(); - } - ImGui.SameLine(); - } - if (ImGui.Button("Save New", Macro == null ? halfButtonSize : quarterButtonSize)) - { - Macro = new() { Name = MacroName, Actions = Actions.Select(a => a.Action).ToList() }; - Config.Macros.Add(Macro); - Config.Save(); - } - ImGui.SameLine(); - if (ImGui.Button("Reset", halfButtonSize)) - ClearAllActions(); - ImGui.EndDisabled(); - } - - private void DrawSimulationGenerateButton(Vector2 buttonSize) - { - var state = GenerateSolverState(); - string buttonText; - string tooltipText; - bool isEnabled; - var taskCompleted = SolverTask?.IsCompleted ?? true; - var taskCancelled = SolverTaskToken?.IsCancellationRequested ?? false; - if (!taskCompleted) - { - if (taskCancelled) - { - buttonText = "Cancelling..."; - tooltipText = "Cancelling macro generation. This shouldn't take long."; - isEnabled = false; - } - else - { - buttonText = "Cancel"; - tooltipText = "Cancel macro generation"; - isEnabled = true; - } - } - else - { - if (SolverActionsChanged) - { - buttonText = "Generate"; - tooltipText = "Generate a set of actions to finish the macro."; - isEnabled = state.HasValue; - if (!isEnabled) - tooltipText += "\nMake sure your craft so far is valid (without random condition changes)"; - } - else - { - buttonText = "Regenerate"; - tooltipText = "Retry and regenerate a new set of actions to finish the macro."; - isEnabled = true; - } - } - ImGui.BeginDisabled(!isEnabled); - if (ImGui.Button(buttonText, buttonSize)) - { - if (!taskCompleted) - { - if (!taskCancelled) - SolverTaskToken?.Cancel(); - } - else - { - if (SolverActionsChanged) - { - if (state.HasValue) - SolveMacro(state.Value); - } - else - { - Actions.RemoveRange(SolverInitialActionCount, Actions.Count - SolverInitialActionCount); - SolveMacro(GenerateSolverState()!.Value); - } - } - } - ImGui.EndDisabled(); - if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) - ImGui.SetTooltip(tooltipText); - } -} diff --git a/Craftimizer/Windows/SimulatorDrawerUtils.cs b/Craftimizer/Windows/SimulatorDrawerUtils.cs deleted file mode 100644 index 7711253..0000000 --- a/Craftimizer/Windows/SimulatorDrawerUtils.cs +++ /dev/null @@ -1,146 +0,0 @@ -using Craftimizer.Simulator; -using Dalamud.Interface.Windowing; -using ImGuiNET; -using System; -using System.Numerics; - -namespace Craftimizer.Plugin.Windows; - -public sealed partial class Simulator : Window, IDisposable -{ - private readonly record struct SynthDrawParams - { - public float LeftColumn { get; init; } - public float RightColumn { get; init; } - public float LeftText { get; init; } - public float RightText { get; init; } - public float Total { get; init; } - } - - private SynthDrawParams CalculateSynthDrawParams() - { - var sidePadding = ImGui.GetFrameHeight() / 2; - var separatorTextWidth = ImGui.CalcTextSize(" / ").X; - var itemSpacing = ImGui.GetStyle().ItemSpacing.X; - - var leftDigits = (int)MathF.Floor(MathF.Log10(Input.Recipe.MaxDurability) + 1); - var leftTextWidth = ImGui.CalcTextSize(new string('0', leftDigits)).X; - var leftWidth = DurabilityBarSize.X + sidePadding + itemSpacing * 2 + separatorTextWidth + leftTextWidth * 2; - - - var rightDigits = (int)MathF.Floor(MathF.Log10(Math.Max(Math.Max(Input.Recipe.MaxProgress, Input.Recipe.MaxQuality), Input.Stats.CP)) + 1); - var rightTextWidth = ImGui.CalcTextSize(new string('0', rightDigits)).X; - var rightWidth = ProgressBarSize.X + sidePadding + itemSpacing * 2 + separatorTextWidth + rightTextWidth * 2; - - return new() - { - LeftColumn = leftWidth, - LeftText = leftTextWidth, - RightColumn = rightWidth, - RightText = rightTextWidth, - Total = leftWidth + rightWidth + itemSpacing - }; - } - - // Generic Progress Bar - private static void DrawSynthProgress(string name, int current, int max, Vector2 size, Vector4 color, float textWidth) - { - ImGuiUtils.BeginGroupPanel(name); - - DrawProgressBar(current, max, size, color); - - var w = ImGui.GetStyle().ItemSpacing.X; - ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(0, ImGui.GetStyle().ItemSpacing.Y)); - - ImGui.SameLine(0, textWidth - ImGui.CalcTextSize($"{current}").X + w); - var adjustedHeight = ImGui.GetCursorPosY() - ((ImGui.GetFrameHeight() - ImGui.GetFontSize()) / 2f); - ImGui.SetCursorPosY(adjustedHeight); - ImGui.TextUnformatted($"{current}"); - - ImGui.SameLine(); - ImGui.SetCursorPosY(adjustedHeight); - ImGui.TextUnformatted(" / "); - - ImGui.SameLine(0, textWidth - ImGui.CalcTextSize($"{max}").X); - ImGui.SetCursorPosY(adjustedHeight); - ImGui.TextUnformatted($"{max}"); - - ImGui.PopStyleVar(); - - ImGuiUtils.EndGroupPanel(); - } - - // HQ% / Collectability Bar (has no fractional bar to indicate max) - private static void DrawSynthBar(string name, int current, int max, string text, Vector2 size, Vector4 color, float textWidth) - { - ImGuiUtils.BeginGroupPanel(name); - - DrawProgressBar(current, max, size, color); - - var w = ImGui.GetStyle().ItemSpacing.X; - ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(0, ImGui.GetStyle().ItemSpacing.Y)); - - var totalWidth = textWidth * 2 + ImGui.CalcTextSize(" / ").X; - - ImGui.SameLine(0, totalWidth - ImGui.CalcTextSize(text).X + w); - var adjustedHeight = ImGui.GetCursorPosY() - ((ImGui.GetFrameHeight() - ImGui.GetFontSize()) / 2f); - ImGui.SetCursorPosY(adjustedHeight); - ImGui.TextUnformatted(text); - - ImGui.PopStyleVar(); - - ImGuiUtils.EndGroupPanel(); - } - - // Condition "Bar" Circle (always 100%, is a circle) - private static void DrawSynthCircle(string name, string text, Vector2 size, Vector4 color, Vector2 otherProgressSize, float textWidth) - { - ImGuiUtils.BeginGroupPanel(name); - - var w = ImGui.GetStyle().ItemSpacing.X; - ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(0, ImGui.GetStyle().ItemSpacing.Y)); - - var contentWidth = size.X + w + ImGui.CalcTextSize(text).X; - var totalWidth = otherProgressSize.X + w + textWidth * 2 + ImGui.CalcTextSize(" / ").X; - - ImGui.Dummy(default); - ImGui.SameLine(0, (totalWidth - contentWidth) / 2); - ImGui.PushStyleVar(ImGuiStyleVar.FrameRounding, Math.Max(size.X, size.Y)); - DrawProgressBar(1, 1, size, color); - ImGui.PopStyleVar(); - ImGui.SameLine(0, w); - var adjustedHeight = ImGui.GetCursorPosY() - ((ImGui.GetFrameHeight() - ImGui.GetFontSize()) / 2f); - ImGui.SetCursorPosY(adjustedHeight); - ImGui.TextUnformatted(text); - - ImGui.PopStyleVar(); - - ImGuiUtils.EndGroupPanel(); - } - - public static void DrawAllProgressBars(SimulationState state, Vector2 progressBarSize) - { - DrawProgressBar(state.Progress, state.Input.Recipe.MaxProgress, progressBarSize, ProgressColor); - if (ImGui.IsItemHovered()) - ImGui.SetTooltip($"Progress: {state.Progress} / {state.Input.Recipe.MaxProgress}"); - DrawProgressBar(state.Quality, state.Input.Recipe.MaxQuality, progressBarSize, QualityColor); - if (ImGui.IsItemHovered()) - ImGui.SetTooltip($"Quality: {state.Quality} / {state.Input.Recipe.MaxQuality}"); - DrawProgressBar(state.Durability, state.Input.Recipe.MaxDurability, progressBarSize, DurabilityColor); - if (ImGui.IsItemHovered()) - ImGui.SetTooltip($"Durability: {state.Durability} / {state.Input.Recipe.MaxDurability}"); - DrawProgressBar(state.CP, state.Input.Stats.CP, progressBarSize, CPColor); - if (ImGui.IsItemHovered()) - ImGui.SetTooltip($"CP: {state.CP} / {state.Input.Stats.CP}"); - } - - public static void DrawAllProgressTooltips(SimulationState state) => - DrawAllProgressBars(state, TooltipProgressBarSize); - - private static void DrawProgressBar(int progress, int maxProgress, Vector2 size, Vector4 color, string overlay = "") - { - ImGui.PushStyleColor(ImGuiCol.PlotHistogram, color); - ImGui.ProgressBar(Math.Clamp((float)progress / maxProgress, 0f, 1f), size, overlay); - ImGui.PopStyleColor(); - } -} diff --git a/Craftimizer/Windows/SimulatorSolver.cs b/Craftimizer/Windows/SimulatorSolver.cs deleted file mode 100644 index cd20dca..0000000 --- a/Craftimizer/Windows/SimulatorSolver.cs +++ /dev/null @@ -1,100 +0,0 @@ -using Craftimizer.Simulator; -using Craftimizer.Simulator.Actions; -using Craftimizer.Utils; -using Dalamud.Interface.Windowing; -using System; -using System.Collections.Concurrent; -using System.Threading; - -namespace Craftimizer.Plugin.Windows; - -public sealed partial class Simulator : Window, IDisposable -{ - private Solver.Solver? SolverTask { get; set; } - private CancellationTokenSource? SolverTaskToken { get; set; } - private ConcurrentQueue SolverActionQueue { get; } = new(); - private int SolverInitialActionCount { get; set; } - private bool SolverActionsChanged { get; set; } = true; - - private bool CanModifyActions => SolverTask?.IsCompleted ?? true; - - private void OnActionsChanged() - { - SolverActionsChanged = true; - } - - private SimulationState? GenerateSolverState() - { - if (Sim is SimulatorNoRandom) - { - if (!Actions.Exists(a => a.Response != ActionResponse.UsedAction)) - return LatestState; - else - return null; - } - - var ret = new SimulationState(Input); - if (Actions.Count != 0) - { - var tmpSim = new SimulatorNoRandom(ret); - foreach (var action in Actions) - { - (var resp, ret) = tmpSim.Execute(ret, action.Action); - if (resp != ActionResponse.UsedAction) - return null; - } - } - return ret; - } - - private void StopSolveMacro() - { - if (SolverTask == null || SolverTaskToken == null) - return; - - if (!SolverTask.IsCompleted) - SolverTaskToken.Cancel(); - else - { - SolverTaskToken.Dispose(); - SolverTask.Dispose(); - - SolverTask = null; - SolverTaskToken = null; - } - } - - private void SolveMacro(SimulationState solverState) - { - StopSolveMacro(); - - // Prevents the quality bar from being unfair between solves - if (Config.ConditionRandomness) - { - Config.ConditionRandomness = false; - Config.Save(); - - ResetSimulator(); - } - - SolverActionsChanged = false; - - SolverActionQueue.Clear(); - - SolverInitialActionCount = Actions.Count; - SolverTaskToken = new(); - SolverTask = new(Config.SimulatorSolverConfig, solverState) { Token = SolverTaskToken.Token }; - SolverTask.OnLog += s => Log.Debug(s); - SolverTask.OnNewAction += SolverActionQueue.Enqueue; - SolverTask.Start(); - } - - public void Dispose() - { - StopSolveMacro(); - SolverTaskToken?.Cancel(); - SolverTask?.TryWait(); - SolverTask?.Dispose(); - SolverTaskToken?.Dispose(); - } -}