diff --git a/Craftimizer/Configuration.cs b/Craftimizer/Configuration.cs index 012590f..9d525a7 100644 --- a/Craftimizer/Configuration.cs +++ b/Craftimizer/Configuration.cs @@ -20,6 +20,7 @@ public class Configuration : IPluginConfiguration public int Version { get; set; } = 1; public bool OverrideUncraftability { get; set; } = true; + public bool HideUnlearnedActions { get; set; } = true; public List Macros { get; set; } = new(); public string SimulatorType { get; set; } = typeof(Simulator.Simulator).AssemblyQualifiedName!; diff --git a/Craftimizer/ImGuiUtils.cs b/Craftimizer/ImGuiUtils.cs index 7b2bf03..f2763d2 100644 --- a/Craftimizer/ImGuiUtils.cs +++ b/Craftimizer/ImGuiUtils.cs @@ -10,7 +10,35 @@ internal static class ImGuiUtils private static readonly Stack<(Vector2 Min, Vector2 Max)> GroupPanelLabelStack = new(); // Adapted from https://github.com/ocornut/imgui/issues/1496#issuecomment-655048353 - public static void BeginGroupPanel(string name, float width = -1) + public static void BeginGroupPanel(float width = -1, bool addPadding = true) + { + ImGui.BeginGroup(); + + var itemSpacing = ImGui.GetStyle().ItemSpacing; + ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, Vector2.Zero); + ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Vector2.Zero); + + var frameHeight = ImGui.GetFrameHeight(); + + ImGui.BeginGroup(); + ImGui.Dummy(new Vector2(width < 0 ? ImGui.GetContentRegionAvail().X : width, 0)); + ImGui.Dummy(new Vector2(frameHeight * 0.5f, 0)); + ImGui.SameLine(0, 0); + + ImGui.BeginGroup(); + ImGui.Dummy(new Vector2(frameHeight * 0.5f, 0)); + GroupPanelLabelStack.Push((ImGui.GetItemRectMin(), ImGui.GetItemRectMax())); + ImGui.SameLine(0, 0); + ImGui.Dummy(new Vector2(0f, frameHeight * (addPadding ? 1 : .5f) + itemSpacing.Y)); + + ImGui.BeginGroup(); + + ImGui.PopStyleVar(2); + + ImGui.PushItemWidth(MathF.Max(0, ImGui.CalcItemWidth() - frameHeight)); + } + + public static void BeginGroupPanel(string name, float width = -1, bool addPadding = true) { ImGui.BeginGroup(); @@ -31,14 +59,13 @@ internal static class ImGuiUtils ImGui.TextUnformatted(name); GroupPanelLabelStack.Push((ImGui.GetItemRectMin(), ImGui.GetItemRectMax())); ImGui.SameLine(0, 0); - ImGui.Dummy(new Vector2(0f, frameHeight + itemSpacing.Y)); + ImGui.Dummy(new Vector2(0f, frameHeight * (addPadding ? 1 : .5f) + itemSpacing.Y)); ImGui.BeginGroup(); ImGui.PopStyleVar(2); ImGui.PushItemWidth(MathF.Max(0, ImGui.CalcItemWidth() - frameHeight)); - } public static void EndGroupPanel() diff --git a/Craftimizer/Plugin.cs b/Craftimizer/Plugin.cs index 22c755b..505f70c 100644 --- a/Craftimizer/Plugin.cs +++ b/Craftimizer/Plugin.cs @@ -40,14 +40,14 @@ public sealed class Plugin : IDalamudPlugin Service.PluginInterface.UiBuilder.OpenConfigUi += () => SettingsWindow.IsOpen = true; } - public void OpenSimulatorWindow(Item item, SimulationInput input, ClassJob classJob, List actions) + public void OpenSimulatorWindow(Item item, bool isExpert, SimulationInput input, ClassJob classJob, List actions) { if (SimulatorWindow != null) { SimulatorWindow.IsOpen = false; WindowSystem.RemoveWindow(SimulatorWindow); } - SimulatorWindow = new(item, input, classJob, actions); + SimulatorWindow = new(item, isExpert, input, classJob, actions); } public void Dispose() diff --git a/Craftimizer/SimulatorUtils.cs b/Craftimizer/SimulatorUtils.cs index daee141..2dba60c 100644 --- a/Craftimizer/SimulatorUtils.cs +++ b/Craftimizer/SimulatorUtils.cs @@ -160,10 +160,10 @@ internal static class ConditionUtils private static Vector3 AddRGB(this Condition me) => me switch { - Condition.Poor => Vector3.Zero, // Unsure + Condition.Poor => new(-50, -50, -50), Condition.Normal => new(32, 48, 64), Condition.Good => new(80, -80, 0), - Condition.Excellent => Vector3.Zero, // Unsure + Condition.Excellent => Vector3.Zero, // All the other conditions are just a single lerp, this one is different Condition.Centered => new(200, 200, 0), Condition.Sturdy => new(-100, 45, 155), Condition.Pliant => new(0, 250, 0), @@ -173,6 +173,65 @@ internal static class ConditionUtils _ => Vector3.Zero // Unknown }; + private const float ConditionCyclePeriod = 19/30f; + // The real period of all condition color cycles are 0.633... (19/30) seconds + // Interp accepts 0-1 + public static Vector4 GetColor(this Condition me, float interp) + { + //var baseColor = new Vector3(0.85f, 0.85f, 0.85f); // Middle-ish pixels of synthesis2_hr1.tex's condition circle + + Vector3 addRgb; + // Excellent has 6 lerps and 1 ending constant + if (me == Condition.Excellent) + { + addRgb = interp switch + { + < 0.155f => Vector3.Lerp(new(128,0,0), new(128,80,0), (interp - 0) / 0.155f), + < 0.315f => Vector3.Lerp(new(128, 80, 0), new(128, 128, 0), (interp - 0.155f) / 0.16f), + < 0.475f => Vector3.Lerp(new(128, 128, 0), new(0, 64, 0), (interp - 0.315f) / 0.16f), + < 0.630f => Vector3.Lerp(new(0, 64, 0), new(0, 128, 128), (interp - 0.475f) / 0.155f), + < 0.790f => Vector3.Lerp(new(0, 128, 128), new(0, 0, 128), (interp - 0.630f) / 0.16f), + < 0.945f => Vector3.Lerp(new(0, 0, 128), new(64, 0, 64), (interp - 0.790f) / 0.155f), + _ => new(64, 0, 64) + }; + } + // Period is twice as fast so we oscillate at twice that speed + else if (me == Condition.Malleable) + { + if (interp > .5f) + interp -= .5f; + if (interp > .25f) + interp = .25f - (interp - .25f); + interp *= 4; + addRgb = Vector3.Lerp(new(-80, -40, 180), new(-41, -1, 254), interp); + } + else + { + if (interp > .5f) + interp = .5f - (interp - .5f); + interp *= 2; + addRgb = me switch + { + Condition.Poor => Vector3.Lerp(new(-50, -50, -50), new(-1, -1, -1), interp), + Condition.Normal => Vector3.Lerp(new(32, 48, 64), new(63, 95, 127), interp), + Condition.Good => Vector3.Lerp(new(80, -80, 0), new(159, -1, 0), interp), + Condition.Centered => Vector3.Lerp(new(199, 199, 0), new(100, 100, 0), interp), + Condition.Sturdy => Vector3.Lerp(new(-100, 45, 155), new(-51, 89, 254), interp), + Condition.Pliant => Vector3.Lerp(new(0, 150, 0), new(0, 249, 0), interp), + Condition.Primed => Vector3.Lerp(new(-30, -255, 50), new(29, -156, 199), interp), + Condition.GoodOmen => Vector3.Lerp(new(100, 20, 0), new(100, 99, 99), interp), + _ => default + }; + } + + return new(addRgb / 255, 1); + } + + public static Vector4 GetColor(this Condition me, TimeSpan time) + { + return me.GetColor((float)(time.TotalSeconds % ConditionCyclePeriod / ConditionCyclePeriod)); + } + public static string Name(this Condition me) => LuminaSheets.AddonSheet.GetRow(me.AddonIds().Name)!.Text.ToDalamudString().TextValue; diff --git a/Craftimizer/Windows/CraftingLog.cs b/Craftimizer/Windows/CraftingLog.cs index 0efad5f..67dfd9b 100644 --- a/Craftimizer/Windows/CraftingLog.cs +++ b/Craftimizer/Windows/CraftingLog.cs @@ -294,7 +294,7 @@ public unsafe class CraftingLog : Window if (ImGui.Button("Open Simulator", size)) { - Service.Plugin.OpenSimulatorWindow(Recipe.ItemResult.Value!, CharacterSimulationInput, RecipeClassJob, new()); + Service.Plugin.OpenSimulatorWindow(Recipe.ItemResult.Value!, Recipe.IsExpert, CharacterSimulationInput, RecipeClassJob, new()); } ImGui.SameLine(); ImGui.Button("Generate a new macro", size); diff --git a/Craftimizer/Windows/SimulatorWindow.cs b/Craftimizer/Windows/SimulatorWindow.cs index c14868f..cc3da23 100644 --- a/Craftimizer/Windows/SimulatorWindow.cs +++ b/Craftimizer/Windows/SimulatorWindow.cs @@ -3,32 +3,46 @@ using Craftimizer.Simulator.Actions; using Dalamud.Interface; using Dalamud.Interface.Windowing; using Dalamud.Logging; -using Dalamud.Plugin; +using Dalamud.Memory; using Dalamud.Utility; +using FFXIVClientStructs.FFXIV.Client.System.Framework; +using FFXIVClientStructs.FFXIV.Client.UI; +using FFXIVClientStructs.FFXIV.Component.GUI; using ImGuiNET; using ImGuiScene; using Lumina.Excel.GeneratedSheets; using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using System.Numerics; using ActionCategory = Craftimizer.Simulator.ActionCategory; using ClassJob = Craftimizer.Simulator.ClassJob; +using Condition = Craftimizer.Simulator.Condition; namespace Craftimizer.Plugin.Windows; public class SimulatorWindow : Window { + private const ImGuiWindowFlags WindowFlags = ImGuiWindowFlags.AlwaysAutoResize; + 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); private static readonly Vector2 TooltipProgressBarSize = new(100, 5); - private static readonly Vector4 ProgressColor = new(.2f, 1f, .2f, 1f); - private static readonly Vector4 QualityColor = new(.2f, .2f, 1f, 1f); - private static readonly Vector4 DurabilityColor = new(1f, 1f, .2f, 1f); - private static readonly Vector4 CPColor = new(1f, .2f, 1f, 1f); + private static readonly Vector4 ProgressColorOld = new(.2f, 1f, .2f, 1f); + private static readonly Vector4 QualityColorOld = new(.2f, .2f, 1f, 1f); + private static readonly Vector4 DurabilityColorOld = new(1f, 1f, .2f, 1f); + private static readonly Vector4 CPColorOld = new(1f, .2f, 1f, 1f); - private static readonly Vector4 CPColorNew = new(0.38f, 0.77f, 1f, 1f); + private static readonly Vector4 ProgressColor = new(0.44f, 0.65f, 0.18f, 1f); + private static readonly Vector4 QualityColor = new(0.26f, 0.71f, 0.69f, 1f); + private static readonly Vector4 DurabilityColor = new(0.13f, 0.52f, 0.93f, 1f); + private static readonly Vector4 HQColor = new(0.592f, 0.863f, 0.376f, 1f); + private 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); @@ -41,6 +55,7 @@ public class SimulatorWindow : Window private Stopwatch Stopwatch { get; } = new(); private Item Item { get; } + private bool IsExpert { get; } private SimulationInput Input { get; } private ClassJob ClassJob { get; } // State is the state of the simulation *after* its corresponding action is executed. @@ -49,18 +64,17 @@ public class SimulatorWindow : Window private SimulationState LatestState => Actions.Count == 0 ? new(Input) : Actions[^1].State; - private ActionType? DraggedAction { get; set; } - static SimulatorWindow() { SortedActions = Enum.GetValues().GroupBy(a => a.Category()).Select(g => (g.Key, g.OrderBy(a => a.Level()).ToArray())).ToArray(); } - public SimulatorWindow(Item item, SimulationInput input, ClassJob classJob, List actions) : base("Simulator") + public SimulatorWindow(Item item, bool isExpert, SimulationInput input, ClassJob classJob, List actions) : base("Simulator", WindowFlags) { Service.WindowSystem.AddWindow(this); Item = item; + IsExpert = isExpert; Input = input; ClassJob = classJob; Actions = new(); @@ -70,6 +84,8 @@ public class SimulatorWindow : Window AppendAction(action); IsOpen = true; + CollapsedCondition = ImGuiCond.Appearing; + Collapsed = false; } public override void PreDraw() @@ -84,77 +100,89 @@ public class SimulatorWindow : Window Stopwatch.Stop(); FrameTime = Stopwatch.Elapsed; + /* + unsafe { + var unitBase = (AtkUnitBase*)Service.GameGui.GetAddonByName("Synthesis"); + if (unitBase != null) + { + var res = unitBase->GetNodeById(95)->GetAsAtkComponentNode()->Component; + var cond = MemoryHelper.ReadStringNullTerminated((nint)res->GetTextNodeById(4)->GetAsAtkTextNode()->GetText()); + var img = res->GetImageNodeById(3); + + var d = unchecked(((short)img->AddRed, (short)img->AddGreen, (short)img->AddBlue)); + PluginLog.LogDebug($"{cond} -> {d}"); + } + } + */ base.PostDraw(); } public override void Draw() { - ImGui.BeginTable("simulatorWindow", 2, ImGuiTableFlags.Resizable); - - ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed, 300); + ImGui.BeginTable("simulatorWindow", 2, ImGuiTableFlags.BordersInnerV); + ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed, 260); + ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthStretch); ImGui.TableNextColumn(); DrawActions(); - ImGui.TableNextColumn(); DrawSimulationInfo(); - ImGui.EndTable(); ImGui.TextUnformatted($"{FrameTime.TotalMilliseconds:0.00}ms"); + return; } - + private void DrawActions() { - ImGui.BeginChild("CraftimizerActions", Vector2.Zero, true, ImGuiWindowFlags.NoDecoration); - //ImGui.Checkbox("Show only guaranteed actions", ref showOnlyGuaranteedActions); - ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Vector2.Zero); - var actionSize = new Vector2(ImGui.GetFontSize() * 2); + var actionSize = new Vector2(ImGui.GetFontSize() * 2f); + ImGui.PushStyleColor(ImGuiCol.Button, Vector4.Zero); + ImGui.PushStyleColor(ImGuiCol.ButtonActive, Vector4.Zero); + ImGui.PushStyleColor(ImGuiCol.ButtonHovered, Vector4.Zero); + + //ImGui.Checkbox("Show only guaranteed actions", ref showOnlyGuaranteedActions); + foreach (var (category, actions) in SortedActions) { var i = 0; - ImGuiUtils.BeginGroupPanel(category.GetDisplayName()); + ImGuiUtils.BeginGroupPanel(category.GetDisplayName(), 260); foreach (var action in actions) { var baseAction = action.Base(); var cannotUse = action.Level() > Input.Stats.Level || (action == ActionType.Manipulation && !Input.Stats.CanUseManipulation); + if (cannotUse && Service.Configuration.HideUnlearnedActions) + continue; + var shouldNotUse = !baseAction.CanUse(Simulator) || Simulator.IsComplete; ImGui.BeginDisabled(cannotUse); - if (shouldNotUse) - ImGui.PushStyleColor(ImGuiCol.Button, BadActionImageColor); - - if (ImGui.ImageButton(action.GetIcon(ClassJob).ImGuiHandle, actionSize, Vector2.Zero, Vector2.One, -1, default, shouldNotUse ? BadActionImageTint : Vector4.One)) + 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(Simulator, true)}"); - if (shouldNotUse) - ImGui.PopStyleColor(); - ImGui.EndDisabled(); if (++i % 5 != 0) ImGui.SameLine(); } + if (i == 0) + ImGui.Dummy(actionSize); ImGuiUtils.EndGroupPanel(); } - ImGui.PopStyleVar(); - ImGui.EndChild(); + ImGui.PopStyleColor(3); } private void DrawSimulationInfo() { - ImGui.BeginChild("simulationInfo", Vector2.Zero, true, ImGuiWindowFlags.NoDecoration); DrawSimulationSynth(); ImGuiHelpers.ScaledDummy(5); DrawSimulationEffects(); ImGuiHelpers.ScaledDummy(5); DrawSimulationActions(); - ImGui.EndChild(); } private void DrawSimulationSynth() @@ -162,73 +190,147 @@ public class SimulatorWindow : Window var state = LatestState; var imageSize = new Vector2(ImGui.GetFontSize() * 2f); - ImGui.Image(Icons.GetIconFromId(Item.Icon).ImGuiHandle, imageSize); - ImGui.SameLine(); - ImGui.AlignTextToFramePadding(); - ImGui.SetCursorPosY(ImGui.GetFontSize()*.75f); - ImGui.TextUnformatted(Item.Name.ToDalamudString().ToString()); - var availWidth = ImGui.GetContentRegionAvail().X; - var text = $"Step {state.StepCount + 1}"; - var textWidth = ImGui.CalcTextSize(text).X; - ImGui.SameLine(availWidth - textWidth); - ImGui.AlignTextToFramePadding(); - ImGui.SetCursorPosY(ImGui.GetFontSize() * .75f); - ImGui.TextUnformatted(text); - ImGui.Separator(); + { + ImGui.Image(Icons.GetIconFromId(Item.Icon).ImGuiHandle, imageSize); + ImGui.SameLine(); + ImGui.SetCursorPosY(ImGui.GetCursorPosY() + ImGui.GetFontSize() * .5f); + ImGui.TextUnformatted(Item.Name.ToDalamudString().ToString()); + if (Item.IsCollectable) + { + ImGui.SameLine(0, 5); + ImGui.SetCursorPosY(ImGui.GetCursorPosY() + ImGui.GetFontSize() * .5f); + ImGui.TextColored(new(0.98f, 0.98f, 0.61f, 1), "(Collectible)"); + } + if (IsExpert) + { + ImGui.SameLine(0, 5); + ImGui.SetCursorPosY(ImGui.GetCursorPosY() + ImGui.GetFontSize() * .5f); + ImGui.TextColored(new(0.941f, 0.557f, 0.216f, 1), "(Expert)"); + } + var availWidth = ImGui.GetContentRegionAvail().X; + var text = $"Step {state.StepCount + 1}"; + var textWidth = ImGui.CalcTextSize(text).X; + ImGui.SameLine(availWidth - textWidth); + ImGui.SetCursorPosY(ImGui.GetCursorPosY() + ImGui.GetFontSize() * .5f); + ImGui.TextUnformatted(text); + ImGui.Separator(); + } ImGui.BeginTable("simSynth", 2); - ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed, 110); - ImGui.TableNextColumn(); - ImGuiUtils.BeginGroupPanel("Durability"); - ImGui.TextUnformatted($"{state.Durability} / {Input.Recipe.MaxDurability}"); - DrawProgressBar(state.Durability, Input.Recipe.MaxDurability, new(100, 20), CPColorNew); - ImGuiUtils.EndGroupPanel(); + var sidePadding = ImGui.GetFrameHeight() / 2; + var separatorTextWidth = ImGui.CalcTextSize(" / ").X; + var itemSpacing = ImGui.GetStyle().ItemSpacing.X * 2; - ImGuiUtils.BeginGroupPanel("Condition"); - ImGui.TextUnformatted(state.Condition.Name()); + 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 + 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 + separatorTextWidth + rightTextWidth * 2; + + ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed, leftWidth); + ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed, rightWidth); + ImGui.TableNextColumn(); + + DrawSynthBarCenteredProgress("Durability", state.Durability, Input.Recipe.MaxDurability, DurabilityBarSize, DurabilityColor, leftTextWidth); + + DrawSynthBarCenteredCircle("Condition", state.Condition.Name(), ConditionBarSize, new Vector4(.35f, .35f, .35f, 0) + state.Condition.GetColor(DateTime.UtcNow.TimeOfDay), DurabilityBarSize, leftTextWidth); if (ImGui.IsItemHovered()) ImGui.SetTooltip(state.Condition.Description(state.Input.Stats.HasSplendorousBuff)); - ImGuiUtils.EndGroupPanel(); + + if (Item.IsCollectable) + { + var collectibility = Math.Max(state.Quality / 10, 1); + DrawSynthBarCentered("Collectability", collectibility, Input.Recipe.MaxQuality / 10, $"{collectibility}", DurabilityBarSize, HQColor, leftTextWidth); + } + else + DrawSynthBarCentered("HQ %", state.HQPercent, 100, $"{state.HQPercent}%", DurabilityBarSize, HQColor, leftTextWidth); ImGui.TableNextColumn(); - ImGuiUtils.BeginGroupPanel("Progress"); - DrawProgressBar(state.Progress, Input.Recipe.MaxProgress, new(200, 20), ProgressColor); - availWidth = ImGui.GetContentRegionAvail().X; - text = $"{state.Progress} / {Input.Recipe.MaxProgress}"; - textWidth = ImGui.CalcTextSize(text).X; - ImGui.SameLine(availWidth - textWidth - 10); - ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ((ImGui.GetFrameHeight() - ImGui.GetFontSize()) / 2f)); - ImGui.TextUnformatted(text); - ImGuiUtils.EndGroupPanel(); - - ImGuiUtils.BeginGroupPanel("Quality"); - DrawProgressBar(state.Quality, Input.Recipe.MaxQuality, new(200, 20), QualityColor); - availWidth = ImGui.GetContentRegionAvail().X; - text = $"{state.Quality} / {Input.Recipe.MaxQuality}"; - textWidth = ImGui.CalcTextSize(text).X; - ImGui.SameLine(availWidth - textWidth - 10); - ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ((ImGui.GetFrameHeight() - ImGui.GetFontSize()) / 2f)); - ImGui.TextUnformatted(text); - ImGuiUtils.EndGroupPanel(); - - ImGuiUtils.BeginGroupPanel("CP"); - DrawProgressBar(state.CP, Input.Stats.CP, new(200, 20), CPColor); - availWidth = ImGui.GetContentRegionAvail().X; - text = $"{state.CP} / {Input.Stats.CP}"; - textWidth = ImGui.CalcTextSize(text).X; - ImGui.SameLine(availWidth - textWidth - 10); - ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ((ImGui.GetFrameHeight() - ImGui.GetFontSize()) / 2f)); - ImGui.TextUnformatted(text); - ImGuiUtils.EndGroupPanel(); - - ImGui.Separator(); - ImGui.TextUnformatted($"HQ {state.HQPercent}%"); + DrawSynthBarCenteredProgress("Progress", state.Progress, Input.Recipe.MaxProgress, ProgressBarSize, ProgressColor, rightTextWidth); + DrawSynthBarCenteredProgress("Quality", state.Quality, Input.Recipe.MaxQuality, ProgressBarSize, QualityColor, rightTextWidth); + DrawSynthBarCenteredProgress("CP", state.CP, Input.Stats.CP, ProgressBarSize, CPColor, rightTextWidth); ImGui.EndTable(); } + private void DrawSynthBarCenteredProgress(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(); + } + + private void DrawSynthBarCenteredCircle(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(); + } + + private void DrawSynthBarCentered(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(); + } + private void DrawSimulationSynthOld() { var state = LatestState; @@ -238,10 +340,10 @@ public class SimulatorWindow : Window if (ImGui.IsItemHovered()) ImGui.SetTooltip(state.Condition.Description(state.Input.Stats.HasSplendorousBuff)); ImGui.Text($"{state.HQPercent}%% HQ"); - DrawProgressBarOld(state.Progress, Input.Recipe.MaxProgress, ProgressColor); - DrawProgressBarOld(state.Quality, Input.Recipe.MaxQuality, QualityColor); - DrawProgressBarOld(state.Durability, Input.Recipe.MaxDurability, DurabilityColor); - DrawProgressBarOld(state.CP, Input.Stats.CP, CPColor); + DrawProgressBarOld(state.Progress, Input.Recipe.MaxProgress, ProgressColorOld); + DrawProgressBarOld(state.Quality, Input.Recipe.MaxQuality, QualityColorOld); + DrawProgressBarOld(state.Durability, Input.Recipe.MaxDurability, DurabilityColorOld); + DrawProgressBarOld(state.CP, Input.Stats.CP, CPColorOld); } private void DrawSimulationEffects() @@ -279,28 +381,30 @@ public class SimulatorWindow : Window 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)) RemoveAction(i); if (ImGui.BeginDragDropSource()) { unsafe { ImGui.SetDragDropPayload("simulationAction", (nint)(void*)&i, sizeof(int)); } - ImGui.ImageButton(action.GetIcon(ClassJob).ImGuiHandle, actionSize); + 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"); - unsafe + bool isValidPayload; + unsafe { isValidPayload = payload.NativePtr != null; } + if (isValidPayload) { - if (payload.NativePtr != null) - { - int droppedIdx; - droppedIdx = *(int*)payload.Data; - var droppedAction = Actions[droppedIdx].Action; - RemoveAction(droppedIdx); - InsertAction(i, droppedAction); - } + int draggedIdx; + unsafe { draggedIdx = *(int*)payload.Data; } + var draggedAction = Actions[draggedIdx].Action; + RemoveAction(draggedIdx); + InsertAction(i, draggedAction); } + ImGui.EndDragDropTarget(); } if (ImGui.IsItemHovered()) { @@ -321,9 +425,10 @@ public class SimulatorWindow : Window DrawProgressBarTooltip(state.Quality, Input.Recipe.MaxQuality, QualityColor); DrawProgressBarTooltip(state.Durability, Input.Recipe.MaxDurability, DurabilityColor); DrawProgressBarTooltip(state.CP, Input.Stats.CP, CPColor); - ImGui.Text("Right Click to Remove\nDrag to Move"); + ImGui.Text("Click to Remove\nDrag to Move"); ImGui.EndTooltip(); } + ImGui.PopID(); ImGui.SameLine(); } ImGui.PopStyleColor(3); @@ -380,7 +485,7 @@ public class SimulatorWindow : Window } private static void DrawProgressBarOld(int progress, int maxProgress, Vector4 color) => - DrawProgressBar(progress, maxProgress, ProgressBarSize, color, $"{progress} / {maxProgress}"); + DrawProgressBar(progress, maxProgress, ProgressBarSizeOld, color, $"{progress} / {maxProgress}"); private static void DrawProgressBar(int progress, int maxProgress, Vector2 size, Vector4 color, string overlay = "") {