Finish crafting log helper window

This commit is contained in:
Asriel Camora
2023-10-10 18:38:01 -07:00
parent 9a924c8049
commit 1da2acc5c5
7 changed files with 288 additions and 34 deletions
+1
View File
@@ -26,6 +26,7 @@ public class Configuration : IPluginConfiguration
public SolverConfig SimulatorSolverConfig { get; set; } = SolverConfig.SimulatorDefault;
public SolverConfig SynthHelperSolverConfig { get; set; } = SolverConfig.SynthHelperDefault;
public bool EnableSynthHelper { get; set; } = true;
public bool ShowOptimalMacroStat { get; set; } = true;
public int SynthHelperStepCount { get; set; } = 5;
public Simulator.Simulator CreateSimulator(SimulationState state) =>
+117 -11
View File
@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Numerics;
namespace Craftimizer.Plugin;
internal static class ImGuiUtils
@@ -124,6 +125,96 @@ internal static class ImGuiUtils
ImGui.EndGroup();
}
private static Vector2 UnitCircle(float theta)
{
var (s, c) = MathF.SinCos(theta);
// SinCos positive y is downwards, but we want it upwards for ImGui
return new Vector2(c, -s);
}
private static float Lerp(float a, float b, float t) =>
MathF.FusedMultiplyAdd(b - a, t, a);
private static void ArcSegment(Vector2 o, Vector2 prev, Vector2 cur, Vector2? next, float radius, float ratio, uint color)
{
var d = ImGui.GetWindowDrawList();
d.PathLineTo(o + cur * radius);
d.PathLineTo(o + prev * radius);
d.PathLineTo(o + prev * radius * ratio);
d.PathLineTo(o + cur * radius * ratio);
if (next is { } nextValue)
d.PathLineTo(o + nextValue * radius);
d.PathFillConvex(color);
}
public static void Arc(float startAngle, float endAngle, float radius, float ratio, uint backgroundColor, uint filledColor, bool addDummy = true)
{
// Fix normals when drawing (for antialiasing)
if (startAngle > endAngle)
(startAngle, endAngle) = (endAngle, startAngle);
var offset = ImGui.GetCursorScreenPos() + new Vector2(radius);
var segments = ImGui.GetWindowDrawList()._CalcCircleAutoSegmentCount(radius * 2);
var incrementAngle = MathF.Tau / segments;
var isFullCircle = (endAngle - startAngle) % MathF.Tau == 0;
var prevA = startAngle;
var prev = UnitCircle(prevA);
for (var i = 1; i <= segments; ++i)
{
var a = startAngle + incrementAngle * i;
var cur = UnitCircle(a);
var nextA = a + incrementAngle;
var next = UnitCircle(nextA);
// full segment is background
if (prevA >= endAngle)
{
// don't overlap with the first segment
if (i == segments && !isFullCircle)
ArcSegment(offset, prev, cur, null, radius, ratio, backgroundColor);
else
ArcSegment(offset, prev, cur, next, radius, ratio, backgroundColor);
}
// segment is partially filled
else if (a > endAngle && !isFullCircle)
{
// we split the drawing in two
var end = UnitCircle(endAngle);
ArcSegment(offset, prev, end, null, radius, ratio, filledColor);
ArcSegment(offset, end, cur, next, radius, ratio, backgroundColor);
// set the previous segment to endAngle
a = endAngle;
cur = end;
}
// full segment is filled
else
{
// if the next segment will be partially filled, the next segment will be the endAngle
if (nextA > endAngle && !isFullCircle)
{
var end = UnitCircle(endAngle);
ArcSegment(offset, prev, cur, end, radius, ratio, filledColor);
}
else
ArcSegment(offset, prev, cur, next, radius, ratio, filledColor);
}
prevA = a;
prev = cur;
}
if (addDummy)
ImGui.Dummy(new Vector2(radius * 2));
}
public static void ArcProgress(float value, float radiusInner, float radiusOuter, uint backgroundColor, uint filledColor)
{
Arc(MathF.PI / 2, MathF.PI / 2 - MathF.Tau * Math.Clamp(value, 0, 1), radiusInner, radiusOuter, backgroundColor, filledColor);
}
public static bool IconButtonSized(FontAwesomeIcon icon, Vector2 size)
{
ImGui.PushFont(UiBuilder.IconFont);
@@ -155,16 +246,18 @@ internal static class ImGuiUtils
}
}
public static void AlignCentered(float width)
public static void AlignCentered(float width, float availWidth = default)
{
var availWidth = ImGui.GetContentRegionAvail().X;
if (availWidth == default)
availWidth = ImGui.GetContentRegionAvail().X;
if (availWidth > width)
ImGui.SetCursorPosX(ImGui.GetCursorPos().X + (availWidth - width) / 2);
}
public static void AlignMiddle(Vector2 size)
public static void AlignMiddle(Vector2 size, Vector2 availSize = default)
{
var availSize = ImGui.GetContentRegionAvail();
if (availSize == default)
availSize = ImGui.GetContentRegionAvail();
if (availSize.X > size.X)
ImGui.SetCursorPosX(ImGui.GetCursorPos().X + (availSize.X - size.X) / 2);
if (availSize.Y > size.Y)
@@ -172,21 +265,34 @@ internal static class ImGuiUtils
}
// https://stackoverflow.com/a/67855985
public static void TextCentered(string text)
public static void TextCentered(string text, float availWidth = default)
{
AlignCentered(ImGui.CalcTextSize(text).X);
AlignCentered(ImGui.CalcTextSize(text).X, availWidth);
ImGui.TextUnformatted(text);
}
public static void TextMiddle(string text)
public static void TextMiddle(string text, Vector2 availSize = default)
{
AlignMiddle(ImGui.CalcTextSize(text));
AlignMiddle(ImGui.CalcTextSize(text), availSize);
ImGui.TextUnformatted(text);
}
public static bool ButtonCentered(string text)
public static void TextMiddleNewLine(string text, Vector2 availSize)
{
AlignCentered(ImGui.CalcTextSize(text).X + ImGui.GetStyle().FramePadding.Y * 2);
return ImGui.Button(text);
if (availSize == default)
availSize = ImGui.GetContentRegionAvail();
var c = ImGui.GetCursorPos();
AlignMiddle(ImGui.CalcTextSize(text), availSize);
ImGui.TextUnformatted(text);
ImGui.SetCursorPos(c + new Vector2(0, availSize.Y + ImGui.GetStyle().ItemSpacing.Y - 1));
}
public static bool ButtonCentered(string text, Vector2 buttonSize = default)
{
var buttonWidth = buttonSize.X;
if (buttonSize == default)
buttonWidth = ImGui.CalcTextSize(text).X + ImGui.GetStyle().FramePadding.X * 2;
AlignCentered(buttonWidth);
return ImGui.Button(text, buttonSize);
}
}
+1 -1
View File
@@ -45,7 +45,7 @@ public record RecipeData
RLvl = (int)Table.RowId,
ConditionsFlag = Table.ConditionsFlag,
MaxDurability = Table.Durability * Recipe.DurabilityFactor / 100,
MaxQuality = (int)Table.Quality * Recipe.QualityFactor / 100,
MaxQuality = (Recipe.CanHq || Recipe.IsExpert) ? (int)Table.Quality * Recipe.QualityFactor / 100 : 0,
MaxProgress = Table.Difficulty * Recipe.DifficultyFactor / 100,
QualityModifier = Table.QualityModifier,
QualityDivider = Table.QualityDivider,
+153 -16
View File
@@ -10,8 +10,6 @@ using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using Dalamud.Interface.GameFonts;
using Dalamud.Interface.Internal;
using Dalamud.Interface.Style;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;
using Dalamud.Utility;
@@ -21,7 +19,6 @@ using FFXIVClientStructs.FFXIV.Client.UI;
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
using FFXIVClientStructs.FFXIV.Component.GUI;
using ImGuiNET;
using ImGuiScene;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -193,12 +190,37 @@ public sealed unsafe class RecipeNote : Window, IDisposable
ImGui.Separator();
ImGuiUtils.TextCentered("Best Saved Macro");
DrawMacro("savedMacro", BestSavedMacro == null ? null : (BestSavedMacro.Value.Item1.Actions, BestSavedMacro.Value.Item2));
ImGuiUtils.ButtonCentered("View Saved Macros");
ImGuiUtils.TextCentered("Suggested Macro");
DrawMacro("suggestedMacro", BestSuggestedMacro == null ? null : (BestSuggestedMacro.Value.Actions, BestSuggestedMacro.Value.State));
ImGuiUtils.ButtonCentered("Open Simulator");
{
using var table = ImRaii.Table("macros", 1, ImGuiTableFlags.SizingStretchSame);
if (table)
{
ImGui.TableNextColumn();
ImGui.AlignTextToFramePadding();
ImGuiUtils.TextCentered("Best Saved Macro");
if (BestSavedMacro is { } savedMacro)
{
ImGuiUtils.TextCentered(savedMacro.Item1.Name);
DrawMacro("savedMacro", (savedMacro.Item1.Actions, savedMacro.Item2));
}
else
{
ImGui.Text("");
DrawMacro("savedMacro", null);
}
ImGui.Button("View Saved Macros", new(-1, 0));
ImGui.Separator();
ImGui.AlignTextToFramePadding();
ImGuiUtils.TextCentered("Suggested Macro");
if (BestSuggestedMacro is { } suggestedMacro)
DrawMacro("suggestedMacro", (suggestedMacro.Actions, suggestedMacro.State));
else
DrawMacro("suggestedMacro", null);
ImGui.Button("Open Simulator", new(-1, 0));
}
}
}
private void DrawCharacterStats()
@@ -494,16 +516,16 @@ public sealed unsafe class RecipeNote : Window, IDisposable
}
}
private void DrawMacro(string macroName, (List<ActionType> Actions, SimulationState State)? macro)
private void DrawMacro(string imGuiId, (List<ActionType> Actions, SimulationState State)? macroValue)
{
var availWidth = ImGui.GetContentRegionAvail().X;
//using var window = ImRaii.Child(imGuiId, new(-1, (name != null ? ImGui.GetTextLineHeightWithSpacing() : 0) + 2 * ImGui.GetFrameHeightWithSpacing()), false, ImGuiWindowFlags.AlwaysAutoResize);
using var window = ImRaii.Child(macroName, new(availWidth, 2 * ImGui.GetFrameHeight()), false);
var windowHeight = 2 * ImGui.GetFrameHeightWithSpacing();
if (macro == null)
if (macroValue == null)
{
if (BestMacroException == null)
ImGuiUtils.TextMiddle("Calculating...");
ImGuiUtils.TextMiddleNewLine("Calculating...", new(ImGui.GetContentRegionAvail().X, windowHeight + 1 + ImGui.GetStyle().ItemSpacing.Y));
else
{
ImGui.AlignTextToFramePadding();
@@ -515,8 +537,123 @@ public sealed unsafe class RecipeNote : Window, IDisposable
return;
}
ImGuiUtils.TextCentered($"{macro.Value.Actions.Count} Actions");
ImGuiUtils.TextCentered($"{macro.Value.State.Quality} Quality");
var macro = macroValue!.Value;
using var table = ImRaii.Table("table", 3, ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.SizingStretchSame);
if (table)
{
ImGui.TableSetupColumn("desc", ImGuiTableColumnFlags.WidthFixed, 0);
ImGui.TableSetupColumn("actions", ImGuiTableColumnFlags.WidthFixed, 0);
ImGui.TableSetupColumn("steps", ImGuiTableColumnFlags.WidthStretch);
ImGui.TableNextRow(ImGuiTableRowFlags.None, windowHeight);
ImGui.TableNextColumn();
var spacing = ImGui.GetStyle().ItemSpacing.Y;
var miniRowHeight = (windowHeight - spacing) / 2f;
//ImGui.Text($"{macro.Actions.Count}");
{
if (Service.Configuration.ShowOptimalMacroStat)
{
var progressHeight = windowHeight;
if (macro.State.Progress >= macro.State.Input.Recipe.MaxProgress && macro.State.Input.Recipe.MaxQuality > 0)
{
ImGuiUtils.ArcProgress(
(float)macro.State.Quality / macro.State.Input.Recipe.MaxQuality,
progressHeight / 2f,
.5f,
ImGui.GetColorU32(ImGuiCol.TableBorderLight),
ImGui.GetColorU32(Plugin.Windows.Simulator.QualityColor));
if (ImGui.IsItemHovered())
ImGui.SetTooltip($"Quality: {macro.State.Quality} / {macro.State.Input.Recipe.MaxQuality}");
}
else
{
ImGuiUtils.ArcProgress(
(float)macro.State.Progress / macro.State.Input.Recipe.MaxProgress,
progressHeight / 2f,
.5f,
ImGui.GetColorU32(ImGuiCol.TableBorderLight),
ImGui.GetColorU32(Plugin.Windows.Simulator.ProgressColor));
if (ImGui.IsItemHovered())
ImGui.SetTooltip($"Progress: {macro.State.Progress} / {macro.State.Input.Recipe.MaxProgress}");
}
}
else
{
ImGuiUtils.ArcProgress(
(float)macro.State.Progress / macro.State.Input.Recipe.MaxProgress,
miniRowHeight / 2f,
.5f,
ImGui.GetColorU32(ImGuiCol.TableBorderLight),
ImGui.GetColorU32(Plugin.Windows.Simulator.ProgressColor));
if (ImGui.IsItemHovered())
ImGui.SetTooltip($"Progress: {macro.State.Progress} / {macro.State.Input.Recipe.MaxProgress}");
ImGui.SameLine(0, spacing);
ImGuiUtils.ArcProgress(
(float)macro.State.Quality / macro.State.Input.Recipe.MaxQuality,
miniRowHeight / 2f,
.5f,
ImGui.GetColorU32(ImGuiCol.TableBorderLight),
ImGui.GetColorU32(Plugin.Windows.Simulator.QualityColor));
if (ImGui.IsItemHovered())
ImGui.SetTooltip($"Quality: {macro.State.Quality} / {macro.State.Input.Recipe.MaxQuality}");
ImGuiUtils.ArcProgress((float)macro.State.Durability / macro.State.Input.Recipe.MaxDurability,
miniRowHeight / 2f,
.5f,
ImGui.GetColorU32(ImGuiCol.TableBorderLight),
ImGui.GetColorU32(Plugin.Windows.Simulator.DurabilityColor));
if (ImGui.IsItemHovered())
ImGui.SetTooltip($"Remaining Durability: {macro.State.Durability} / {macro.State.Input.Recipe.MaxDurability}");
ImGui.SameLine(0, spacing);
ImGuiUtils.ArcProgress(
(float)macro.State.CP / macro.State.Input.Stats.CP,
miniRowHeight / 2f,
.5f,
ImGui.GetColorU32(ImGuiCol.TableBorderLight),
ImGui.GetColorU32(Plugin.Windows.Simulator.CPColor));
if (ImGui.IsItemHovered())
ImGui.SetTooltip($"Remaining CP: {macro.State.CP} / {macro.State.Input.Stats.CP}");
}
}
ImGui.TableNextColumn();
{
ImGuiUtils.TextMiddleNewLine($"{macro.Actions.Count}", new(miniRowHeight));
if (ImGui.IsItemHovered())
ImGui.SetTooltip($"{macro.Actions.Count} Step{(macro.Actions.Count != 1 ? "s" : "")}");
using (var iconFont = ImRaii.PushFont(UiBuilder.IconFont))
if (ImGuiUtils.ButtonCentered(FontAwesomeIcon.Copy.ToIconString(), new(miniRowHeight)))
{
throw new NotImplementedException();
}
if (ImGui.IsItemHovered())
ImGui.SetTooltip("Copy to Clipboard");
}
ImGui.TableNextColumn();
{
var itemsPerRow = (int)MathF.Ceiling((ImGui.GetContentRegionAvail().X + spacing) / (miniRowHeight + spacing));
var itemCount = Math.Min(macro.Actions.Count, itemsPerRow * 2);
for (var i = 0; i < itemsPerRow * 2; i++)
{
if (i % itemsPerRow != 0)
ImGui.SameLine(0, spacing);
if (i < itemCount)
{
ImGui.Image(macro.Actions[i].GetIcon(RecipeData!.ClassJob).ImGuiHandle, new(miniRowHeight));
if (ImGui.IsItemHovered())
ImGui.SetTooltip(macro.Actions[i].GetName(RecipeData!.ClassJob));
}
else
ImGui.Dummy(new(miniRowHeight));
}
}
}
}
private static void DrawRequiredStatsTable(int current, int required)
+10
View File
@@ -154,6 +154,16 @@ public class Settings : Window
ref isDirty
);
DrawOption(
"Show Only One Macro Stat",
"Only one stat will be shown for a macro. If a craft will be finished, quality\n" +
"is shown. Otherwise, progress is shown. Durability and remaining CP will be\n" +
"hidden.",
Config.ShowOptimalMacroStat,
v => Config.ShowOptimalMacroStat = v,
ref isDirty
);
if (isDirty)
Config.Save();
+5 -5
View File
@@ -24,11 +24,11 @@ public sealed partial class Simulator : Window, IDisposable
private static readonly Vector2 ProgressBarSizeOld = new(200, 20);
public static readonly Vector2 TooltipProgressBarSize = new(100, 5);
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);
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);
+1 -1
View File
@@ -47,7 +47,7 @@ public struct SimulationNode
return null;
static float Apply(float bonus, float value, float target) =>
bonus * Math.Min(1f, value / target);
bonus * (target > 0 ? Math.Min(1f, value / target) : 1);
var progressScore = Apply(
config.ScoreProgress,